/* IDirectMusicPerformance Implementation * * Copyright (C) 2003-2004 Rok Mandeljc * Copyright (C) 2003-2004 Raphael Junqueira * * 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 "dmime_private.h" #include "dmusic_midi.h" #include "wine/rbtree.h" #include WINE_DEFAULT_DEBUG_CHANNEL(dmime); enum dmus_internal_message_type { DMUS_PMSGT_INTERNAL_FIRST = 0x10, DMUS_PMSGT_INTERNAL_SEGMENT_END = DMUS_PMSGT_INTERNAL_FIRST, DMUS_PMSGT_INTERNAL_SEGMENT_TICK, }; struct channel { DWORD midi_group; DWORD midi_channel; IDirectMusicPort *port; }; struct channel_block { DWORD block_num; /* Block 0 is PChannels 0-15, Block 1 is PChannels 16-31, etc */ struct channel channels[16]; struct wine_rb_entry entry; }; struct performance { IDirectMusicPerformance8 IDirectMusicPerformance8_iface; IDirectMusicGraph IDirectMusicGraph_iface; IDirectMusicTool IDirectMusicTool_iface; LONG ref; IDirectMusic *dmusic; IDirectSound *dsound; IDirectMusicGraph *pToolGraph; BOOL fAutoDownload; char cMasterGrooveLevel; float fMasterTempo; long lMasterVolume; /* performance channels */ struct wine_rb_tree channel_blocks; BOOL audio_paths_enabled; IDirectMusicAudioPath *pDefaultPath; REFERENCE_TIME latency_offset; DWORD dwBumperLength; DWORD dwPrepareTime; HANDLE message_thread; CRITICAL_SECTION safe; CONDITION_VARIABLE cond; IReferenceClock *master_clock; REFERENCE_TIME init_time; struct list messages; struct list notifications; REFERENCE_TIME notification_timeout; HANDLE notification_event; BOOL notification_performance; BOOL notification_segment; IDirectMusicSegment *primary_segment; IDirectMusicSegment *control_segment; }; struct message { struct list entry; DMUS_PMSG msg; }; static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg) { return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL; } static struct message *performance_get_message(struct performance *This, DWORD *timeout) { static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; IDirectMusicPerformance *performance = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface; REFERENCE_TIME latency, offset = 0; struct message *message; struct list *ptr; if (!(ptr = list_head(&This->messages))) return NULL; message = LIST_ENTRY(ptr, struct message, entry); if (FAILED(IDirectMusicPerformance_GetLatencyTime(performance, &latency))) return NULL; switch (message->msg.dwFlags & delivery_flags) { default: WARN("No delivery flag found for message %p\n", &message->msg); break; case DMUS_PMSGF_TOOL_QUEUE: offset = This->dwBumperLength * 10000; /* fallthrough */ case DMUS_PMSGF_TOOL_ATTIME: if (message->msg.rtTime >= offset && message->msg.rtTime - offset >= latency) { *timeout = (message->msg.rtTime - offset - latency) / 10000; return NULL; } break; } return message; } static HRESULT performance_process_message(struct performance *This, DMUS_PMSG *msg) { IDirectMusicPerformance *performance = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface; IDirectMusicTool *tool; HRESULT hr; if (!(tool = msg->pTool)) tool = &This->IDirectMusicTool_iface; hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg); if (hr == DMUS_S_FREE) hr = IDirectMusicPerformance_FreePMsg(performance, msg); if (FAILED(hr)) WARN("Failed to process message, hr %#lx\n", hr); return hr; } static HRESULT performance_queue_message(struct performance *This, struct message *message) { static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; struct message *prev; HRESULT hr; while ((message->msg.dwFlags & delivery_flags) == DMUS_PMSGF_TOOL_IMMEDIATE) { hr = performance_process_message(This, &message->msg); if (hr != DMUS_S_REQUEUE) return hr; } LIST_FOR_EACH_ENTRY_REV(prev, &This->messages, struct message, entry) { if (&prev->entry == &This->messages) break; if (prev->msg.rtTime <= message->msg.rtTime) break; } list_add_after(&prev->entry, &message->entry); return S_OK; } static DWORD WINAPI message_thread_proc(void *args) { struct performance *This = args; HRESULT hr; TRACE("performance %p message thread\n", This); SetThreadDescription(GetCurrentThread(), L"wine_dmime_message"); EnterCriticalSection(&This->safe); while (This->message_thread) { DWORD timeout = INFINITE; struct message *message; if (!(message = performance_get_message(This, &timeout))) { SleepConditionVariableCS(&This->cond, &This->safe, timeout); continue; } list_remove(&message->entry); list_init(&message->entry); hr = performance_process_message(This, &message->msg); if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message); } LeaveCriticalSection(&This->safe); TRACE("(%p): Exiting\n", This); return 0; } static HRESULT performance_send_pmsg(struct performance *This, MUSIC_TIME music_time, DWORD flags, DWORD type, IUnknown *object) { IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface; DMUS_PMSG *msg; HRESULT hr; if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), &msg))) return hr; msg->mtTime = music_time; msg->dwFlags = DMUS_PMSGF_MUSICTIME | flags; msg->dwType = type; if ((msg->punkUser = object)) IUnknown_AddRef(object); if ((type < DMUS_PMSGT_INTERNAL_FIRST && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, msg))) || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg))) IDirectMusicPerformance8_FreePMsg(performance, msg); return hr; } static HRESULT performance_send_notification_pmsg(struct performance *This, MUSIC_TIME music_time, BOOL stamp, GUID type, DWORD option, IUnknown *object) { IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface; DMUS_NOTIFICATION_PMSG *msg; HRESULT hr; if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg))) return hr; msg->mtTime = music_time; msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE; msg->dwType = DMUS_PMSGT_NOTIFICATION; if ((msg->punkUser = object)) IUnknown_AddRef(object); msg->guidNotificationType = type; msg->dwNotificationOption = option; /* only stamp the message if notifications are enabled, otherwise send them directly to the output tool */ if ((stamp && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg))) || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)msg))) IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)msg); return hr; } static int channel_block_compare(const void *key, const struct wine_rb_entry *entry) { const struct channel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct channel_block, entry); return *(DWORD *)key - b->block_num; } static void channel_block_free(struct wine_rb_entry *entry, void *context) { struct channel_block *block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); UINT i; for (i = 0; i < ARRAY_SIZE(block->channels); i++) { struct channel *channel = block->channels + i; if (channel->port) IDirectMusicPort_Release(channel->port); } free(block); } static struct channel *performance_get_channel(struct performance *This, DWORD channel_num) { DWORD block_num = channel_num / 16; struct wine_rb_entry *entry; if (!(entry = wine_rb_get(&This->channel_blocks, &block_num))) return NULL; return WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry)->channels + channel_num % 16; } static HRESULT channel_block_init(struct performance *This, DWORD block_num, IDirectMusicPort *port, DWORD midi_group) { struct channel_block *block; struct wine_rb_entry *entry; UINT i; if ((entry = wine_rb_get(&This->channel_blocks, &block_num))) block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); else { if (!(block = calloc(1, sizeof(*block)))) return E_OUTOFMEMORY; block->block_num = block_num; wine_rb_put(&This->channel_blocks, &block_num, &block->entry); } for (i = 0; i < ARRAY_SIZE(block->channels); ++i) { struct channel *channel = block->channels + i; channel->midi_group = midi_group; channel->midi_channel = i; if (channel->port) IDirectMusicPort_Release(channel->port); if ((channel->port = port)) IDirectMusicPort_AddRef(channel->port); } return S_OK; } static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface) { return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface); } HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr; if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance, GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL))) return hr; if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state))) return hr; if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL))) return hr; return S_OK; } HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); REFERENCE_TIME time; HRESULT hr; if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface, music_time, &time))) return hr; if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, time + 2000000, &music_time))) return hr; if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_QUEUE, DMUS_PMSGT_INTERNAL_SEGMENT_TICK, (IUnknown *)state))) return hr; return S_OK; } HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state, BOOL abort) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr; if (!abort) { if (FAILED(hr = performance_send_notification_pmsg(This, music_time - 1450, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state))) return hr; if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state))) return hr; } if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL))) return hr; if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance, GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL))) return hr; if (FAILED(hr = performance_send_pmsg(This, music_time, abort ? DMUS_PMSGF_TOOL_IMMEDIATE : DMUS_PMSGF_TOOL_ATTIME, DMUS_PMSGT_INTERNAL_SEGMENT_END, (IUnknown *)state))) return hr; return S_OK; } static void performance_set_primary_segment(struct performance *This, IDirectMusicSegment *segment) { if (This->primary_segment) IDirectMusicSegment_Release(This->primary_segment); if ((This->primary_segment = segment)) IDirectMusicSegment_AddRef(This->primary_segment); } static void performance_set_control_segment(struct performance *This, IDirectMusicSegment *segment) { if (This->control_segment) IDirectMusicSegment_Release(This->control_segment); if ((This->control_segment = segment)) IDirectMusicSegment_AddRef(This->control_segment); } /* IDirectMusicPerformance8 IUnknown part: */ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDirectMusicPerformance) || IsEqualGUID(riid, &IID_IDirectMusicPerformance2) || IsEqualGUID(riid, &IID_IDirectMusicPerformance8)) { *ret_iface = iface; IUnknown_AddRef(iface); return S_OK; } if (IsEqualGUID(riid, &IID_IDirectMusicGraph)) { *ret_iface = &This->IDirectMusicGraph_iface; IDirectMusicGraph_AddRef(&This->IDirectMusicGraph_iface); return S_OK; } if (IsEqualGUID(riid, &IID_IDirectMusicTool)) { *ret_iface = &This->IDirectMusicTool_iface; IDirectMusicTool_AddRef(&This->IDirectMusicTool_iface); return S_OK; } *ret_iface = NULL; WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); return E_NOINTERFACE; } static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): ref=%ld\n", This, ref); return ref; } static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): ref=%ld\n", This, ref); if (ref == 0) { wine_rb_destroy(&This->channel_blocks, channel_block_free, NULL); This->safe.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->safe); free(This); } return ref; } static HRESULT performance_init_dsound(struct performance *This, HWND hwnd) { IDirectSound *dsound; HRESULT hr; if (FAILED(hr = DirectSoundCreate(NULL, &dsound, NULL))) return hr; if (!hwnd) hwnd = GetForegroundWindow(); hr = IDirectSound_SetCooperativeLevel(dsound, hwnd, DSSCL_PRIORITY); if (SUCCEEDED(hr)) This->dsound = dsound; else IDirectSound_Release(dsound); return hr; } static HRESULT performance_init_dmusic(struct performance *This, IDirectSound *dsound) { IDirectMusic *dmusic; HRESULT hr; if (FAILED(hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusic8, (void **)&dmusic))) return hr; hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL); if (SUCCEEDED(hr)) This->dmusic = dmusic; else IDirectSound_Release(dmusic); return hr; } static HRESULT performance_send_midi_pmsg(struct performance *This, DMUS_PMSG *msg, UINT flags, BYTE status, BYTE byte1, BYTE byte2) { IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; DMUS_MIDI_PMSG *midi; HRESULT hr; if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*midi), (DMUS_PMSG **)&midi))) return hr; if (flags & DMUS_PMSGF_REFTIME) midi->rtTime = msg->rtTime; if (flags & DMUS_PMSGF_MUSICTIME) midi->mtTime = msg->mtTime; midi->dwFlags = flags; midi->dwPChannel = msg->dwPChannel; midi->dwVirtualTrackID = msg->dwVirtualTrackID; midi->dwVoiceID = msg->dwVoiceID; midi->dwGroupID = msg->dwGroupID; midi->dwType = DMUS_PMSGT_MIDI; midi->bStatus = status; midi->bByte1 = byte1; midi->bByte2 = byte2; if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)midi))) IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)midi); return hr; } static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic, IDirectSound *dsound, HWND hwnd) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr; TRACE("(%p, %p, %p, %p)\n", iface, dmusic, dsound, hwnd); if (This->dmusic) return DMUS_E_ALREADY_INITED; if ((This->dsound = dsound)) IDirectMusic8_AddRef(This->dsound); else if (FAILED(hr = performance_init_dsound(This, hwnd))) return hr; if (dmusic && (This->dmusic = *dmusic)) IDirectMusic_AddRef(This->dmusic); else if (FAILED(hr = performance_init_dmusic(This, This->dsound))) { IDirectMusicPerformance_CloseDown(iface); return hr; } if (FAILED(hr = IDirectMusic_GetMasterClock(This->dmusic, NULL, &This->master_clock)) || FAILED(hr = IDirectMusicPerformance8_GetTime(iface, &This->init_time, NULL))) { IDirectMusicPerformance_CloseDown(iface); return hr; } if (!(This->message_thread = CreateThread(NULL, 0, message_thread_proc, This, 0, NULL))) { ERR("Failed to start performance message thread, error %lu\n", GetLastError()); IDirectMusicPerformance_CloseDown(iface); return HRESULT_FROM_WIN32(GetLastError()); } if (dmusic && !*dmusic) { *dmusic = This->dmusic; IDirectMusic_AddRef(*dmusic); } return S_OK; } static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment, DWORD segment_flags, INT64 start_time, IDirectMusicSegmentState **ret_state) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p, %ld, %I64d, %p)\n", This, segment, segment_flags, start_time, ret_state); return IDirectMusicPerformance8_PlaySegmentEx(iface, (IUnknown *)segment, NULL, NULL, segment_flags, start_time, ret_state, NULL, NULL); } struct state_entry { struct list entry; IDirectMusicSegmentState *state; }; static void state_entry_destroy(struct state_entry *entry) { list_remove(&entry->entry); IDirectMusicSegmentState_Release(entry->state); free(entry); } static void enum_segment_states(struct performance *This, IDirectMusicSegment *segment, struct list *list) { struct state_entry *entry; struct message *message; LIST_FOR_EACH_ENTRY(message, &This->messages, struct message, entry) { IDirectMusicSegmentState *message_state; if (message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_TICK && message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_END) continue; message_state = (IDirectMusicSegmentState *)message->msg.punkUser; if (segment && !segment_state_has_segment(message_state, segment)) continue; if (!(entry = malloc(sizeof(*entry)))) return; entry->state = message_state; IDirectMusicSegmentState_AddRef(entry->state); list_add_tail(list, &entry->entry); } } static BOOL message_needs_flushing(struct message *message, IDirectMusicSegmentState *state) { if (!state) return TRUE; switch (message->msg.dwType) { case DMUS_PMSGT_MIDI: case DMUS_PMSGT_NOTE: case DMUS_PMSGT_CURVE: case DMUS_PMSGT_PATCH: case DMUS_PMSGT_WAVE: if (!segment_state_has_track(state, message->msg.dwVirtualTrackID)) return FALSE; break; case DMUS_PMSGT_NOTIFICATION: { DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)&message->msg; if (!IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) return FALSE; if ((IDirectMusicSegmentState *)message->msg.punkUser != state) return FALSE; break; } case DMUS_PMSGT_INTERNAL_SEGMENT_TICK: case DMUS_PMSGT_INTERNAL_SEGMENT_END: if ((IDirectMusicSegmentState *)message->msg.punkUser != state) return FALSE; break; default: FIXME("Unhandled message type %#lx\n", message->msg.dwType); break; } return TRUE; } static void performance_flush_messages(struct performance *This, IDirectMusicSegmentState *state) { IDirectMusicPerformance *iface = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface; struct message *message, *next; HRESULT hr; LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) { if (!message_needs_flushing(message, state)) continue; list_remove(&message->entry); list_init(&message->entry); if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) ERR("Failed to free message %p, hr %#lx\n", message, hr); } LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->notifications, struct message, entry) { if (!message_needs_flushing(message, state)) continue; list_remove(&message->entry); list_init(&message->entry); if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) ERR("Failed to free message %p, hr %#lx\n", message, hr); } } static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment, IDirectMusicSegmentState *state, MUSIC_TIME music_time, DWORD flags) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct list states = LIST_INIT(states); struct state_entry *entry, *next; DMUS_PMSG msg = {.mtTime = -1}; HRESULT hr; FIXME("(%p, %p, %p, %ld, %ld): semi-stub\n", This, segment, state, music_time, flags); if (music_time) FIXME("time parameter %lu not implemented\n", music_time); if (flags != DMUS_SEGF_DEFAULT) FIXME("flags parameter %#lx not implemented\n", flags); if (!music_time && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time))) return hr; EnterCriticalSection(&This->safe); if (!state) enum_segment_states(This, segment, &states); else if ((entry = malloc(sizeof(*entry)))) { entry->state = state; IDirectMusicSegmentState_AddRef(entry->state); list_add_tail(&states, &entry->entry); } if (!segment && !state) performance_flush_messages(This, NULL); else LIST_FOR_EACH_ENTRY(entry, &states, struct state_entry, entry) performance_flush_messages(This, entry->state); LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry) { if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGABORT, (IUnknown *)entry->state))) ERR("Failed to send DMUS_NOTIFICATION_SEGABORT, hr %#lx\n", hr); if (FAILED(hr = segment_state_stop(entry->state, iface))) ERR("Failed to stop segment state, hr %#lx\n", hr); IDirectMusicSegmentState_Release(entry->state); list_remove(&entry->entry); free(entry); } if (!state && !segment) { if (FAILED(hr = performance_send_midi_pmsg(This, &msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_SYSTEM_RESET, 0, 0))) ERR("Failed to send MIDI_SYSTEM_RESET message, hr %#lx\n", hr); } LeaveCriticalSection(&This->safe); return S_OK; } static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface, IDirectMusicSegmentState **state, MUSIC_TIME time) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct list *ptr, states = LIST_INIT(states); struct state_entry *entry, *next; HRESULT hr = S_OK; TRACE("(%p, %p, %ld)\n", This, state, time); if (!state) return E_POINTER; EnterCriticalSection(&This->safe); enum_segment_states(This, This->primary_segment, &states); if (!(ptr = list_head(&states))) hr = DMUS_E_NOT_FOUND; else { entry = LIST_ENTRY(ptr, struct state_entry, entry); *state = entry->state; IDirectMusicSegmentState_AddRef(entry->state); LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry) state_entry_destroy(entry); } LeaveCriticalSection(&This->safe); return hr; } static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %ld)\n", This, dwMilliSeconds); This->dwPrepareTime = dwMilliSeconds; return S_OK; } static HRESULT WINAPI performance_GetPrepareTime(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p)\n", This, pdwMilliSeconds); if (NULL == pdwMilliSeconds) { return E_POINTER; } *pdwMilliSeconds = This->dwPrepareTime; return S_OK; } static HRESULT WINAPI performance_SetBumperLength(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %ld)\n", This, dwMilliSeconds); This->dwBumperLength = dwMilliSeconds; return S_OK; } static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p)\n", This, pdwMilliSeconds); if (NULL == pdwMilliSeconds) { return E_POINTER; } *pdwMilliSeconds = This->dwBumperLength; return S_OK; } static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct message *message; HRESULT hr; TRACE("(%p, %p)\n", This, msg); if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER; if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK; if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG; EnterCriticalSection(&This->safe); if (!list_empty(&message->entry)) hr = DMUS_E_ALREADY_SENT; else { if (!(msg->dwFlags & delivery_flags)) msg->dwFlags |= DMUS_PMSGF_TOOL_IMMEDIATE; if (!(msg->dwFlags & DMUS_PMSGF_MUSICTIME)) { if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, msg->rtTime, &msg->mtTime))) goto done; msg->dwFlags |= DMUS_PMSGF_MUSICTIME; } if (!(msg->dwFlags & DMUS_PMSGF_REFTIME)) { if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface, msg->mtTime == -1 ? 0 : msg->mtTime, &msg->rtTime))) goto done; msg->dwFlags |= DMUS_PMSGF_REFTIME; } hr = performance_queue_message(This, message); } done: LeaveCriticalSection(&This->safe); if (SUCCEEDED(hr)) WakeConditionVariable(&This->cond); return hr; } static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, REFERENCE_TIME *time) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); MUSIC_TIME tempo_time, next = 0; DMUS_TEMPO_PARAM param; double tempo, duration; HRESULT hr; TRACE("(%p, %ld, %p)\n", This, music_time, time); if (!time) return E_POINTER; *time = 0; if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; EnterCriticalSection(&This->safe); for (tempo = 120.0, duration = tempo_time = 0; music_time > 0; tempo_time += next) { if (FAILED(hr = IDirectMusicPerformance_GetParam(iface, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, tempo_time, &next, ¶m))) break; if (!next) next = music_time; else next = min(next, music_time); if (param.mtTime <= 0) tempo = param.dblTempo; duration += (600000000.0 * next) / (tempo * DMUS_PPQ); music_time -= next; } duration += (600000000.0 * music_time) / (tempo * DMUS_PPQ); *time = This->init_time + duration; LeaveCriticalSection(&This->safe); return S_OK; } static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME time, MUSIC_TIME *music_time) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); MUSIC_TIME tempo_time, next = 0; double tempo, duration, step; DMUS_TEMPO_PARAM param; HRESULT hr; TRACE("(%p, %I64d, %p)\n", This, time, music_time); if (!music_time) return E_POINTER; *music_time = 0; if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; EnterCriticalSection(&This->safe); duration = time - This->init_time; for (tempo = 120.0, tempo_time = 0; duration > 0; tempo_time += next, duration -= step) { if (FAILED(hr = IDirectMusicPerformance_GetParam(iface, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, tempo_time, &next, ¶m))) break; if (param.mtTime <= 0) tempo = param.dblTempo; step = (600000000.0 * next) / (tempo * DMUS_PPQ); if (!next || duration < step) break; *music_time = *music_time + next; } *music_time = *music_time + round((duration * tempo * DMUS_PPQ) / 600000000.0); LeaveCriticalSection(&This->safe); return S_OK; } static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegState) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, %p): stub\n", This, pSegment, pSegState); return S_FALSE; } static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *time, MUSIC_TIME *music_time) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); REFERENCE_TIME now; HRESULT hr; TRACE("(%p, %p, %p)\n", iface, time, music_time); if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &now))) return hr; if (time) *time = now; if (music_time) hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, now, music_time); return hr; } static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG size, DMUS_PMSG **msg) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct message *message; TRACE("(%p, %ld, %p)\n", This, size, msg); if (!msg) return E_POINTER; if (size < sizeof(DMUS_PMSG)) return E_INVALIDARG; if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(struct message)))) return E_OUTOFMEMORY; message->msg.dwSize = size; list_init(&message->entry); *msg = &message->msg; return S_OK; } static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct message *message; HRESULT hr; TRACE("(%p, %p)\n", This, msg); if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER; EnterCriticalSection(&This->safe); hr = !list_empty(&message->entry) ? DMUS_E_CANNOT_FREE : S_OK; LeaveCriticalSection(&This->safe); if (SUCCEEDED(hr)) { if (msg->pTool) IDirectMusicTool_Release(msg->pTool); if (msg->pGraph) IDirectMusicGraph_Release(msg->pGraph); if (msg->punkUser) IUnknown_Release(msg->punkUser); free(message); } return hr; } static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph **graph) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p)\n", This, graph); if (!graph) return E_POINTER; *graph = This->pToolGraph; if (This->pToolGraph) { IDirectMusicGraph_AddRef(*graph); } return *graph ? S_OK : DMUS_E_NOT_FOUND; } static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph *pGraph) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): to check\n", This, pGraph); if (NULL != This->pToolGraph) { /* Todo clean buffers and tools before */ IDirectMusicGraph_Release(This->pToolGraph); } This->pToolGraph = pGraph; if (NULL != This->pToolGraph) { IDirectMusicGraph_AddRef(This->pToolGraph); } return S_OK; } static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 *iface, HANDLE notification_event, REFERENCE_TIME minimum_time) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p, %I64d)\n", This, notification_event, minimum_time); This->notification_event = notification_event; if (minimum_time) This->notification_timeout = minimum_time; else if (!This->notification_timeout) This->notification_timeout = 20000000; /* 2 seconds */ return S_OK; } static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 *iface, DMUS_NOTIFICATION_PMSG **ret_msg) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct list *entry; TRACE("(%p, %p)\n", This, ret_msg); if (!ret_msg) return E_POINTER; EnterCriticalSection(&This->safe); if ((entry = list_head(&This->notifications))) { struct message *message = LIST_ENTRY(entry, struct message, entry); list_remove(&message->entry); list_init(&message->entry); *ret_msg = (DMUS_NOTIFICATION_PMSG *)&message->msg; } LeaveCriticalSection(&This->safe); return entry ? S_OK : S_FALSE; } static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID type) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr = S_OK; FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type)); if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE)) { hr = This->notification_performance ? S_FALSE : S_OK; This->notification_performance = TRUE; } if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT)) { hr = This->notification_segment ? S_FALSE : S_OK; This->notification_segment = TRUE; } return hr; } static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID type) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr = S_FALSE; FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type)); if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE)) { hr = This->notification_performance ? S_OK : S_FALSE; This->notification_performance = FALSE; } if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT)) { hr = This->notification_segment ? S_OK : S_FALSE; This->notification_segment = FALSE; } return hr; } static void performance_update_latency_time(struct performance *This, IDirectMusicPort *port, REFERENCE_TIME *ret_time) { IDirectMusicPerformance8 *iface = &This->IDirectMusicPerformance8_iface; REFERENCE_TIME latency_time, current_time; IReferenceClock *latency_clock; HRESULT hr; if (!ret_time) ret_time = &latency_time; if (SUCCEEDED(hr = IDirectMusicPort_GetLatencyClock(port, &latency_clock))) { hr = IReferenceClock_GetTime(latency_clock, ret_time); if (SUCCEEDED(hr)) hr = IDirectMusicPerformance8_GetTime(iface, ¤t_time, NULL); if (SUCCEEDED(hr) && This->latency_offset < (*ret_time - current_time)) { TRACE("Updating performance %p latency %I64d -> %I64d\n", This, This->latency_offset, *ret_time - current_time); This->latency_offset = *ret_time - current_time; } IReferenceClock_Release(latency_clock); } if (FAILED(hr)) ERR("Failed to update performance %p latency, hr %#lx\n", This, hr); } static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params) { IDirectMusicPort *port; GUID guid; unsigned int i; HRESULT hr; if (FAILED(hr = IDirectMusic8_GetDefaultPort(perf->dmusic, &guid))) return hr; if (FAILED(hr = IDirectMusic8_CreatePort(perf->dmusic, &guid, params, &port, NULL))) return hr; if (FAILED(hr = IDirectMusicPort_SetDirectSound(port, perf->dsound, NULL)) || FAILED(hr = IDirectMusicPort_Activate(port, TRUE))) { IDirectMusicPort_Release(port); return hr; } wine_rb_destroy(&perf->channel_blocks, channel_block_free, NULL); for (i = 0; i < params->dwChannelGroups; i++) { if (FAILED(hr = channel_block_init(perf, i, port, i + 1))) ERR("Failed to init channel block, hr %#lx\n", hr); } performance_update_latency_time(perf, port, NULL); IDirectMusicPort_Release(port); return S_OK; } static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDirectMusicPort *port) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): semi-stub\n", This, port); if (!This->dmusic) return DMUS_E_NOT_INIT; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; if (!port) { DMUS_PORTPARAMS params = { .dwSize = sizeof(params), .dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS, .dwChannelGroups = 1 }; return perf_dmport_create(This, ¶ms); } IDirectMusicPort_AddRef(port); /** * We should remember added Ports (for example using a list) * and control if Port is registered for each api who use ports */ performance_update_latency_time(This, port, NULL); return S_OK; } static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, IDirectMusicPort *pPort) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; FIXME("(%p, %p): stub\n", This, pPort); IDirectMusicPort_Release(pPort); return S_OK; } static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface, DWORD block_num, IDirectMusicPort *port, DWORD midi_group) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, midi_group); if (!port) return E_POINTER; if (block_num > MAXDWORD / 16) return E_INVALIDARG; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; return channel_block_init(This, block_num, port, midi_group); } static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD channel_num, IDirectMusicPort *port, DWORD midi_group, DWORD midi_channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct channel *channel; HRESULT hr; FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, channel_num, port, midi_group, midi_channel); if (!port) return E_POINTER; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; if (!(channel = performance_get_channel(This, channel_num))) { if (FAILED(hr = IDirectMusicPerformance8_AssignPChannelBlock(iface, channel_num / 16, port, 0))) return hr; if (!(channel = performance_get_channel(This, channel_num))) return DMUS_E_NOT_FOUND; } channel->midi_group = midi_group; channel->midi_channel = midi_channel; if (channel->port) IDirectMusicPort_Release(channel->port); if ((channel->port = port)) IDirectMusicPort_AddRef(channel->port); return S_OK; } static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD channel_num, IDirectMusicPort **port, DWORD *midi_group, DWORD *midi_channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct channel *channel; TRACE("(%p)->(%ld, %p, %p, %p)\n", This, channel_num, port, midi_group, midi_channel); if (!(channel = performance_get_channel(This, channel_num))) return E_INVALIDARG; if (port) { *port = channel->port; IDirectMusicPort_AddRef(*port); } if (midi_group) *midi_group = channel->midi_group; if (midi_channel) *midi_channel = channel->midi_channel; return S_OK; } static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *iface, IDirectMusicInstrument *instrument, DWORD port_channel, IDirectMusicDownloadedInstrument **downloaded, DMUS_NOTERANGE *note_ranges, DWORD note_range_count, IDirectMusicPort **port, DWORD *group, DWORD *music_channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicPort *tmp_port = NULL; HRESULT hr; TRACE("(%p, %p, %ld, %p, %p, %ld, %p, %p, %p)\n", This, instrument, port_channel, downloaded, note_ranges, note_range_count, port, group, music_channel); if (!port) port = &tmp_port; if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(iface, port_channel, port, group, music_channel))) return hr; hr = IDirectMusicPort_DownloadInstrument(*port, instrument, downloaded, note_ranges, note_range_count); if (tmp_port) IDirectMusicPort_Release(tmp_port); return hr; } static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DWORD dwFlags) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %ld, %ld): stub\n", This, mtTime, dwFlags); return S_OK; } static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID type, DWORD group, DWORD index, MUSIC_TIME music_time, MUSIC_TIME *next_time, void *param) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr; TRACE("(%p, %s, %ld, %ld, %ld, %p, %p)\n", This, debugstr_dmguid(type), group, index, music_time, next_time, param); if (next_time) *next_time = 0; if (!This->control_segment) hr = DMUS_E_NOT_FOUND; else hr = IDirectMusicSegment_GetParam(This->control_segment, type, group, index, music_time, next_time, param); if (FAILED(hr)) { if (!This->primary_segment) hr = DMUS_E_NOT_FOUND; else hr = IDirectMusicSegment_GetParam(This->primary_segment, type, group, index, music_time, next_time, param); } return hr; } static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %s, %ld, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pParam); return S_OK; } static HRESULT WINAPI performance_GetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, void *pParam, DWORD dwSize) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %s, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), pParam, dwSize); if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload)) memcpy(pParam, &This->fAutoDownload, sizeof(This->fAutoDownload)); if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel)) memcpy(pParam, &This->cMasterGrooveLevel, sizeof(This->cMasterGrooveLevel)); if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo)) memcpy(pParam, &This->fMasterTempo, sizeof(This->fMasterTempo)); if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume)) memcpy(pParam, &This->lMasterVolume, sizeof(This->lMasterVolume)); return S_OK; } static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, void *pParam, DWORD dwSize) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %s, %p, %ld)\n", This, debugstr_dmguid(rguidType), pParam, dwSize); if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload)) { memcpy(&This->fAutoDownload, pParam, dwSize); TRACE("=> AutoDownload set to %d\n", This->fAutoDownload); } if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel)) { memcpy(&This->cMasterGrooveLevel, pParam, dwSize); TRACE("=> MasterGrooveLevel set to %i\n", This->cMasterGrooveLevel); } if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo)) { memcpy(&This->fMasterTempo, pParam, dwSize); TRACE("=> MasterTempo set to %f\n", This->fMasterTempo); } if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume)) { memcpy(&This->lMasterVolume, pParam, dwSize); TRACE("=> MasterVolume set to %li\n", This->lMasterVolume); } return S_OK; } static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *ret_time) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); REFERENCE_TIME current_time; HRESULT hr; TRACE("(%p, %p)\n", This, ret_time); if (SUCCEEDED(hr = IDirectMusicPerformance8_GetTime(iface, ¤t_time, NULL))) *ret_time = current_time + This->latency_offset; return hr; } static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): stub\n", This, prtTime); return S_OK; } static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtAmount) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, 0x%s): stub\n", This, wine_dbgstr_longlong(rtAmount)); return S_OK; } static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct list states = LIST_INIT(states); struct state_entry *entry, *next; DMUS_PMSG msg = {.mtTime = -1}; HANDLE message_thread; HRESULT hr; FIXME("(%p): semi-stub\n", This); if ((message_thread = This->message_thread)) { EnterCriticalSection(&This->safe); This->message_thread = NULL; LeaveCriticalSection(&This->safe); WakeConditionVariable(&This->cond); WaitForSingleObject(message_thread, INFINITE); CloseHandle(message_thread); } This->notification_performance = FALSE; This->notification_segment = FALSE; enum_segment_states(This, NULL, &states); performance_flush_messages(This, NULL); LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry) { if (FAILED(hr = segment_state_end_play(entry->state, iface))) ERR("Failed to stop segment state, hr %#lx\n", hr); IDirectMusicSegmentState_Release(entry->state); list_remove(&entry->entry); free(entry); } if (FAILED(hr = performance_send_midi_pmsg(This, &msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_SYSTEM_RESET, 0, 0))) ERR("Failed to send MIDI_SYSTEM_RESET message, hr %#lx\n", hr); performance_set_primary_segment(This, NULL); performance_set_control_segment(This, NULL); if (This->master_clock) { IReferenceClock_Release(This->master_clock); This->master_clock = NULL; } if (This->dsound) { IDirectSound_Release(This->dsound); This->dsound = NULL; } if (This->dmusic) { IDirectMusic8_SetDirectSound(This->dmusic, NULL, NULL); IDirectMusic8_Release(This->dmusic); This->dmusic = NULL; } This->audio_paths_enabled = FALSE; return S_OK; } static HRESULT WINAPI performance_GetResolvedTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtTime, REFERENCE_TIME *prtResolved, DWORD dwTimeResolveFlags) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, 0x%s, %p, %ld): stub\n", This, wine_dbgstr_longlong(rtTime), prtResolved, dwTimeResolveFlags); return S_OK; } static HRESULT WINAPI performance_MIDIToMusic(IDirectMusicPerformance8 *iface, BYTE bMIDIValue, DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, WORD *pwMusicValue) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, bMIDIValue, pChord, bPlayMode, bChordLevel, pwMusicValue); return S_OK; } static HRESULT WINAPI performance_MusicToMIDI(IDirectMusicPerformance8 *iface, WORD wMusicValue, DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, BYTE *pbMIDIValue) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, wMusicValue, pChord, bPlayMode, bChordLevel, pbMIDIValue); return S_OK; } static HRESULT WINAPI performance_TimeToRhythm(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DMUS_TIMESIGNATURE *pTimeSig, WORD *pwMeasure, BYTE *pbBeat, BYTE *pbGrid, short *pnOffset) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %ld, %p, %p, %p, %p, %p): stub\n", This, mtTime, pTimeSig, pwMeasure, pbBeat, pbGrid, pnOffset); return S_OK; } static HRESULT WINAPI performance_RhythmToTime(IDirectMusicPerformance8 *iface, WORD wMeasure, BYTE bBeat, BYTE bGrid, short nOffset, DMUS_TIMESIGNATURE *pTimeSig, MUSIC_TIME *pmtTime) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %d, %d, %d, %i, %p, %p): stub\n", This, wMeasure, bBeat, bGrid, nOffset, pTimeSig, pmtTime); return S_OK; } /* IDirectMusicPerformance8 Interface part follow: */ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic, IDirectSound **dsound, HWND hwnd, DWORD default_path_type, DWORD num_channels, DWORD flags, DMUS_AUDIOPARAMS *params) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr = S_OK; TRACE("(%p, %p, %p, %p, %lx, %lu, %lx, %p)\n", This, dmusic, dsound, hwnd, default_path_type, num_channels, flags, params); if (flags) FIXME("flags parameter not used\n"); if (params) FIXME("params parameter not used\n"); if (FAILED(hr = IDirectMusicPerformance8_Init(iface, dmusic && *dmusic ? dmusic : NULL, dsound ? *dsound : NULL, hwnd))) return hr; This->audio_paths_enabled = TRUE; if (default_path_type) { hr = IDirectMusicPerformance8_CreateStandardAudioPath(iface, default_path_type, num_channels, FALSE, &This->pDefaultPath); if (FAILED(hr)) { IDirectMusicPerformance_CloseDown(iface); return hr; } } if (dsound && !*dsound) { *dsound = This->dsound; IDirectSound_AddRef(*dsound); } if (dmusic && !*dmusic) { *dmusic = This->dmusic; IDirectMusic_AddRef(*dmusic); } return S_OK; } static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *source, WCHAR *segment_name, IUnknown *transition, DWORD segment_flags, INT64 start_time, IDirectMusicSegmentState **segment_state, IUnknown *from, IUnknown *audio_path) { BOOL primary = !(segment_flags & DMUS_SEGF_SECONDARY), control = (segment_flags & DMUS_SEGF_CONTROL); struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicSegmentState *state; IDirectMusicSegment *segment; MUSIC_TIME music_time; HRESULT hr; FIXME("(%p, %p, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name), transition, segment_flags, start_time, segment_state, from, audio_path); /* NOTE: The time is in music time unless the DMUS_SEGF_REFTIME flag is set. */ if (segment_flags) FIXME("flags %#lx not implemented\n", segment_flags); if (start_time) FIXME("start_time %I64d not implemented\n", start_time); if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) return hr; EnterCriticalSection(&This->safe); if (primary) performance_set_primary_segment(This, segment); if (control) performance_set_control_segment(This, segment); if ((!(music_time = start_time) && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time))) || FAILED(hr = segment_state_create(segment, music_time, iface, &state))) { if (primary) performance_set_primary_segment(This, NULL); if (control) performance_set_control_segment(This, NULL); LeaveCriticalSection(&This->safe); IDirectMusicSegment_Release(segment); return hr; } if (FAILED(hr = segment_state_play(state, iface))) { ERR("Failed to play segment state, hr %#lx\n", hr); if (primary) performance_set_primary_segment(This, NULL); if (control) performance_set_control_segment(This, NULL); } else if (segment_state) { *segment_state = state; IDirectMusicSegmentState_AddRef(state); } LeaveCriticalSection(&This->safe); IDirectMusicSegmentState_Release(state); IDirectMusicSegment_Release(segment); return hr; } static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnknown *pObjectToStop, __int64 i64StopTime, DWORD dwFlags) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, 0x%s, %ld): stub\n", This, pObjectToStop, wine_dbgstr_longlong(i64StopTime), dwFlags); return S_OK; } static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg, DMUS_PMSG **clone) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr; TRACE("(%p, %p, %p)\n", This, msg, clone); if (!msg || !clone) return E_POINTER; if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(iface, msg->dwSize, clone))) return hr; memcpy(*clone, msg, msg->dwSize); if (msg->pTool) IDirectMusicTool_AddRef(msg->pTool); if (msg->pGraph) IDirectMusicGraph_AddRef(msg->pGraph); if (msg->punkUser) IUnknown_AddRef(msg->punkUser); return S_OK; } static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface, IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ret_iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicAudioPath *pPath; IDirectMusicObject *dmo; IDirectSoundBuffer *buffer, *primary_buffer; DMUS_OBJECTDESC objDesc; DMUS_PORTPARAMS8 port_params; HRESULT hr; WAVEFORMATEX format; DSBUFFERDESC desc; FIXME("(%p, %p, %d, %p): semi-stub\n", This, pSourceConfig, fActivate, ret_iface); if (!ret_iface || !pSourceConfig) return E_POINTER; if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; hr = IUnknown_QueryInterface(pSourceConfig, &IID_IDirectMusicObject, (void **)&dmo); if (FAILED(hr)) return hr; hr = IDirectMusicObject_GetDescriptor(dmo, &objDesc); IDirectMusicObject_Release(dmo); if (FAILED(hr)) return hr; if (!IsEqualCLSID(&objDesc.guidClass, &CLSID_DirectMusicAudioPathConfig)) { ERR("Unexpected object class %s for source config.\n", debugstr_dmguid(&objDesc.guidClass)); return E_INVALIDARG; } hr = path_config_get_audio_path_params(pSourceConfig, &format, &desc, &port_params); if (FAILED(hr)) return hr; hr = perf_dmport_create(This, &port_params); if (FAILED(hr)) return hr; hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &buffer, NULL); if (FAILED(hr)) return DSERR_BUFFERLOST; /* Update description for creating primary buffer */ desc.dwFlags |= DSBCAPS_PRIMARYBUFFER; desc.dwFlags &= ~DSBCAPS_CTRLFX; desc.dwBufferBytes = 0; desc.lpwfxFormat = NULL; hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &primary_buffer, NULL); if (FAILED(hr)) { IDirectSoundBuffer_Release(buffer); return DSERR_BUFFERLOST; } create_dmaudiopath(&IID_IDirectMusicAudioPath, (void **)&pPath); set_audiopath_perf_pointer(pPath, iface); set_audiopath_dsound_buffer(pPath, buffer); set_audiopath_primary_dsound_buffer(pPath, primary_buffer); TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface); *ret_iface = pPath; return IDirectMusicAudioPath_Activate(*ret_iface, fActivate); } static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface, DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ret_iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicAudioPath *pPath; DSBUFFERDESC desc; WAVEFORMATEX format; DMUS_PORTPARAMS params = {0}; IDirectSoundBuffer *buffer, *primary_buffer; HRESULT hr = S_OK; FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This, dwType, pchannel_count, fActivate, ret_iface); if (!ret_iface) return E_POINTER; if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; *ret_iface = NULL; /* Secondary buffer description */ memset(&format, 0, sizeof(format)); format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = 1; format.nSamplesPerSec = 44000; format.nAvgBytesPerSec = 44000*2; format.nBlockAlign = 2; format.wBitsPerSample = 16; format.cbSize = 0; memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); desc.dwFlags = DSBCAPS_CTRLFX | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS; desc.dwBufferBytes = DSBSIZE_MIN; desc.dwReserved = 0; desc.lpwfxFormat = &format; desc.guid3DAlgorithm = GUID_NULL; switch(dwType) { case DMUS_APATH_DYNAMIC_3D: desc.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE; break; case DMUS_APATH_DYNAMIC_MONO: desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY; break; case DMUS_APATH_SHARED_STEREOPLUSREVERB: /* normally we have to create 2 buffers (one for music other for reverb) * in this case. See msdn */ case DMUS_APATH_DYNAMIC_STEREO: desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY; format.nChannels = 2; format.nBlockAlign *= 2; format.nAvgBytesPerSec *=2; break; default: return E_INVALIDARG; } /* Create a port */ params.dwSize = sizeof(params); params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS | DMUS_PORTPARAMS_AUDIOCHANNELS; params.dwChannelGroups = (pchannel_count + 15) / 16; params.dwAudioChannels = format.nChannels; if (FAILED(hr = perf_dmport_create(This, ¶ms))) return hr; hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &buffer, NULL); if (FAILED(hr)) return DSERR_BUFFERLOST; /* Update description for creating primary buffer */ desc.dwFlags |= DSBCAPS_PRIMARYBUFFER; desc.dwFlags &= ~DSBCAPS_CTRLFX; desc.dwBufferBytes = 0; desc.lpwfxFormat = NULL; hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &primary_buffer, NULL); if (FAILED(hr)) { IDirectSoundBuffer_Release(buffer); return DSERR_BUFFERLOST; } create_dmaudiopath(&IID_IDirectMusicAudioPath, (void**)&pPath); set_audiopath_perf_pointer(pPath, iface); set_audiopath_dsound_buffer(pPath, buffer); set_audiopath_primary_dsound_buffer(pPath, primary_buffer); *ret_iface = pPath; TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface); return IDirectMusicAudioPath_Activate(*ret_iface, fActivate); } static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *audio_path) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): semi-stub\n", This, audio_path); if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; if (This->pDefaultPath) IDirectMusicAudioPath_Release(This->pDefaultPath); if ((This->pDefaultPath = audio_path)) { IDirectMusicAudioPath_AddRef(This->pDefaultPath); set_audiopath_perf_pointer(This->pDefaultPath, iface); } return S_OK; } static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath **ret_iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): semi-stub (%p)\n", This, ret_iface, This->pDefaultPath); if (!ret_iface) return E_POINTER; if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; if ((*ret_iface = This->pDefaultPath)) IDirectMusicAudioPath_AddRef(*ret_iface); return S_OK; } static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %s, %ld, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwTrackID, dwGroupBits, dwIndex, mtTime, pmtNext, pParam); return S_OK; } static const IDirectMusicPerformance8Vtbl performance_vtbl = { performance_QueryInterface, performance_AddRef, performance_Release, performance_Init, performance_PlaySegment, performance_Stop, performance_GetSegmentState, performance_SetPrepareTime, performance_GetPrepareTime, performance_SetBumperLength, performance_GetBumperLength, performance_SendPMsg, performance_MusicToReferenceTime, performance_ReferenceToMusicTime, performance_IsPlaying, performance_GetTime, performance_AllocPMsg, performance_FreePMsg, performance_GetGraph, performance_SetGraph, performance_SetNotificationHandle, performance_GetNotificationPMsg, performance_AddNotificationType, performance_RemoveNotificationType, performance_AddPort, performance_RemovePort, performance_AssignPChannelBlock, performance_AssignPChannel, performance_PChannelInfo, performance_DownloadInstrument, performance_Invalidate, performance_GetParam, performance_SetParam, performance_GetGlobalParam, performance_SetGlobalParam, performance_GetLatencyTime, performance_GetQueueTime, performance_AdjustTime, performance_CloseDown, performance_GetResolvedTime, performance_MIDIToMusic, performance_MusicToMIDI, performance_TimeToRhythm, performance_RhythmToTime, performance_InitAudio, performance_PlaySegmentEx, performance_StopEx, performance_ClonePMsg, performance_CreateAudioPath, performance_CreateStandardAudioPath, performance_SetDefaultAudioPath, performance_GetDefaultAudioPath, performance_GetParamEx, }; static inline struct performance *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface) { return CONTAINING_RECORD(iface, struct performance, IDirectMusicGraph_iface); } static HRESULT WINAPI performance_graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface) { struct performance *This = impl_from_IDirectMusicGraph(iface); return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface); } static ULONG WINAPI performance_graph_AddRef(IDirectMusicGraph *iface) { struct performance *This = impl_from_IDirectMusicGraph(iface); return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface); } static ULONG WINAPI performance_graph_Release(IDirectMusicGraph *iface) { struct performance *This = impl_from_IDirectMusicGraph(iface); return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface); } static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicGraph(iface); HRESULT hr; TRACE("(%p, %p)\n", This, msg); if (!msg) return E_POINTER; /* FIXME: Implement segment and audio path graphs support */ if (!This->pToolGraph) hr = DMUS_S_LAST_TOOL; else if (FAILED(hr = IDirectMusicGraph_StampPMsg(This->pToolGraph, msg))) return hr; if (msg->pGraph) { IDirectMusicTool_Release(msg->pGraph); msg->pGraph = NULL; } if (hr == DMUS_S_LAST_TOOL) { const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; msg->dwFlags &= ~delivery_flags; msg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE; if (msg->pTool) IDirectMusicTool_Release(msg->pTool); msg->pTool = &This->IDirectMusicTool_iface; IDirectMusicTool_AddRef(msg->pTool); return S_OK; } if (SUCCEEDED(hr)) { msg->pGraph = &This->IDirectMusicGraph_iface; IDirectMusicTool_AddRef(msg->pGraph); } return hr; } static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool, DWORD *channels, DWORD channels_count, LONG index) { struct performance *This = impl_from_IDirectMusicGraph(iface); TRACE("(%p, %p, %p, %lu, %ld)\n", This, tool, channels, channels_count, index); return E_NOTIMPL; } static HRESULT WINAPI performance_graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **tool) { struct performance *This = impl_from_IDirectMusicGraph(iface); TRACE("(%p, %lu, %p)\n", This, index, tool); return E_NOTIMPL; } static HRESULT WINAPI performance_graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool) { struct performance *This = impl_from_IDirectMusicGraph(iface); TRACE("(%p, %p)\n", This, tool); return E_NOTIMPL; } static const IDirectMusicGraphVtbl performance_graph_vtbl = { performance_graph_QueryInterface, performance_graph_AddRef, performance_graph_Release, performance_graph_StampPMsg, performance_graph_InsertTool, performance_graph_GetTool, performance_graph_RemoveTool, }; static inline struct performance *impl_from_IDirectMusicTool(IDirectMusicTool *iface) { return CONTAINING_RECORD(iface, struct performance, IDirectMusicTool_iface); } static HRESULT WINAPI performance_tool_QueryInterface(IDirectMusicTool *iface, REFIID riid, void **ret_iface) { struct performance *This = impl_from_IDirectMusicTool(iface); return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface); } static ULONG WINAPI performance_tool_AddRef(IDirectMusicTool *iface) { struct performance *This = impl_from_IDirectMusicTool(iface); return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface); } static ULONG WINAPI performance_tool_Release(IDirectMusicTool *iface) { struct performance *This = impl_from_IDirectMusicTool(iface); return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface); } static HRESULT WINAPI performance_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph) { struct performance *This = impl_from_IDirectMusicTool(iface); TRACE("(%p, %p)\n", This, graph); return E_NOTIMPL; } static HRESULT WINAPI performance_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type) { struct performance *This = impl_from_IDirectMusicTool(iface); TRACE("(%p, %p)\n", This, type); *type = DMUS_PMSGF_TOOL_IMMEDIATE; return S_OK; } static HRESULT WINAPI performance_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size) { struct performance *This = impl_from_IDirectMusicTool(iface); TRACE("(%p, %p)\n", This, size); *size = 0; return S_OK; } static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size) { struct performance *This = impl_from_IDirectMusicTool(iface); TRACE("(%p, %p, %lu)\n", This, types, size); return E_NOTIMPL; } static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicTool(iface); struct message *message = message_from_DMUS_PMSG(msg); HRESULT hr; TRACE("(%p, %p, %p)\n", This, performance, msg); switch (msg->dwType) { case DMUS_PMSGT_MIDI: { static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD); DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size}; DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg; REFERENCE_TIME latency_time; IDirectMusicBuffer *buffer; IDirectMusicPort *port; DWORD group, channel; UINT value = 0; if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(performance, msg->dwPChannel, &port, &group, &channel))) { WARN("Failed to get message port, hr %#lx\n", hr); return DMUS_S_FREE; } performance_update_latency_time(This, port, &latency_time); value |= channel; value |= (UINT)midi->bStatus; value |= (UINT)midi->bByte1 << 8; value |= (UINT)midi->bByte2 << 16; if (SUCCEEDED(hr = IDirectMusic_CreateMusicBuffer(This->dmusic, &desc, &buffer, NULL))) { if (msg->rtTime == -1) msg->rtTime = latency_time; hr = IDirectMusicBuffer_PackStructured(buffer, msg->rtTime, group, value); if (SUCCEEDED(hr)) hr = IDirectMusicPort_PlayBuffer(port, buffer); IDirectMusicBuffer_Release(buffer); } IDirectMusicPort_Release(port); break; } case DMUS_PMSGT_NOTE: { DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg; msg->mtTime += note->nOffset; if (note->bFlags & DMUS_NOTEF_NOTEON) { if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_NOTE_ON, note->bMidiValue, note->bVelocity))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); if (note->mtDuration) { msg->mtTime -= note->nOffset; msg->mtTime += max(1, note->mtDuration - 1); if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(performance, msg->mtTime, &msg->rtTime))) return hr; note->bFlags &= ~DMUS_NOTEF_NOTEON; return DMUS_S_REQUEUE; } } if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_NOTE_OFF, note->bMidiValue, 0))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); break; } case DMUS_PMSGT_CURVE: { DMUS_CURVE_PMSG *curve = (DMUS_CURVE_PMSG *)msg; msg->mtTime += curve->nOffset; switch (curve->bType) { case DMUS_CURVET_CCCURVE: if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_CONTROL_CHANGE, curve->bCCData, curve->nStartValue))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); break; case DMUS_CURVET_RPNCURVE: case DMUS_CURVET_NRPNCURVE: case DMUS_CURVET_MATCURVE: case DMUS_CURVET_PATCURVE: case DMUS_CURVET_PBCURVE: FIXME("Unhandled curve type %#x\n", curve->bType); break; default: WARN("Invalid curve type %#x\n", curve->bType); break; } break; } case DMUS_PMSGT_PATCH: { DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg; if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_CONTROL_CHANGE, MIDI_CC_BANK_MSB, patch->byMSB))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_CONTROL_CHANGE, MIDI_CC_BANK_LSB, patch->byLSB))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_PROGRAM_CHANGE, patch->byInstrument, 0))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); break; } case DMUS_PMSGT_NOTIFICATION: { DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg; struct message *previous; BOOL enabled = FALSE; if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE)) enabled = This->notification_performance; if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) enabled = This->notification_segment; if (!enabled) return DMUS_S_FREE; if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) { /* re-send the message for queueing at the expected time */ msg->dwFlags &= ~DMUS_PMSGF_TOOL_IMMEDIATE; msg->dwFlags |= DMUS_PMSGF_TOOL_ATTIME; if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg))) { ERR("Failed to send notification message, hr %#lx\n", hr); return DMUS_S_FREE; } return S_OK; } list_add_tail(&This->notifications, &message->entry); /* discard old notification messages */ do { previous = LIST_ENTRY(list_head(&This->notifications), struct message, entry); if (This->notification_timeout <= 0) break; /* negative values may be used to keep everything */ if (message->msg.rtTime - previous->msg.rtTime <= This->notification_timeout) break; list_remove(&previous->entry); list_init(&previous->entry); } while (SUCCEEDED(hr = IDirectMusicPerformance_FreePMsg(performance, &previous->msg))); SetEvent(This->notification_event); return S_OK; } case DMUS_PMSGT_WAVE: if (FAILED(hr = IDirectSoundBuffer_Play((IDirectSoundBuffer *)msg->punkUser, 0, 0, 0))) WARN("Failed to play wave buffer, hr %#lx\n", hr); break; case DMUS_PMSGT_INTERNAL_SEGMENT_TICK: msg->rtTime += 10000000; msg->dwFlags &= ~DMUS_PMSGF_MUSICTIME; /* re-send the tick message until segment_state_tick returns S_FALSE */ if (FAILED(hr = segment_state_tick((IDirectMusicSegmentState *)msg->punkUser, (IDirectMusicPerformance8 *)performance))) ERR("Failed to tick segment state %p, hr %#lx\n", msg->punkUser, hr); else if (hr == S_FALSE) return DMUS_S_FREE; /* done ticking */ else if (FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, msg))) ERR("Failed to queue tick for segment state %p, hr %#lx\n", msg->punkUser, hr); return S_OK; case DMUS_PMSGT_INTERNAL_SEGMENT_END: if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)msg->punkUser, (IDirectMusicPerformance8 *)performance))) WARN("Failed to end segment state %p, hr %#lx\n", msg->punkUser, hr); break; default: FIXME("Unhandled message type %#lx\n", msg->dwType); break; } return DMUS_S_FREE; } static HRESULT WINAPI performance_tool_Flush(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg, REFERENCE_TIME time) { struct performance *This = impl_from_IDirectMusicTool(iface); FIXME("(%p, %p, %p, %I64d): stub\n", This, performance, msg, time); return E_NOTIMPL; } static const IDirectMusicToolVtbl performance_tool_vtbl = { performance_tool_QueryInterface, performance_tool_AddRef, performance_tool_Release, performance_tool_Init, performance_tool_GetMsgDeliveryType, performance_tool_GetMediaTypeArraySize, performance_tool_GetMediaTypes, performance_tool_ProcessPMsg, performance_tool_Flush, }; /* for ClassFactory */ HRESULT create_dmperformance(REFIID iid, void **ret_iface) { struct performance *obj; HRESULT hr; TRACE("(%s, %p)\n", debugstr_guid(iid), ret_iface); *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl; obj->IDirectMusicGraph_iface.lpVtbl = &performance_graph_vtbl; obj->IDirectMusicTool_iface.lpVtbl = &performance_tool_vtbl; obj->ref = 1; obj->pDefaultPath = NULL; InitializeCriticalSectionEx(&obj->safe, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe"); wine_rb_init(&obj->channel_blocks, channel_block_compare); list_init(&obj->messages); list_init(&obj->notifications); obj->latency_offset = 50; obj->dwBumperLength = 50; /* 50 ms default */ obj->dwPrepareTime = 1000; /* 1000 ms default */ hr = IDirectMusicPerformance8_QueryInterface(&obj->IDirectMusicPerformance8_iface, iid, ret_iface); IDirectMusicPerformance_Release(&obj->IDirectMusicPerformance8_iface); return hr; } static inline struct performance *unsafe_impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface) { if (iface->lpVtbl != &performance_vtbl) return NULL; return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface); } HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound) { struct performance *This = unsafe_impl_from_IDirectMusicPerformance8(iface); if (!This || !(*dsound = This->dsound)) return E_FAIL; IDirectSound_AddRef(*dsound); return S_OK; }