dmime: Avoid crashing when purging notification messages.
[wine.git] / dlls / dmime / performance.c
bloba2e611523a65b4abe7db457aec3031ec1ee753f6
1 /* IDirectMusicPerformance Implementation
3 * Copyright (C) 2003-2004 Rok Mandeljc
4 * Copyright (C) 2003-2004 Raphael Junqueira
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "dmime_private.h"
22 #include "dmusic_midi.h"
23 #include "wine/rbtree.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(dmime);
27 struct pchannel_block {
28 DWORD block_num; /* Block 0 is PChannels 0-15, Block 1 is PChannels 16-31, etc */
29 struct {
30 DWORD channel; /* MIDI channel */
31 DWORD group; /* MIDI group */
32 IDirectMusicPort *port;
33 } pchannel[16];
34 struct wine_rb_entry entry;
37 struct performance
39 IDirectMusicPerformance8 IDirectMusicPerformance8_iface;
40 IDirectMusicGraph IDirectMusicGraph_iface;
41 IDirectMusicTool IDirectMusicTool_iface;
42 LONG ref;
43 IDirectMusic *dmusic;
44 IDirectSound *dsound;
45 IDirectMusicGraph *pToolGraph;
46 BOOL fAutoDownload;
47 char cMasterGrooveLevel;
48 float fMasterTempo;
49 long lMasterVolume;
50 /* performance channels */
51 struct wine_rb_tree pchannels;
53 BOOL audio_paths_enabled;
54 IDirectMusicAudioPath *pDefaultPath;
55 REFERENCE_TIME latency_offset;
56 DWORD dwBumperLength;
57 DWORD dwPrepareTime;
59 HANDLE message_thread;
60 CRITICAL_SECTION safe;
61 CONDITION_VARIABLE cond;
63 IReferenceClock *master_clock;
64 REFERENCE_TIME init_time;
65 struct list messages;
67 struct list notifications;
68 REFERENCE_TIME notification_timeout;
69 HANDLE notification_event;
70 BOOL notification_performance;
71 BOOL notification_segment;
74 struct message
76 struct list entry;
77 DMUS_PMSG msg;
80 static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg)
82 return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL;
85 static HRESULT performance_process_message(struct performance *This, DMUS_PMSG *msg, DWORD *timeout)
87 static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
88 IDirectMusicPerformance *performance = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface;
89 HRESULT hr;
93 REFERENCE_TIME latency, offset = 0;
94 IDirectMusicTool *tool;
96 if (FAILED(hr = IDirectMusicPerformance_GetLatencyTime(performance, &latency))) return hr;
97 if (!(tool = msg->pTool)) tool = &This->IDirectMusicTool_iface;
99 switch (msg->dwFlags & delivery_flags)
101 default:
102 WARN("No delivery flag found for message %p\n", msg);
103 /* fallthrough */
104 case DMUS_PMSGF_TOOL_IMMEDIATE:
105 hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg);
106 break;
107 case DMUS_PMSGF_TOOL_QUEUE:
108 offset = This->dwBumperLength * 10000;
109 /* fallthrough */
110 case DMUS_PMSGF_TOOL_ATTIME:
111 if (msg->rtTime >= offset && msg->rtTime - offset >= latency)
113 if (timeout) *timeout = (msg->rtTime - offset - latency) / 10000;
114 return DMUS_S_REQUEUE;
117 hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg);
118 break;
120 } while (hr == DMUS_S_REQUEUE);
122 if (hr == DMUS_S_FREE) hr = IDirectMusicPerformance_FreePMsg(performance, msg);
123 if (FAILED(hr)) WARN("Failed to process message, hr %#lx\n", hr);
124 return hr;
127 static DWORD WINAPI message_thread_proc(void *args)
129 struct performance *This = args;
130 struct message *message, *next;
131 HRESULT hr;
133 TRACE("performance %p message thread\n", This);
134 SetThreadDescription(GetCurrentThread(), L"wine_dmime_message");
136 EnterCriticalSection(&This->safe);
138 while (This->message_thread)
140 DWORD timeout = INFINITE;
142 LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry)
144 list_remove(&message->entry);
145 list_init(&message->entry);
147 hr = performance_process_message(This, &message->msg, &timeout);
148 if (hr == DMUS_S_REQUEUE) list_add_before(&next->entry, &message->entry);
149 if (hr != S_OK) break;
152 SleepConditionVariableCS(&This->cond, &This->safe, timeout);
155 LeaveCriticalSection(&This->safe);
157 TRACE("(%p): Exiting\n", This);
158 return 0;
161 static HRESULT performance_send_dirty_pmsg(struct performance *This, MUSIC_TIME music_time)
163 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
164 IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface;
165 DMUS_PMSG *msg;
166 HRESULT hr;
168 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), &msg)))
169 return hr;
171 msg->mtTime = music_time;
172 msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE;
173 msg->dwType = DMUS_PMSGT_DIRTY;
175 if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, msg))
176 || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg)))
177 IDirectMusicPerformance8_FreePMsg(performance, msg);
179 return hr;
182 static HRESULT performance_send_notification_pmsg(struct performance *This, MUSIC_TIME music_time, BOOL stamp,
183 GUID type, DWORD option, IUnknown *object)
185 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
186 IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface;
187 DMUS_NOTIFICATION_PMSG *msg;
188 HRESULT hr;
190 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg)))
191 return hr;
193 msg->mtTime = music_time;
194 msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE;
195 msg->dwType = DMUS_PMSGT_NOTIFICATION;
196 if ((msg->punkUser = object)) IUnknown_AddRef(object);
197 msg->guidNotificationType = type;
198 msg->dwNotificationOption = option;
200 /* only stamp the message if notifications are enabled, otherwise send them directly to the output tool */
201 if ((stamp && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)))
202 || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)msg)))
203 IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)msg);
205 return hr;
208 static int pchannel_block_compare(const void *key, const struct wine_rb_entry *entry)
210 const struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct pchannel_block, entry);
212 return *(DWORD *)key - b->block_num;
215 static void pchannel_block_free(struct wine_rb_entry *entry, void *context)
217 struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry);
219 free(b);
222 static struct pchannel_block *pchannel_block_set(struct wine_rb_tree *tree, DWORD block_num,
223 IDirectMusicPort *port, DWORD group, BOOL only_set_new)
225 struct pchannel_block *block;
226 struct wine_rb_entry *entry;
227 unsigned int i;
229 entry = wine_rb_get(tree, &block_num);
230 if (entry) {
231 block = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry);
232 if (only_set_new)
233 return block;
234 } else {
235 if (!(block = malloc(sizeof(*block)))) return NULL;
236 block->block_num = block_num;
239 for (i = 0; i < 16; ++i) {
240 block->pchannel[i].port = port;
241 block->pchannel[i].group = group;
242 block->pchannel[i].channel = i;
244 if (!entry)
245 wine_rb_put(tree, &block->block_num, &block->entry);
247 return block;
250 static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface)
252 return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface);
255 /* IDirectMusicPerformance8 IUnknown part: */
256 static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface)
258 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
260 TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface);
262 if (IsEqualGUID(riid, &IID_IUnknown)
263 || IsEqualGUID(riid, &IID_IDirectMusicPerformance)
264 || IsEqualGUID(riid, &IID_IDirectMusicPerformance2)
265 || IsEqualGUID(riid, &IID_IDirectMusicPerformance8))
267 *ret_iface = iface;
268 IUnknown_AddRef(iface);
269 return S_OK;
272 if (IsEqualGUID(riid, &IID_IDirectMusicGraph))
274 *ret_iface = &This->IDirectMusicGraph_iface;
275 IDirectMusicGraph_AddRef(&This->IDirectMusicGraph_iface);
276 return S_OK;
279 if (IsEqualGUID(riid, &IID_IDirectMusicTool))
281 *ret_iface = &This->IDirectMusicTool_iface;
282 IDirectMusicTool_AddRef(&This->IDirectMusicTool_iface);
283 return S_OK;
286 *ret_iface = NULL;
287 WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface);
288 return E_NOINTERFACE;
291 static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface)
293 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
294 ULONG ref = InterlockedIncrement(&This->ref);
296 TRACE("(%p): ref=%ld\n", This, ref);
298 return ref;
301 static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface)
303 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
304 ULONG ref = InterlockedDecrement(&This->ref);
306 TRACE("(%p): ref=%ld\n", This, ref);
308 if (ref == 0) {
309 wine_rb_destroy(&This->pchannels, pchannel_block_free, NULL);
310 This->safe.DebugInfo->Spare[0] = 0;
311 DeleteCriticalSection(&This->safe);
312 free(This);
315 return ref;
318 static HRESULT performance_init_dsound(struct performance *This, HWND hwnd)
320 IDirectSound *dsound;
321 HRESULT hr;
323 if (FAILED(hr = DirectSoundCreate(NULL, &dsound, NULL))) return hr;
325 if (!hwnd) hwnd = GetForegroundWindow();
326 hr = IDirectSound_SetCooperativeLevel(dsound, hwnd, DSSCL_PRIORITY);
328 if (SUCCEEDED(hr)) This->dsound = dsound;
329 else IDirectSound_Release(dsound);
331 return hr;
334 static HRESULT performance_init_dmusic(struct performance *This, IDirectSound *dsound)
336 IDirectMusic *dmusic;
337 HRESULT hr;
339 if (FAILED(hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER,
340 &IID_IDirectMusic8, (void **)&dmusic)))
341 return hr;
343 hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL);
345 if (SUCCEEDED(hr)) This->dmusic = dmusic;
346 else IDirectSound_Release(dmusic);
348 return hr;
351 static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic,
352 IDirectSound *dsound, HWND hwnd)
354 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
355 HRESULT hr;
357 TRACE("(%p, %p, %p, %p)\n", iface, dmusic, dsound, hwnd);
359 if (This->dmusic) return DMUS_E_ALREADY_INITED;
361 if ((This->dsound = dsound)) IDirectMusic8_AddRef(This->dsound);
362 else if (FAILED(hr = performance_init_dsound(This, hwnd))) return hr;
364 if (dmusic && (This->dmusic = *dmusic)) IDirectMusic_AddRef(This->dmusic);
365 else if (FAILED(hr = performance_init_dmusic(This, This->dsound)))
367 IDirectMusicPerformance_CloseDown(iface);
368 return hr;
371 if (FAILED(hr = IDirectMusic_GetMasterClock(This->dmusic, NULL, &This->master_clock))
372 || FAILED(hr = IDirectMusicPerformance8_GetTime(iface, &This->init_time, NULL)))
374 IDirectMusicPerformance_CloseDown(iface);
375 return hr;
378 if (!(This->message_thread = CreateThread(NULL, 0, message_thread_proc, This, 0, NULL)))
380 ERR("Failed to start performance message thread, error %lu\n", GetLastError());
381 IDirectMusicPerformance_CloseDown(iface);
382 return HRESULT_FROM_WIN32(GetLastError());
385 if (dmusic && !*dmusic)
387 *dmusic = This->dmusic;
388 IDirectMusic_AddRef(*dmusic);
390 return S_OK;
393 static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment,
394 DWORD segment_flags, INT64 start_time, IDirectMusicSegmentState **ret_state)
396 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
398 TRACE("(%p, %p, %ld, %I64d, %p)\n", This, segment, segment_flags, start_time, ret_state);
400 return IDirectMusicPerformance8_PlaySegmentEx(iface, (IUnknown *)segment, NULL, NULL,
401 segment_flags, start_time, ret_state, NULL, NULL);
404 static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment,
405 IDirectMusicSegmentState *pSegmentState, MUSIC_TIME mtTime, DWORD dwFlags)
407 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
409 FIXME("(%p, %p, %p, %ld, %ld): stub\n", This, pSegment, pSegmentState, mtTime, dwFlags);
410 return S_OK;
413 static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface,
414 IDirectMusicSegmentState **ppSegmentState, MUSIC_TIME mtTime)
416 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
418 FIXME("(%p,%p, %ld): stub\n", This, ppSegmentState, mtTime);
419 return S_OK;
422 static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds)
424 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
426 TRACE("(%p, %ld)\n", This, dwMilliSeconds);
427 This->dwPrepareTime = dwMilliSeconds;
428 return S_OK;
431 static HRESULT WINAPI performance_GetPrepareTime(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds)
433 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
435 TRACE("(%p, %p)\n", This, pdwMilliSeconds);
436 if (NULL == pdwMilliSeconds) {
437 return E_POINTER;
439 *pdwMilliSeconds = This->dwPrepareTime;
440 return S_OK;
443 static HRESULT WINAPI performance_SetBumperLength(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds)
445 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
447 TRACE("(%p, %ld)\n", This, dwMilliSeconds);
448 This->dwBumperLength = dwMilliSeconds;
449 return S_OK;
452 static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds)
454 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
456 TRACE("(%p, %p)\n", This, pdwMilliSeconds);
457 if (NULL == pdwMilliSeconds) {
458 return E_POINTER;
460 *pdwMilliSeconds = This->dwBumperLength;
461 return S_OK;
464 static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg)
466 const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
467 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
468 struct message *message, *next;
469 HRESULT hr;
471 TRACE("(%p, %p)\n", This, msg);
473 if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER;
474 if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK;
475 if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG;
477 EnterCriticalSection(&This->safe);
479 if (!list_empty(&message->entry))
480 hr = DMUS_E_ALREADY_SENT;
481 else
483 if (!(msg->dwFlags & delivery_flags)) msg->dwFlags |= DMUS_PMSGF_TOOL_IMMEDIATE;
484 if (!(msg->dwFlags & DMUS_PMSGF_MUSICTIME))
486 if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface,
487 msg->rtTime, &msg->mtTime)))
488 goto done;
489 msg->dwFlags |= DMUS_PMSGF_MUSICTIME;
491 if (!(msg->dwFlags & DMUS_PMSGF_REFTIME))
493 if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface,
494 msg->mtTime == -1 ? 0 : msg->mtTime, &msg->rtTime)))
495 goto done;
496 msg->dwFlags |= DMUS_PMSGF_REFTIME;
499 if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE)
501 hr = performance_process_message(This, &message->msg, NULL);
502 if (hr != DMUS_S_REQUEUE) goto done;
505 LIST_FOR_EACH_ENTRY(next, &This->messages, struct message, entry)
506 if (next->msg.rtTime > message->msg.rtTime) break;
507 list_add_before(&next->entry, &message->entry);
509 hr = S_OK;
512 done:
513 LeaveCriticalSection(&This->safe);
514 if (SUCCEEDED(hr)) WakeConditionVariable(&This->cond);
516 return hr;
519 static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface,
520 MUSIC_TIME music_time, REFERENCE_TIME *time)
522 static int once;
523 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
525 if (!once++) FIXME("(%p, %ld, %p): semi-stub\n", This, music_time, time);
526 else TRACE("(%p, %ld, %p)\n", This, music_time, time);
528 if (!time) return E_POINTER;
529 *time = 0;
531 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
533 /* FIXME: This should be (music_time * 60) / (DMUS_PPQ * tempo)
534 * but it gives innacurate results */
535 *time = This->init_time + (music_time * 6510);
537 return S_OK;
540 static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface,
541 REFERENCE_TIME time, MUSIC_TIME *music_time)
543 static int once;
544 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
546 if (!once++) FIXME("(%p, %I64d, %p): semi-stub\n", This, time, music_time);
547 else TRACE("(%p, %I64d, %p)\n", This, time, music_time);
549 if (!music_time) return E_POINTER;
550 *music_time = 0;
552 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
554 /* FIXME: This should be (time * DMUS_PPQ * tempo) / 60
555 * but it gives innacurate results */
556 *music_time = (time - This->init_time) / 6510;
558 return S_OK;
561 static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface,
562 IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegState)
564 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
566 FIXME("(%p, %p, %p): stub\n", This, pSegment, pSegState);
567 return S_FALSE;
570 static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *time, MUSIC_TIME *music_time)
572 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
573 REFERENCE_TIME now;
574 HRESULT hr;
576 TRACE("(%p, %p, %p)\n", iface, time, music_time);
578 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
579 if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &now))) return hr;
581 if (time) *time = now;
582 if (music_time) hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, now, music_time);
584 return hr;
587 static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG size, DMUS_PMSG **msg)
589 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
590 struct message *message;
592 TRACE("(%p, %ld, %p)\n", This, size, msg);
594 if (!msg) return E_POINTER;
595 if (size < sizeof(DMUS_PMSG)) return E_INVALIDARG;
597 if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(struct message)))) return E_OUTOFMEMORY;
598 message->msg.dwSize = size;
599 list_init(&message->entry);
600 *msg = &message->msg;
602 return S_OK;
605 static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg)
607 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
608 struct message *message;
609 HRESULT hr;
611 TRACE("(%p, %p)\n", This, msg);
613 if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER;
615 EnterCriticalSection(&This->safe);
616 hr = !list_empty(&message->entry) ? DMUS_E_CANNOT_FREE : S_OK;
617 LeaveCriticalSection(&This->safe);
619 if (SUCCEEDED(hr))
621 if (msg->pTool) IDirectMusicTool_Release(msg->pTool);
622 if (msg->pGraph) IDirectMusicGraph_Release(msg->pGraph);
623 if (msg->punkUser) IUnknown_Release(msg->punkUser);
624 free(message);
627 return hr;
630 static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph **graph)
632 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
634 TRACE("(%p, %p)\n", This, graph);
636 if (!graph)
637 return E_POINTER;
639 *graph = This->pToolGraph;
640 if (This->pToolGraph) {
641 IDirectMusicGraph_AddRef(*graph);
644 return *graph ? S_OK : DMUS_E_NOT_FOUND;
647 static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph *pGraph)
649 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
651 FIXME("(%p, %p): to check\n", This, pGraph);
653 if (NULL != This->pToolGraph) {
654 /* Todo clean buffers and tools before */
655 IDirectMusicGraph_Release(This->pToolGraph);
657 This->pToolGraph = pGraph;
658 if (NULL != This->pToolGraph) {
659 IDirectMusicGraph_AddRef(This->pToolGraph);
661 return S_OK;
664 static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 *iface,
665 HANDLE notification_event, REFERENCE_TIME minimum_time)
667 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
669 TRACE("(%p, %p, %I64d)\n", This, notification_event, minimum_time);
671 This->notification_event = notification_event;
672 if (minimum_time)
673 This->notification_timeout = minimum_time;
674 else if (!This->notification_timeout)
675 This->notification_timeout = 20000000; /* 2 seconds */
677 return S_OK;
680 static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 *iface,
681 DMUS_NOTIFICATION_PMSG **ret_msg)
683 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
684 struct list *entry;
686 TRACE("(%p, %p)\n", This, ret_msg);
688 if (!ret_msg) return E_POINTER;
690 EnterCriticalSection(&This->safe);
691 if ((entry = list_head(&This->notifications)))
693 struct message *message = LIST_ENTRY(entry, struct message, entry);
694 list_remove(&message->entry);
695 list_init(&message->entry);
696 *ret_msg = (DMUS_NOTIFICATION_PMSG *)&message->msg;
698 LeaveCriticalSection(&This->safe);
700 return entry ? S_OK : S_FALSE;
703 static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID type)
705 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
706 HRESULT hr = S_OK;
708 FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type));
710 if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE))
712 hr = This->notification_performance ? S_FALSE : S_OK;
713 This->notification_performance = TRUE;
715 if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT))
717 hr = This->notification_segment ? S_FALSE : S_OK;
718 This->notification_segment = TRUE;
721 return hr;
724 static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID type)
726 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
727 HRESULT hr = S_FALSE;
729 FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type));
731 if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE))
733 hr = This->notification_performance ? S_OK : S_FALSE;
734 This->notification_performance = FALSE;
736 if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT))
738 hr = This->notification_segment ? S_OK : S_FALSE;
739 This->notification_segment = FALSE;
742 return hr;
745 static void performance_update_latency_time(struct performance *This, IDirectMusicPort *port,
746 REFERENCE_TIME *ret_time)
748 IDirectMusicPerformance8 *iface = &This->IDirectMusicPerformance8_iface;
749 REFERENCE_TIME latency_time, current_time;
750 IReferenceClock *latency_clock;
751 HRESULT hr;
753 if (!ret_time) ret_time = &latency_time;
754 if (SUCCEEDED(hr = IDirectMusicPort_GetLatencyClock(port, &latency_clock)))
756 hr = IReferenceClock_GetTime(latency_clock, ret_time);
757 if (SUCCEEDED(hr)) hr = IDirectMusicPerformance8_GetTime(iface, &current_time, NULL);
758 if (SUCCEEDED(hr) && This->latency_offset < (*ret_time - current_time))
760 TRACE("Updating performance %p latency %I64d -> %I64d\n", This,
761 This->latency_offset, *ret_time - current_time);
762 This->latency_offset = *ret_time - current_time;
764 IReferenceClock_Release(latency_clock);
767 if (FAILED(hr)) ERR("Failed to update performance %p latency, hr %#lx\n", This, hr);
770 static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params)
772 IDirectMusicPort *port;
773 GUID guid;
774 unsigned int i;
775 HRESULT hr;
777 if (FAILED(hr = IDirectMusic8_GetDefaultPort(perf->dmusic, &guid)))
778 return hr;
780 if (FAILED(hr = IDirectMusic8_CreatePort(perf->dmusic, &guid, params, &port, NULL)))
781 return hr;
783 if (FAILED(hr = IDirectMusicPort_SetDirectSound(port, perf->dsound, NULL))
784 || FAILED(hr = IDirectMusicPort_Activate(port, TRUE)))
786 IDirectMusicPort_Release(port);
787 return hr;
790 for (i = 0; i < params->dwChannelGroups; i++)
791 pchannel_block_set(&perf->pchannels, i, port, i + 1, FALSE);
793 performance_update_latency_time(perf, port, NULL);
794 return S_OK;
797 static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDirectMusicPort *port)
799 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
801 FIXME("(%p, %p): semi-stub\n", This, port);
803 if (!This->dmusic) return DMUS_E_NOT_INIT;
804 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
806 if (!port) {
807 DMUS_PORTPARAMS params = {
808 .dwSize = sizeof(params),
809 .dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS,
810 .dwChannelGroups = 1
813 return perf_dmport_create(This, &params);
816 IDirectMusicPort_AddRef(port);
818 * We should remember added Ports (for example using a list)
819 * and control if Port is registered for each api who use ports
822 performance_update_latency_time(This, port, NULL);
823 return S_OK;
826 static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, IDirectMusicPort *pPort)
828 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
830 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
832 FIXME("(%p, %p): stub\n", This, pPort);
833 IDirectMusicPort_Release(pPort);
834 return S_OK;
837 static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface,
838 DWORD block_num, IDirectMusicPort *port, DWORD group)
840 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
842 FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, group);
844 if (!port) return E_POINTER;
845 if (block_num > MAXDWORD / 16) return E_INVALIDARG;
846 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
848 pchannel_block_set(&This->pchannels, block_num, port, group, FALSE);
850 return S_OK;
853 static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD pchannel,
854 IDirectMusicPort *port, DWORD group, DWORD channel)
856 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
857 struct pchannel_block *block;
859 FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, pchannel, port, group, channel);
861 if (!port) return E_POINTER;
862 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
864 block = pchannel_block_set(&This->pchannels, pchannel / 16, port, 0, TRUE);
865 if (block) {
866 block->pchannel[pchannel % 16].group = group;
867 block->pchannel[pchannel % 16].channel = channel;
870 return S_OK;
873 static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD pchannel,
874 IDirectMusicPort **port, DWORD *group, DWORD *channel)
876 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
877 struct pchannel_block *block;
878 struct wine_rb_entry *entry;
879 DWORD block_num = pchannel / 16;
880 unsigned int index = pchannel % 16;
882 TRACE("(%p)->(%ld, %p, %p, %p)\n", This, pchannel, port, group, channel);
884 entry = wine_rb_get(&This->pchannels, &block_num);
885 if (!entry)
886 return E_INVALIDARG;
887 block = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry);
889 if (port) {
890 *port = block->pchannel[index].port;
891 IDirectMusicPort_AddRef(*port);
893 if (group)
894 *group = block->pchannel[index].group;
895 if (channel)
896 *channel = block->pchannel[index].channel;
898 return S_OK;
901 static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *iface,
902 IDirectMusicInstrument *instrument, DWORD port_channel,
903 IDirectMusicDownloadedInstrument **downloaded, DMUS_NOTERANGE *note_ranges,
904 DWORD note_range_count, IDirectMusicPort **port, DWORD *group, DWORD *music_channel)
906 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
907 IDirectMusicPort *tmp_port = NULL;
908 HRESULT hr;
910 TRACE("(%p, %p, %ld, %p, %p, %ld, %p, %p, %p)\n", This, instrument, port_channel, downloaded,
911 note_ranges, note_range_count, port, group, music_channel);
913 if (!port) port = &tmp_port;
914 if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(iface, port_channel, port, group, music_channel)))
915 return hr;
917 hr = IDirectMusicPort_DownloadInstrument(*port, instrument, downloaded, note_ranges, note_range_count);
918 if (tmp_port) IDirectMusicPort_Release(tmp_port);
919 return hr;
922 static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DWORD dwFlags)
924 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
926 FIXME("(%p, %ld, %ld): stub\n", This, mtTime, dwFlags);
927 return S_OK;
930 static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
931 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
933 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
935 FIXME("(%p, %s, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pmtNext, pParam);
936 return S_OK;
939 static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
940 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam)
942 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
944 FIXME("(%p, %s, %ld, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pParam);
945 return S_OK;
948 static HRESULT WINAPI performance_GetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
949 void *pParam, DWORD dwSize)
951 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
953 TRACE("(%p, %s, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), pParam, dwSize);
955 if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload))
956 memcpy(pParam, &This->fAutoDownload, sizeof(This->fAutoDownload));
957 if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel))
958 memcpy(pParam, &This->cMasterGrooveLevel, sizeof(This->cMasterGrooveLevel));
959 if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo))
960 memcpy(pParam, &This->fMasterTempo, sizeof(This->fMasterTempo));
961 if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume))
962 memcpy(pParam, &This->lMasterVolume, sizeof(This->lMasterVolume));
964 return S_OK;
967 static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
968 void *pParam, DWORD dwSize)
970 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
972 TRACE("(%p, %s, %p, %ld)\n", This, debugstr_dmguid(rguidType), pParam, dwSize);
974 if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload)) {
975 memcpy(&This->fAutoDownload, pParam, dwSize);
976 TRACE("=> AutoDownload set to %d\n", This->fAutoDownload);
978 if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel)) {
979 memcpy(&This->cMasterGrooveLevel, pParam, dwSize);
980 TRACE("=> MasterGrooveLevel set to %i\n", This->cMasterGrooveLevel);
982 if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo)) {
983 memcpy(&This->fMasterTempo, pParam, dwSize);
984 TRACE("=> MasterTempo set to %f\n", This->fMasterTempo);
986 if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume)) {
987 memcpy(&This->lMasterVolume, pParam, dwSize);
988 TRACE("=> MasterVolume set to %li\n", This->lMasterVolume);
991 return S_OK;
994 static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *ret_time)
996 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
997 REFERENCE_TIME current_time;
998 HRESULT hr;
1000 TRACE("(%p, %p)\n", This, ret_time);
1002 if (SUCCEEDED(hr = IDirectMusicPerformance8_GetTime(iface, &current_time, NULL)))
1003 *ret_time = current_time + This->latency_offset;
1005 return hr;
1008 static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime)
1011 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1013 FIXME("(%p, %p): stub\n", This, prtTime);
1014 return S_OK;
1017 static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtAmount)
1019 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1021 FIXME("(%p, 0x%s): stub\n", This, wine_dbgstr_longlong(rtAmount));
1022 return S_OK;
1025 static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface)
1027 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1028 struct message *message, *next;
1029 HANDLE message_thread;
1030 HRESULT hr;
1032 FIXME("(%p): semi-stub\n", This);
1034 if ((message_thread = This->message_thread))
1036 EnterCriticalSection(&This->safe);
1037 This->message_thread = NULL;
1038 LeaveCriticalSection(&This->safe);
1039 WakeConditionVariable(&This->cond);
1041 WaitForSingleObject(message_thread, INFINITE);
1042 CloseHandle(message_thread);
1045 This->notification_performance = FALSE;
1046 This->notification_segment = FALSE;
1048 LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry)
1050 list_remove(&message->entry);
1051 list_init(&message->entry);
1053 /* process notifications to end any pending segment states */
1054 if (message->msg.dwType == DMUS_PMSGT_NOTIFICATION)
1055 hr = IDirectMusicTool_ProcessPMsg(&This->IDirectMusicTool_iface,
1056 (IDirectMusicPerformance *)iface, &message->msg);
1057 else
1058 hr = DMUS_S_FREE;
1060 if (hr == DMUS_S_FREE && FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg)))
1061 WARN("Failed to free message %p, hr %#lx\n", message, hr);
1064 LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->notifications, struct message, entry)
1066 list_remove(&message->entry);
1067 list_init(&message->entry);
1069 if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg)))
1070 WARN("Failed to free message %p, hr %#lx\n", message, hr);
1073 if (This->master_clock)
1075 IReferenceClock_Release(This->master_clock);
1076 This->master_clock = NULL;
1078 if (This->dsound) {
1079 IDirectSound_Release(This->dsound);
1080 This->dsound = NULL;
1082 if (This->dmusic) {
1083 IDirectMusic8_SetDirectSound(This->dmusic, NULL, NULL);
1084 IDirectMusic8_Release(This->dmusic);
1085 This->dmusic = NULL;
1087 This->audio_paths_enabled = FALSE;
1089 return S_OK;
1092 static HRESULT WINAPI performance_GetResolvedTime(IDirectMusicPerformance8 *iface,
1093 REFERENCE_TIME rtTime, REFERENCE_TIME *prtResolved, DWORD dwTimeResolveFlags)
1095 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1097 FIXME("(%p, 0x%s, %p, %ld): stub\n", This, wine_dbgstr_longlong(rtTime),
1098 prtResolved, dwTimeResolveFlags);
1099 return S_OK;
1102 static HRESULT WINAPI performance_MIDIToMusic(IDirectMusicPerformance8 *iface, BYTE bMIDIValue,
1103 DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, WORD *pwMusicValue)
1105 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1107 FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, bMIDIValue, pChord, bPlayMode, bChordLevel, pwMusicValue);
1108 return S_OK;
1111 static HRESULT WINAPI performance_MusicToMIDI(IDirectMusicPerformance8 *iface, WORD wMusicValue,
1112 DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, BYTE *pbMIDIValue)
1114 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1116 FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, wMusicValue, pChord, bPlayMode, bChordLevel, pbMIDIValue);
1117 return S_OK;
1120 static HRESULT WINAPI performance_TimeToRhythm(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime,
1121 DMUS_TIMESIGNATURE *pTimeSig, WORD *pwMeasure, BYTE *pbBeat, BYTE *pbGrid, short *pnOffset)
1123 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1125 FIXME("(%p, %ld, %p, %p, %p, %p, %p): stub\n", This, mtTime, pTimeSig, pwMeasure, pbBeat, pbGrid, pnOffset);
1126 return S_OK;
1129 static HRESULT WINAPI performance_RhythmToTime(IDirectMusicPerformance8 *iface, WORD wMeasure,
1130 BYTE bBeat, BYTE bGrid, short nOffset, DMUS_TIMESIGNATURE *pTimeSig, MUSIC_TIME *pmtTime)
1132 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1134 FIXME("(%p, %d, %d, %d, %i, %p, %p): stub\n", This, wMeasure, bBeat, bGrid, nOffset, pTimeSig, pmtTime);
1135 return S_OK;
1138 /* IDirectMusicPerformance8 Interface part follow: */
1139 static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic,
1140 IDirectSound **dsound, HWND hwnd, DWORD default_path_type, DWORD num_channels, DWORD flags,
1141 DMUS_AUDIOPARAMS *params)
1143 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1144 HRESULT hr = S_OK;
1146 TRACE("(%p, %p, %p, %p, %lx, %lu, %lx, %p)\n", This, dmusic, dsound, hwnd, default_path_type,
1147 num_channels, flags, params);
1149 if (flags) FIXME("flags parameter not used\n");
1150 if (params) FIXME("params parameter not used\n");
1152 if (FAILED(hr = IDirectMusicPerformance8_Init(iface, dmusic && *dmusic ? dmusic : NULL,
1153 dsound ? *dsound : NULL, hwnd)))
1154 return hr;
1156 This->audio_paths_enabled = TRUE;
1157 if (default_path_type)
1159 hr = IDirectMusicPerformance8_CreateStandardAudioPath(iface, default_path_type,
1160 num_channels, FALSE, &This->pDefaultPath);
1161 if (FAILED(hr))
1163 IDirectMusicPerformance_CloseDown(iface);
1164 return hr;
1168 if (dsound && !*dsound) {
1169 *dsound = This->dsound;
1170 IDirectSound_AddRef(*dsound);
1172 if (dmusic && !*dmusic) {
1173 *dmusic = (IDirectMusic *)This->dmusic;
1174 IDirectMusic_AddRef(*dmusic);
1177 return S_OK;
1180 static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *source,
1181 WCHAR *segment_name, IUnknown *transition, DWORD segment_flags, INT64 start_time,
1182 IDirectMusicSegmentState **segment_state, IUnknown *from, IUnknown *audio_path)
1184 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1185 IDirectMusicSegmentState *state;
1186 IDirectMusicSegment *segment;
1187 MUSIC_TIME length;
1188 HRESULT hr;
1190 FIXME("(%p, %p, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name),
1191 transition, segment_flags, start_time, segment_state, from, audio_path);
1193 /* NOTE: The time is in music time unless the DMUS_SEGF_REFTIME flag is set. */
1194 if (segment_flags) FIXME("flags %#lx not implemented\n", segment_flags);
1195 if (start_time) FIXME("start_time %I64d not implemented\n", start_time);
1197 if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment)))
1198 return hr;
1199 if (FAILED(hr = segment_state_create(segment, start_time, (IDirectMusicPerformance *)iface, &state)))
1201 IDirectMusicSegment_Release(segment);
1202 return hr;
1205 hr = IDirectMusicSegment_GetLength(segment, &length);
1206 if (SUCCEEDED(hr))
1207 hr = performance_send_notification_pmsg(This, start_time, This->notification_performance,
1208 GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL);
1209 if (SUCCEEDED(hr))
1210 hr = performance_send_notification_pmsg(This, start_time, This->notification_segment,
1211 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state);
1212 if (SUCCEEDED(hr))
1213 hr = performance_send_dirty_pmsg(This, start_time);
1215 if (SUCCEEDED(hr))
1216 hr = segment_state_play(state, (IDirectMusicPerformance *)iface);
1218 if (SUCCEEDED(hr))
1219 hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment,
1220 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state);
1221 if (SUCCEEDED(hr))
1222 hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment,
1223 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state);
1224 if (SUCCEEDED(hr))
1225 hr = performance_send_dirty_pmsg(This, start_time + length);
1226 if (SUCCEEDED(hr))
1227 hr = performance_send_notification_pmsg(This, start_time + length, This->notification_performance,
1228 GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL);
1230 if (SUCCEEDED(hr) && segment_state)
1232 *segment_state = state;
1233 IDirectMusicSegmentState_AddRef(state);
1236 IDirectMusicSegmentState_Release(state);
1237 IDirectMusicSegment_Release(segment);
1238 return hr;
1241 static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnknown *pObjectToStop,
1242 __int64 i64StopTime, DWORD dwFlags)
1244 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1246 FIXME("(%p, %p, 0x%s, %ld): stub\n", This, pObjectToStop,
1247 wine_dbgstr_longlong(i64StopTime), dwFlags);
1248 return S_OK;
1251 static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg, DMUS_PMSG **clone)
1253 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1254 HRESULT hr;
1256 TRACE("(%p, %p, %p)\n", This, msg, clone);
1258 if (!msg || !clone) return E_POINTER;
1259 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(iface, msg->dwSize, clone))) return hr;
1261 memcpy(*clone, msg, msg->dwSize);
1262 if (msg->pTool) IDirectMusicTool_AddRef(msg->pTool);
1263 if (msg->pGraph) IDirectMusicGraph_AddRef(msg->pGraph);
1264 if (msg->punkUser) IUnknown_AddRef(msg->punkUser);
1266 return S_OK;
1269 static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface,
1270 IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ret_iface)
1272 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1273 IDirectMusicAudioPath *pPath;
1275 FIXME("(%p, %p, %d, %p): stub\n", This, pSourceConfig, fActivate, ret_iface);
1277 if (!ret_iface) return E_POINTER;
1278 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1280 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void **)&pPath);
1281 set_audiopath_perf_pointer(pPath, iface);
1283 /** TODO */
1284 *ret_iface = pPath;
1285 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1288 static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface,
1289 DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ret_iface)
1291 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1292 IDirectMusicAudioPath *pPath;
1293 DSBUFFERDESC desc;
1294 WAVEFORMATEX format;
1295 DMUS_PORTPARAMS params = {0};
1296 IDirectSoundBuffer *buffer, *primary_buffer;
1297 HRESULT hr = S_OK;
1299 FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This, dwType, pchannel_count, fActivate, ret_iface);
1301 if (!ret_iface) return E_POINTER;
1302 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1304 *ret_iface = NULL;
1306 /* Secondary buffer description */
1307 memset(&format, 0, sizeof(format));
1308 format.wFormatTag = WAVE_FORMAT_PCM;
1309 format.nChannels = 1;
1310 format.nSamplesPerSec = 44000;
1311 format.nAvgBytesPerSec = 44000*2;
1312 format.nBlockAlign = 2;
1313 format.wBitsPerSample = 16;
1314 format.cbSize = 0;
1316 memset(&desc, 0, sizeof(desc));
1317 desc.dwSize = sizeof(desc);
1318 desc.dwFlags = DSBCAPS_CTRLFX | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
1319 desc.dwBufferBytes = DSBSIZE_MIN;
1320 desc.dwReserved = 0;
1321 desc.lpwfxFormat = &format;
1322 desc.guid3DAlgorithm = GUID_NULL;
1324 switch(dwType) {
1325 case DMUS_APATH_DYNAMIC_3D:
1326 desc.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE;
1327 break;
1328 case DMUS_APATH_DYNAMIC_MONO:
1329 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1330 break;
1331 case DMUS_APATH_SHARED_STEREOPLUSREVERB:
1332 /* normally we have to create 2 buffers (one for music other for reverb)
1333 * in this case. See msdn
1335 case DMUS_APATH_DYNAMIC_STEREO:
1336 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1337 format.nChannels = 2;
1338 format.nBlockAlign *= 2;
1339 format.nAvgBytesPerSec *=2;
1340 break;
1341 default:
1342 return E_INVALIDARG;
1345 /* Create a port */
1346 params.dwSize = sizeof(params);
1347 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS | DMUS_PORTPARAMS_AUDIOCHANNELS;
1348 params.dwChannelGroups = (pchannel_count + 15) / 16;
1349 params.dwAudioChannels = format.nChannels;
1350 if (FAILED(hr = perf_dmport_create(This, &params)))
1351 return hr;
1353 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &buffer, NULL);
1354 if (FAILED(hr))
1355 return DSERR_BUFFERLOST;
1357 /* Update description for creating primary buffer */
1358 desc.dwFlags |= DSBCAPS_PRIMARYBUFFER;
1359 desc.dwFlags &= ~DSBCAPS_CTRLFX;
1360 desc.dwBufferBytes = 0;
1361 desc.lpwfxFormat = NULL;
1363 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &primary_buffer, NULL);
1364 if (FAILED(hr)) {
1365 IDirectSoundBuffer_Release(buffer);
1366 return DSERR_BUFFERLOST;
1369 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void**)&pPath);
1370 set_audiopath_perf_pointer(pPath, iface);
1371 set_audiopath_dsound_buffer(pPath, buffer);
1372 set_audiopath_primary_dsound_buffer(pPath, primary_buffer);
1374 *ret_iface = pPath;
1375 TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface);
1376 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1379 static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *audio_path)
1381 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1383 FIXME("(%p, %p): semi-stub\n", This, audio_path);
1385 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1387 if (This->pDefaultPath) IDirectMusicAudioPath_Release(This->pDefaultPath);
1388 if ((This->pDefaultPath = audio_path))
1390 IDirectMusicAudioPath_AddRef(This->pDefaultPath);
1391 set_audiopath_perf_pointer(This->pDefaultPath, iface);
1394 return S_OK;
1397 static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface,
1398 IDirectMusicAudioPath **ret_iface)
1400 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1402 FIXME("(%p, %p): semi-stub (%p)\n", This, ret_iface, This->pDefaultPath);
1404 if (!ret_iface) return E_POINTER;
1405 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1407 if ((*ret_iface = This->pDefaultPath)) IDirectMusicAudioPath_AddRef(*ret_iface);
1409 return S_OK;
1412 static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID,
1413 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
1415 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1417 FIXME("(%p, %s, %ld, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwTrackID, dwGroupBits, dwIndex, mtTime, pmtNext, pParam);
1419 return S_OK;
1422 static const IDirectMusicPerformance8Vtbl performance_vtbl =
1424 performance_QueryInterface,
1425 performance_AddRef,
1426 performance_Release,
1427 performance_Init,
1428 performance_PlaySegment,
1429 performance_Stop,
1430 performance_GetSegmentState,
1431 performance_SetPrepareTime,
1432 performance_GetPrepareTime,
1433 performance_SetBumperLength,
1434 performance_GetBumperLength,
1435 performance_SendPMsg,
1436 performance_MusicToReferenceTime,
1437 performance_ReferenceToMusicTime,
1438 performance_IsPlaying,
1439 performance_GetTime,
1440 performance_AllocPMsg,
1441 performance_FreePMsg,
1442 performance_GetGraph,
1443 performance_SetGraph,
1444 performance_SetNotificationHandle,
1445 performance_GetNotificationPMsg,
1446 performance_AddNotificationType,
1447 performance_RemoveNotificationType,
1448 performance_AddPort,
1449 performance_RemovePort,
1450 performance_AssignPChannelBlock,
1451 performance_AssignPChannel,
1452 performance_PChannelInfo,
1453 performance_DownloadInstrument,
1454 performance_Invalidate,
1455 performance_GetParam,
1456 performance_SetParam,
1457 performance_GetGlobalParam,
1458 performance_SetGlobalParam,
1459 performance_GetLatencyTime,
1460 performance_GetQueueTime,
1461 performance_AdjustTime,
1462 performance_CloseDown,
1463 performance_GetResolvedTime,
1464 performance_MIDIToMusic,
1465 performance_MusicToMIDI,
1466 performance_TimeToRhythm,
1467 performance_RhythmToTime,
1468 performance_InitAudio,
1469 performance_PlaySegmentEx,
1470 performance_StopEx,
1471 performance_ClonePMsg,
1472 performance_CreateAudioPath,
1473 performance_CreateStandardAudioPath,
1474 performance_SetDefaultAudioPath,
1475 performance_GetDefaultAudioPath,
1476 performance_GetParamEx,
1479 static inline struct performance *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface)
1481 return CONTAINING_RECORD(iface, struct performance, IDirectMusicGraph_iface);
1484 static HRESULT WINAPI performance_graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface)
1486 struct performance *This = impl_from_IDirectMusicGraph(iface);
1487 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
1490 static ULONG WINAPI performance_graph_AddRef(IDirectMusicGraph *iface)
1492 struct performance *This = impl_from_IDirectMusicGraph(iface);
1493 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
1496 static ULONG WINAPI performance_graph_Release(IDirectMusicGraph *iface)
1498 struct performance *This = impl_from_IDirectMusicGraph(iface);
1499 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
1502 static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg)
1504 struct performance *This = impl_from_IDirectMusicGraph(iface);
1505 HRESULT hr;
1507 TRACE("(%p, %p)\n", This, msg);
1509 if (!msg) return E_POINTER;
1511 /* FIXME: Implement segment and audio path graphs support */
1512 if (!This->pToolGraph) hr = DMUS_S_LAST_TOOL;
1513 else if (FAILED(hr = IDirectMusicGraph_StampPMsg(This->pToolGraph, msg))) return hr;
1515 if (msg->pGraph)
1517 IDirectMusicTool_Release(msg->pGraph);
1518 msg->pGraph = NULL;
1521 if (hr == DMUS_S_LAST_TOOL)
1523 const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
1524 msg->dwFlags &= ~delivery_flags;
1525 msg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
1527 if (msg->pTool) IDirectMusicTool_Release(msg->pTool);
1528 msg->pTool = &This->IDirectMusicTool_iface;
1529 IDirectMusicTool_AddRef(msg->pTool);
1530 return S_OK;
1533 if (SUCCEEDED(hr))
1535 msg->pGraph = &This->IDirectMusicGraph_iface;
1536 IDirectMusicTool_AddRef(msg->pGraph);
1539 return hr;
1542 static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool,
1543 DWORD *channels, DWORD channels_count, LONG index)
1545 struct performance *This = impl_from_IDirectMusicGraph(iface);
1546 TRACE("(%p, %p, %p, %lu, %ld)\n", This, tool, channels, channels_count, index);
1547 return E_NOTIMPL;
1550 static HRESULT WINAPI performance_graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **tool)
1552 struct performance *This = impl_from_IDirectMusicGraph(iface);
1553 TRACE("(%p, %lu, %p)\n", This, index, tool);
1554 return E_NOTIMPL;
1557 static HRESULT WINAPI performance_graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool)
1559 struct performance *This = impl_from_IDirectMusicGraph(iface);
1560 TRACE("(%p, %p)\n", This, tool);
1561 return E_NOTIMPL;
1564 static const IDirectMusicGraphVtbl performance_graph_vtbl =
1566 performance_graph_QueryInterface,
1567 performance_graph_AddRef,
1568 performance_graph_Release,
1569 performance_graph_StampPMsg,
1570 performance_graph_InsertTool,
1571 performance_graph_GetTool,
1572 performance_graph_RemoveTool,
1575 static inline struct performance *impl_from_IDirectMusicTool(IDirectMusicTool *iface)
1577 return CONTAINING_RECORD(iface, struct performance, IDirectMusicTool_iface);
1580 static HRESULT WINAPI performance_tool_QueryInterface(IDirectMusicTool *iface, REFIID riid, void **ret_iface)
1582 struct performance *This = impl_from_IDirectMusicTool(iface);
1583 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
1586 static ULONG WINAPI performance_tool_AddRef(IDirectMusicTool *iface)
1588 struct performance *This = impl_from_IDirectMusicTool(iface);
1589 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
1592 static ULONG WINAPI performance_tool_Release(IDirectMusicTool *iface)
1594 struct performance *This = impl_from_IDirectMusicTool(iface);
1595 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
1598 static HRESULT WINAPI performance_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph)
1600 struct performance *This = impl_from_IDirectMusicTool(iface);
1601 TRACE("(%p, %p)\n", This, graph);
1602 return E_NOTIMPL;
1605 static HRESULT WINAPI performance_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type)
1607 struct performance *This = impl_from_IDirectMusicTool(iface);
1608 TRACE("(%p, %p)\n", This, type);
1609 *type = DMUS_PMSGF_TOOL_IMMEDIATE;
1610 return S_OK;
1613 static HRESULT WINAPI performance_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size)
1615 struct performance *This = impl_from_IDirectMusicTool(iface);
1616 TRACE("(%p, %p)\n", This, size);
1617 *size = 0;
1618 return S_OK;
1621 static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size)
1623 struct performance *This = impl_from_IDirectMusicTool(iface);
1624 TRACE("(%p, %p, %lu)\n", This, types, size);
1625 return E_NOTIMPL;
1628 static HRESULT performance_send_midi_pmsg(struct performance *This, DMUS_PMSG *msg, UINT flags,
1629 BYTE status, BYTE byte1, BYTE byte2)
1631 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
1632 DMUS_MIDI_PMSG *midi;
1633 HRESULT hr;
1635 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*midi),
1636 (DMUS_PMSG **)&midi)))
1637 return hr;
1639 if (flags & DMUS_PMSGF_REFTIME) midi->rtTime = msg->rtTime;
1640 if (flags & DMUS_PMSGF_MUSICTIME) midi->mtTime = msg->mtTime;
1641 midi->dwFlags = flags;
1642 midi->dwPChannel = msg->dwPChannel;
1643 midi->dwVirtualTrackID = msg->dwVirtualTrackID;
1644 midi->dwVoiceID = msg->dwVoiceID;
1645 midi->dwGroupID = msg->dwGroupID;
1646 midi->dwType = DMUS_PMSGT_MIDI;
1647 midi->bStatus = status;
1648 midi->bByte1 = byte1;
1649 midi->bByte2 = byte2;
1651 if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)midi)))
1652 IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)midi);
1654 return hr;
1657 static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface,
1658 IDirectMusicPerformance *performance, DMUS_PMSG *msg)
1660 struct performance *This = impl_from_IDirectMusicTool(iface);
1661 struct message *message = message_from_DMUS_PMSG(msg);
1662 HRESULT hr;
1664 TRACE("(%p, %p, %p)\n", This, performance, msg);
1666 switch (msg->dwType)
1668 case DMUS_PMSGT_MIDI:
1670 static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD);
1671 DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size};
1672 DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg;
1673 REFERENCE_TIME latency_time;
1674 IDirectMusicBuffer *buffer;
1675 IDirectMusicPort *port;
1676 DWORD group, channel;
1677 UINT value = 0;
1679 if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(performance, msg->dwPChannel,
1680 &port, &group, &channel)))
1682 WARN("Failed to get message port, hr %#lx\n", hr);
1683 return DMUS_S_FREE;
1685 performance_update_latency_time(This, port, &latency_time);
1687 value |= channel;
1688 value |= (UINT)midi->bStatus;
1689 value |= (UINT)midi->bByte1 << 8;
1690 value |= (UINT)midi->bByte2 << 16;
1692 if (SUCCEEDED(hr = IDirectMusic_CreateMusicBuffer(This->dmusic, &desc, &buffer, NULL)))
1694 if (msg->rtTime == -1) msg->rtTime = latency_time;
1695 hr = IDirectMusicBuffer_PackStructured(buffer, msg->rtTime, group, value);
1696 if (SUCCEEDED(hr)) hr = IDirectMusicPort_PlayBuffer(port, buffer);
1697 IDirectMusicBuffer_Release(buffer);
1700 IDirectMusicPort_Release(port);
1701 break;
1704 case DMUS_PMSGT_NOTE:
1706 DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg;
1708 msg->mtTime += note->nOffset;
1709 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1710 MIDI_NOTE_ON, note->bMidiValue, note->bVelocity)))
1711 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1713 msg->mtTime += note->mtDuration;
1714 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE,
1715 MIDI_NOTE_OFF, note->bMidiValue, 0)))
1716 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1718 break;
1721 case DMUS_PMSGT_CURVE:
1723 DMUS_CURVE_PMSG *curve = (DMUS_CURVE_PMSG *)msg;
1725 msg->mtTime += curve->nOffset;
1726 switch (curve->dwType)
1728 case DMUS_CURVET_CCCURVE:
1729 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1730 MIDI_CONTROL_CHANGE, curve->bCCData, curve->nStartValue)))
1731 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1732 break;
1733 case DMUS_CURVET_RPNCURVE:
1734 case DMUS_CURVET_NRPNCURVE:
1735 FIXME("Unhandled curve type %#lx\n", curve->dwType);
1736 break;
1739 break;
1742 case DMUS_PMSGT_PATCH:
1744 DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg;
1746 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1747 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_MSB, patch->byMSB)))
1748 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1750 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1751 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_LSB, patch->byLSB)))
1752 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1754 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1755 MIDI_PROGRAM_CHANGE, patch->byInstrument, 0)))
1756 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1758 break;
1761 case DMUS_PMSGT_NOTIFICATION:
1763 DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg;
1764 struct message *previous;
1765 BOOL enabled = FALSE;
1767 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)
1768 && notif->dwNotificationOption == DMUS_NOTIFICATION_SEGEND)
1770 if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)notif->punkUser, performance)))
1771 WARN("Failed to end segment state %p, hr %#lx\n", notif->punkUser, hr);
1774 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE))
1775 enabled = This->notification_performance;
1776 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_SEGMENT))
1777 enabled = This->notification_segment;
1778 if (!enabled) return DMUS_S_FREE;
1780 list_add_tail(&This->notifications, &message->entry);
1782 /* discard old notification messages */
1785 previous = LIST_ENTRY(list_head(&This->notifications), struct message, entry);
1786 if (This->notification_timeout <= 0) break; /* negative values may be used to keep everything */
1787 if (message->msg.rtTime - previous->msg.rtTime <= This->notification_timeout) break;
1788 list_remove(&previous->entry);
1789 list_init(&previous->entry);
1790 } while (SUCCEEDED(hr = IDirectMusicPerformance_FreePMsg(performance, &previous->msg)));
1792 SetEvent(This->notification_event);
1793 return S_OK;
1796 case DMUS_PMSGT_WAVE:
1797 if (FAILED(hr = IDirectSoundBuffer_Play((IDirectSoundBuffer *)msg->punkUser, 0, 0, 0)))
1798 WARN("Failed to play wave buffer, hr %#lx\n", hr);
1799 break;
1801 default:
1802 FIXME("Unhandled message type %#lx\n", msg->dwType);
1803 break;
1806 return DMUS_S_FREE;
1809 static HRESULT WINAPI performance_tool_Flush(IDirectMusicTool *iface,
1810 IDirectMusicPerformance *performance, DMUS_PMSG *msg, REFERENCE_TIME time)
1812 struct performance *This = impl_from_IDirectMusicTool(iface);
1813 FIXME("(%p, %p, %p, %I64d): stub\n", This, performance, msg, time);
1814 return E_NOTIMPL;
1817 static const IDirectMusicToolVtbl performance_tool_vtbl =
1819 performance_tool_QueryInterface,
1820 performance_tool_AddRef,
1821 performance_tool_Release,
1822 performance_tool_Init,
1823 performance_tool_GetMsgDeliveryType,
1824 performance_tool_GetMediaTypeArraySize,
1825 performance_tool_GetMediaTypes,
1826 performance_tool_ProcessPMsg,
1827 performance_tool_Flush,
1830 /* for ClassFactory */
1831 HRESULT create_dmperformance(REFIID iid, void **ret_iface)
1833 struct performance *obj;
1834 HRESULT hr;
1836 TRACE("(%s, %p)\n", debugstr_guid(iid), ret_iface);
1838 *ret_iface = NULL;
1839 if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
1840 obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl;
1841 obj->IDirectMusicGraph_iface.lpVtbl = &performance_graph_vtbl;
1842 obj->IDirectMusicTool_iface.lpVtbl = &performance_tool_vtbl;
1843 obj->ref = 1;
1845 obj->pDefaultPath = NULL;
1846 InitializeCriticalSection(&obj->safe);
1847 obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe");
1848 wine_rb_init(&obj->pchannels, pchannel_block_compare);
1850 list_init(&obj->messages);
1851 list_init(&obj->notifications);
1853 obj->latency_offset = 50;
1854 obj->dwBumperLength = 50; /* 50 ms default */
1855 obj->dwPrepareTime = 1000; /* 1000 ms default */
1857 hr = IDirectMusicPerformance8_QueryInterface(&obj->IDirectMusicPerformance8_iface, iid, ret_iface);
1858 IDirectMusicPerformance_Release(&obj->IDirectMusicPerformance8_iface);
1859 return hr;
1862 static inline struct performance *unsafe_impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface)
1864 if (iface->lpVtbl != &performance_vtbl) return NULL;
1865 return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface);
1868 HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound)
1870 struct performance *This = unsafe_impl_from_IDirectMusicPerformance8(iface);
1871 if (!This || !(*dsound = This->dsound)) return E_FAIL;
1872 IDirectSound_AddRef(*dsound);
1873 return S_OK;