/*
 * IDirectMusicPort Implementation
 *
 * Copyright (C) 2003-2004 Rok Mandeljc
 * Copyright (C) 2012 Christian Costa
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "dmusic_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(dmusic);

static inline SynthPortImpl *impl_from_SynthPortImpl_IDirectMusicPort(IDirectMusicPort *iface)
{
    return CONTAINING_RECORD(iface, SynthPortImpl, IDirectMusicPort_iface);
}

static inline SynthPortImpl *impl_from_SynthPortImpl_IDirectMusicPortDownload(IDirectMusicPortDownload *iface)
{
    return CONTAINING_RECORD(iface, SynthPortImpl, IDirectMusicPortDownload_iface);
}

static inline SynthPortImpl *impl_from_SynthPortImpl_IDirectMusicThru(IDirectMusicThru *iface)
{
    return CONTAINING_RECORD(iface, SynthPortImpl, IDirectMusicThru_iface);
}

/* SynthPortImpl IDirectMusicPort IUnknown part follows: */
static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_QueryInterface(LPDIRECTMUSICPORT iface, REFIID riid, LPVOID *ret_iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    TRACE("(%p/%p)->(%s, %p)\n", iface, This, debugstr_dmguid(riid), ret_iface);

    if (IsEqualIID (riid, &IID_IUnknown) ||
        IsEqualGUID(riid, &IID_IDirectMusicPort) ||
        IsEqualGUID(riid, &IID_IDirectMusicPort8)) {
        *ret_iface = &This->IDirectMusicPort_iface;
        IDirectMusicPort_AddRef((LPDIRECTMUSICPORT)*ret_iface);
        return S_OK;
    } else if (IsEqualGUID(riid, &IID_IDirectMusicPortDownload) ||
               IsEqualGUID(riid, &IID_IDirectMusicPortDownload8)) {
        *ret_iface = &This->IDirectMusicPortDownload_iface;
        IDirectMusicPortDownload_AddRef((LPDIRECTMUSICPORTDOWNLOAD)*ret_iface);
        return S_OK;
    } else if (IsEqualGUID(riid, &IID_IDirectMusicThru) ||
               IsEqualGUID(riid, &IID_IDirectMusicThru8)) {
        *ret_iface = &This->IDirectMusicThru_iface;
        IDirectMusicThru_AddRef((LPDIRECTMUSICTHRU)*ret_iface);
        return S_OK;
    }

    WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ret_iface);

    return E_NOINTERFACE;
}

static ULONG WINAPI SynthPortImpl_IDirectMusicPort_AddRef(LPDIRECTMUSICPORT iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);
    ULONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p)->(): new ref = %u\n", This, ref);

    DMUSIC_LockModule();

    return ref;
}

static ULONG WINAPI SynthPortImpl_IDirectMusicPort_Release(LPDIRECTMUSICPORT iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);
    ULONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p)->(): new ref = %u\n", This, ref);

    if (!ref)
    {
        IDirectMusicSynth_Activate(This->synth, FALSE);
        IDirectMusicSynth_Close(This->synth);
        IDirectMusicSynth_Release(This->synth);
        IDirectMusicSynthSink_Release(This->synth_sink);
        IReferenceClock_Release(This->pLatencyClock);
        HeapFree(GetProcessHeap(), 0, This);
    }

    DMUSIC_UnlockModule();

    return ref;
}

/* SynthPortImpl IDirectMusicPort interface follows: */
static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_PlayBuffer(LPDIRECTMUSICPORT iface, LPDIRECTMUSICBUFFER buffer)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);
    HRESULT hr;
    REFERENCE_TIME time;
    LPBYTE data;
    DWORD size;

    TRACE("(%p/%p)->(%p)\n", iface, This, buffer);

    if (!buffer)
        return E_POINTER;

    hr = IDirectMusicBuffer_GetStartTime(buffer, &time);

    if (SUCCEEDED(hr))
        hr = IDirectMusicBuffer_GetRawBufferPtr(buffer, &data);

    if (SUCCEEDED(hr))
        hr = IDirectMusicBuffer_GetUsedBytes(buffer, &size);

    if (SUCCEEDED(hr))
        hr = IDirectMusicSynth_PlayBuffer(This->synth, time, data, size);

    return hr;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_SetReadNotificationHandle(LPDIRECTMUSICPORT iface, HANDLE event)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%p): stub\n", iface, This, event);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_Read(LPDIRECTMUSICPORT iface, LPDIRECTMUSICBUFFER buffer)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%p): stub\n", iface, This, buffer);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_DownloadInstrument(LPDIRECTMUSICPORT iface, IDirectMusicInstrument* instrument, IDirectMusicDownloadedInstrument** downloaded_instrument, DMUS_NOTERANGE* note_ranges, DWORD num_note_ranges)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%p, %p, %p, %d): stub\n", iface, This, instrument, downloaded_instrument, note_ranges, num_note_ranges);

    if (!instrument || !downloaded_instrument || (num_note_ranges && !note_ranges))
        return E_POINTER;

    return DMUSIC_CreateDirectMusicDownloadedInstrumentImpl(&IID_IDirectMusicDownloadedInstrument, (LPVOID*)downloaded_instrument, NULL);
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_UnloadInstrument(LPDIRECTMUSICPORT iface, IDirectMusicDownloadedInstrument *downloaded_instrument)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%p): stub\n", iface, This, downloaded_instrument);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_GetLatencyClock(LPDIRECTMUSICPORT iface, IReferenceClock** clock)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    TRACE("(%p/%p)->(%p)\n", iface, This, clock);

    *clock = This->pLatencyClock;
    IReferenceClock_AddRef(*clock);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_GetRunningStats(LPDIRECTMUSICPORT iface, LPDMUS_SYNTHSTATS stats)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%p): stub\n", iface, This, stats);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_Compact(LPDIRECTMUSICPORT iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(): stub\n", iface, This);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_GetCaps(LPDIRECTMUSICPORT iface, LPDMUS_PORTCAPS port_caps)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    TRACE("(%p/%p)->(%p)\n", iface, This, port_caps);

    *port_caps = This->caps;

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_DeviceIoControl(LPDIRECTMUSICPORT iface, DWORD io_control_code, LPVOID in_buffer, DWORD in_buffer_size,
                                                           LPVOID out_buffer, DWORD out_buffer_size, LPDWORD bytes_returned, LPOVERLAPPED overlapped)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%d, %p, %d, %p, %d, %p, %p): stub\n", iface, This, io_control_code, in_buffer, in_buffer_size, out_buffer, out_buffer_size, bytes_returned, overlapped);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_SetNumChannelGroups(LPDIRECTMUSICPORT iface, DWORD channel_groups)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%d): semi-stub\n", iface, This, channel_groups);

    This->nrofgroups = channel_groups;

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_GetNumChannelGroups(LPDIRECTMUSICPORT iface, LPDWORD channel_groups)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    TRACE("(%p/%p)->(%p)\n", iface, This, channel_groups);

    *channel_groups = This->nrofgroups;

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_Activate(LPDIRECTMUSICPORT iface, BOOL active)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    TRACE("(%p/%p)->(%d)\n", iface, This, active);

    This->fActive = active;

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_SetChannelPriority(LPDIRECTMUSICPORT iface, DWORD channel_group, DWORD channel, DWORD priority)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%d, %d, %d): semi-stub\n", iface, This, channel_group, channel, priority);

    if (channel > 16)
    {
        WARN("isn't there supposed to be 16 channels (no. %d requested)?! (faking as it is ok)\n", channel);
        /*return E_INVALIDARG;*/
    }

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_GetChannelPriority(LPDIRECTMUSICPORT iface, DWORD channel_group, DWORD channel, LPDWORD priority)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    TRACE("(%p/%p)->(%u, %u, %p)\n", iface, This, channel_group, channel, priority);

    *priority = This->group[channel_group - 1].channel[channel].priority;

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_SetDirectSound(LPDIRECTMUSICPORT iface, LPDIRECTSOUND direct_sound, LPDIRECTSOUNDBUFFER direct_sound_buffer)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);

    FIXME("(%p/%p)->(%p, %p): stub\n", iface, This, direct_sound, direct_sound_buffer);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPort_GetFormat(LPDIRECTMUSICPORT iface, LPWAVEFORMATEX pWaveFormatEx, LPDWORD pdwWaveFormatExSize, LPDWORD pdwBufferSize)
{
	SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPort(iface);
	WAVEFORMATEX format;
	FIXME("(%p, %p, %p, %p): stub\n", This, pWaveFormatEx, pdwWaveFormatExSize, pdwBufferSize);

	if (pWaveFormatEx == NULL)
	{
		if (pdwWaveFormatExSize)
			*pdwWaveFormatExSize = sizeof(format);
		else
			return E_POINTER;
	}
	else
	{
		if (pdwWaveFormatExSize == NULL)
			return E_POINTER;

		/* Just fill this in with something that will not crash Direct Sound for now. */
		/* It won't be used anyway until Performances are completed */
		format.wFormatTag = WAVE_FORMAT_PCM;
		format.nChannels = 2; /* This->params.dwAudioChannels; */
		format.nSamplesPerSec = 44100; /* This->params.dwSampleRate; */
		format.wBitsPerSample = 16;	/* FIXME: check this */
		format.nBlockAlign = (format.wBitsPerSample * format.nChannels) / 8;
		format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
		format.cbSize = 0;

		if (*pdwWaveFormatExSize >= sizeof(format))
		{
			CopyMemory(pWaveFormatEx, &format, min(sizeof(format), *pdwWaveFormatExSize));
			*pdwWaveFormatExSize = sizeof(format);	/* FIXME check if this is set */
		}
		else
			return E_POINTER;	/* FIXME find right error */
	}

	if (pdwBufferSize)
		*pdwBufferSize = 44100 * 2 * 2;
	else
		return E_POINTER;

	return S_OK;
}

static const IDirectMusicPortVtbl SynthPortImpl_DirectMusicPort_Vtbl = {
    /**** IDirectMusicPort IUnknown part methods ***/
    SynthPortImpl_IDirectMusicPort_QueryInterface,
    SynthPortImpl_IDirectMusicPort_AddRef,
    SynthPortImpl_IDirectMusicPort_Release,
    /**** IDirectMusicPort methods ***/
    SynthPortImpl_IDirectMusicPort_PlayBuffer,
    SynthPortImpl_IDirectMusicPort_SetReadNotificationHandle,
    SynthPortImpl_IDirectMusicPort_Read,
    SynthPortImpl_IDirectMusicPort_DownloadInstrument,
    SynthPortImpl_IDirectMusicPort_UnloadInstrument,
    SynthPortImpl_IDirectMusicPort_GetLatencyClock,
    SynthPortImpl_IDirectMusicPort_GetRunningStats,
    SynthPortImpl_IDirectMusicPort_Compact,
    SynthPortImpl_IDirectMusicPort_GetCaps,
    SynthPortImpl_IDirectMusicPort_DeviceIoControl,
    SynthPortImpl_IDirectMusicPort_SetNumChannelGroups,
    SynthPortImpl_IDirectMusicPort_GetNumChannelGroups,
    SynthPortImpl_IDirectMusicPort_Activate,
    SynthPortImpl_IDirectMusicPort_SetChannelPriority,
    SynthPortImpl_IDirectMusicPort_GetChannelPriority,
    SynthPortImpl_IDirectMusicPort_SetDirectSound,
    SynthPortImpl_IDirectMusicPort_GetFormat
};

/* SynthPortImpl IDirectMusicPortDownload IUnknown part follows: */
static HRESULT WINAPI SynthPortImpl_IDirectMusicPortDownload_QueryInterface(LPDIRECTMUSICPORTDOWNLOAD iface, REFIID riid, LPVOID *ret_iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    TRACE("(%p/%p)->(%s, %p)\n", iface, This, debugstr_dmguid(riid), ret_iface);

    return IDirectMusicPort_QueryInterface(&This->IDirectMusicPort_iface, riid, ret_iface);
}

static ULONG WINAPI SynthPortImpl_IDirectMusicPortDownload_AddRef (LPDIRECTMUSICPORTDOWNLOAD iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    TRACE("(%p/%p)->()\n", iface, This);

    return IDirectMusicPort_AddRef(&This->IDirectMusicPort_iface);
}

static ULONG WINAPI SynthPortImpl_IDirectMusicPortDownload_Release(LPDIRECTMUSICPORTDOWNLOAD iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    TRACE("(%p/%p)->()\n", iface, This);

    return IDirectMusicPort_Release(&This->IDirectMusicPort_iface);
}

/* SynthPortImpl IDirectMusicPortDownload Interface follows: */
static HRESULT WINAPI SynthPortImpl_IDirectMusicPortDownload_GetBuffer(LPDIRECTMUSICPORTDOWNLOAD iface, DWORD DLId, IDirectMusicDownload** IDMDownload)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    FIXME("(%p/%p)->(%u, %p): stub\n", iface, This, DLId, IDMDownload);

    if (!IDMDownload)
        return E_POINTER;

    return DMUSIC_CreateDirectMusicDownloadImpl(&IID_IDirectMusicDownload, (LPVOID*)IDMDownload, NULL);
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPortDownload_AllocateBuffer(LPDIRECTMUSICPORTDOWNLOAD iface, DWORD size, IDirectMusicDownload** IDMDownload)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    FIXME("(%p/%p)->(%u, %p): stub\n", iface, This, size, IDMDownload);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPortDownload_GetDLId(LPDIRECTMUSICPORTDOWNLOAD iface, DWORD* start_DLId, DWORD count)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    FIXME("(%p/%p)->(%p, %u): stub\n", iface, This, start_DLId, count);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPortDownload_GetAppend (LPDIRECTMUSICPORTDOWNLOAD iface, DWORD* append)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    FIXME("(%p/%p)->(%p): stub\n", iface, This, append);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPortDownload_Download(LPDIRECTMUSICPORTDOWNLOAD iface, IDirectMusicDownload* IDMDownload)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    FIXME("(%p/%p)->(%p): stub\n", iface, This, IDMDownload);

    return S_OK;
}

static HRESULT WINAPI SynthPortImpl_IDirectMusicPortDownload_Unload(LPDIRECTMUSICPORTDOWNLOAD iface, IDirectMusicDownload* IDMDownload)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicPortDownload(iface);

    FIXME("(%p/%p)->(%p): stub\n", iface, This, IDMDownload);

    return S_OK;
}

static const IDirectMusicPortDownloadVtbl SynthPortImpl_DirectMusicPortDownload_Vtbl = {
    /*** IDirectMusicPortDownload IUnknown part methods ***/
    SynthPortImpl_IDirectMusicPortDownload_QueryInterface,
    SynthPortImpl_IDirectMusicPortDownload_AddRef,
    SynthPortImpl_IDirectMusicPortDownload_Release,
    /*** IDirectMusicPortDownload methods ***/
    SynthPortImpl_IDirectMusicPortDownload_GetBuffer,
    SynthPortImpl_IDirectMusicPortDownload_AllocateBuffer,
    SynthPortImpl_IDirectMusicPortDownload_GetDLId,
    SynthPortImpl_IDirectMusicPortDownload_GetAppend,
    SynthPortImpl_IDirectMusicPortDownload_Download,
    SynthPortImpl_IDirectMusicPortDownload_Unload
};

/* SynthPortImpl IDirectMusicThru IUnknown part follows: */
static HRESULT WINAPI SynthPortImpl_IDirectMusicThru_QueryInterface(LPDIRECTMUSICTHRU iface, REFIID riid, LPVOID *ret_iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicThru(iface);

    TRACE("(%p/%p)->(%s, %p)\n", iface, This, debugstr_dmguid(riid), ret_iface);

    return IDirectMusicPort_QueryInterface(&This->IDirectMusicPort_iface, riid, ret_iface);
}

static ULONG WINAPI SynthPortImpl_IDirectMusicThru_AddRef(LPDIRECTMUSICTHRU iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicThru(iface);

    TRACE("(%p/%p)->()\n", iface, This);

    return IDirectMusicPort_AddRef(&This->IDirectMusicPort_iface);
}

static ULONG WINAPI SynthPortImpl_IDirectMusicThru_Release(LPDIRECTMUSICTHRU iface)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicThru(iface);

    TRACE("(%p/%p)->()\n", iface, This);

    return IDirectMusicPort_Release(&This->IDirectMusicPort_iface);
}

/*  SynthPortImpl IDirectMusicThru Interface follows: */
static HRESULT WINAPI SynthPortImpl_IDirectMusicThru_ThruChannel(LPDIRECTMUSICTHRU iface, DWORD source_channel_group, DWORD source_channel, DWORD destination_channel_group,
                                                       DWORD destination_channel, LPDIRECTMUSICPORT destination_port)
{
    SynthPortImpl *This = impl_from_SynthPortImpl_IDirectMusicThru(iface);

    FIXME("(%p/%p)->(%d, %d, %d, %d, %p): stub\n", iface, This, source_channel_group, source_channel, destination_channel_group, destination_channel, destination_port);

    return S_OK;
}

static const IDirectMusicThruVtbl SynthPortImpl_DirectMusicThru_Vtbl = {
    /*** IDirectMusicThru IUnknown part methods */
    SynthPortImpl_IDirectMusicThru_QueryInterface,
    SynthPortImpl_IDirectMusicThru_AddRef,
    SynthPortImpl_IDirectMusicThru_Release,
    /*** IDirectMusicThru methods ***/
    SynthPortImpl_IDirectMusicThru_ThruChannel
};

HRESULT DMUSIC_CreateSynthPortImpl(LPCGUID guid, LPVOID *object, LPUNKNOWN unkouter, LPDMUS_PORTPARAMS port_params, LPDMUS_PORTCAPS port_caps, DWORD device)
{
    SynthPortImpl *obj;
    HRESULT hr = E_FAIL;
    UINT i;

    TRACE("(%p,%p,%p,%p,%p%d)\n", guid, object, unkouter, port_params, port_caps, device);

    *object = NULL;

    obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SynthPortImpl));
    if (!obj)
        return E_OUTOFMEMORY;

    obj->IDirectMusicPort_iface.lpVtbl = &SynthPortImpl_DirectMusicPort_Vtbl;
    obj->IDirectMusicPortDownload_iface.lpVtbl = &SynthPortImpl_DirectMusicPortDownload_Vtbl;
    obj->IDirectMusicThru_iface.lpVtbl = &SynthPortImpl_DirectMusicThru_Vtbl;
    obj->ref = 0;  /* Will be inited by QueryInterface */
    obj->fActive = FALSE;
    obj->params = *port_params;
    obj->caps = *port_caps;

    hr = DMUSIC_CreateReferenceClockImpl(&IID_IReferenceClock, (LPVOID*)&obj->pLatencyClock, NULL);
    if (hr != S_OK)
    {
        HeapFree(GetProcessHeap(), 0, obj);
        return hr;
    }

    if (SUCCEEDED(hr))
        hr = CoCreateInstance(&CLSID_DirectMusicSynth, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicSynth, (void**)&obj->synth);

    if (SUCCEEDED(hr))
        hr = CoCreateInstance(&CLSID_DirectMusicSynthSink, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicSynthSink, (void**)&obj->synth_sink);

    if (SUCCEEDED(hr))
        hr = IDirectMusicSynth_SetMasterClock(obj->synth, obj->pLatencyClock);

    if (SUCCEEDED(hr))
        hr = IDirectMusicSynthSink_SetMasterClock(obj->synth_sink, obj->pLatencyClock);

    if (SUCCEEDED(hr))
        hr = IDirectMusicSynth_SetSynthSink(obj->synth, obj->synth_sink);

    if (SUCCEEDED(hr))
        hr = IDirectMusicSynth_Open(obj->synth, port_params);

    if (0)
    {
        if (port_params->dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS) {
            obj->nrofgroups = port_params->dwChannelGroups;
            /* Setting default priorities */
            for (i = 0; i < obj->nrofgroups; i++) {
                TRACE ("Setting default channel priorities on channel group %i\n", i + 1);
                obj->group[i].channel[0].priority = DAUD_CHAN1_DEF_VOICE_PRIORITY;
                obj->group[i].channel[1].priority = DAUD_CHAN2_DEF_VOICE_PRIORITY;
                obj->group[i].channel[2].priority = DAUD_CHAN3_DEF_VOICE_PRIORITY;
                obj->group[i].channel[3].priority = DAUD_CHAN4_DEF_VOICE_PRIORITY;
                obj->group[i].channel[4].priority = DAUD_CHAN5_DEF_VOICE_PRIORITY;
                obj->group[i].channel[5].priority = DAUD_CHAN6_DEF_VOICE_PRIORITY;
                obj->group[i].channel[6].priority = DAUD_CHAN7_DEF_VOICE_PRIORITY;
                obj->group[i].channel[7].priority = DAUD_CHAN8_DEF_VOICE_PRIORITY;
                obj->group[i].channel[8].priority = DAUD_CHAN9_DEF_VOICE_PRIORITY;
                obj->group[i].channel[9].priority = DAUD_CHAN10_DEF_VOICE_PRIORITY;
                obj->group[i].channel[10].priority = DAUD_CHAN11_DEF_VOICE_PRIORITY;
                obj->group[i].channel[11].priority = DAUD_CHAN12_DEF_VOICE_PRIORITY;
                obj->group[i].channel[12].priority = DAUD_CHAN13_DEF_VOICE_PRIORITY;
                obj->group[i].channel[13].priority = DAUD_CHAN14_DEF_VOICE_PRIORITY;
                obj->group[i].channel[14].priority = DAUD_CHAN15_DEF_VOICE_PRIORITY;
                obj->group[i].channel[15].priority = DAUD_CHAN16_DEF_VOICE_PRIORITY;
            }
        }
    }

    if (SUCCEEDED(hr))
        return IDirectMusicPort_QueryInterface((LPDIRECTMUSICPORT)obj, guid, object);

    if (obj->synth)
        IDirectMusicSynth_Release(obj->synth);
    if (obj->synth_sink)
        IDirectMusicSynthSink_Release(obj->synth_sink);
    if (obj->pLatencyClock)
        IReferenceClock_Release(obj->pLatencyClock);
    HeapFree(GetProcessHeap(), 0, obj);

    return hr;
}

HRESULT DMUSIC_CreateMidiOutPortImpl(LPCGUID guid, LPVOID *object, LPUNKNOWN unkouter, LPDMUS_PORTPARAMS port_params, LPDMUS_PORTCAPS port_caps, DWORD device)
{
    TRACE("(%p,%p,%p,%p,%p,%d): stub\n", guid, object, unkouter, port_params, port_caps, device);

    return E_NOTIMPL;
}

HRESULT DMUSIC_CreateMidiInPortImpl(LPCGUID guid, LPVOID *object, LPUNKNOWN unkouter, LPDMUS_PORTPARAMS port_params, LPDMUS_PORTCAPS port_caps, DWORD device)
{
    TRACE("(%p,%p,%p,%p,%p,%d): stub\n", guid, object, unkouter, port_params, port_caps, device);

    return E_NOTIMPL;
}