dmime: Requeue the note message instead of directly queueing MIDI note-off.
[wine.git] / dlls / dmime / performance.c
blob1d1da250cc309787dd4614485e446bbd13ab70f6
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"
24 #include <math.h>
26 WINE_DEFAULT_DEBUG_CHANNEL(dmime);
28 enum dmus_internal_message_type
30 DMUS_PMSGT_INTERNAL_FIRST = 0x10,
31 DMUS_PMSGT_INTERNAL_SEGMENT_END = DMUS_PMSGT_INTERNAL_FIRST,
32 DMUS_PMSGT_INTERNAL_SEGMENT_TICK,
35 struct channel
37 DWORD midi_group;
38 DWORD midi_channel;
39 IDirectMusicPort *port;
42 struct channel_block
44 DWORD block_num; /* Block 0 is PChannels 0-15, Block 1 is PChannels 16-31, etc */
45 struct channel channels[16];
46 struct wine_rb_entry entry;
49 struct performance
51 IDirectMusicPerformance8 IDirectMusicPerformance8_iface;
52 IDirectMusicGraph IDirectMusicGraph_iface;
53 IDirectMusicTool IDirectMusicTool_iface;
54 LONG ref;
55 IDirectMusic *dmusic;
56 IDirectSound *dsound;
57 IDirectMusicGraph *pToolGraph;
58 BOOL fAutoDownload;
59 char cMasterGrooveLevel;
60 float fMasterTempo;
61 long lMasterVolume;
62 /* performance channels */
63 struct wine_rb_tree channel_blocks;
65 BOOL audio_paths_enabled;
66 IDirectMusicAudioPath *pDefaultPath;
67 REFERENCE_TIME latency_offset;
68 DWORD dwBumperLength;
69 DWORD dwPrepareTime;
71 HANDLE message_thread;
72 CRITICAL_SECTION safe;
73 CONDITION_VARIABLE cond;
75 IReferenceClock *master_clock;
76 REFERENCE_TIME init_time;
77 struct list messages;
79 struct list notifications;
80 REFERENCE_TIME notification_timeout;
81 HANDLE notification_event;
82 BOOL notification_performance;
83 BOOL notification_segment;
85 IDirectMusicSegment *primary_segment;
86 IDirectMusicSegment *control_segment;
89 struct message
91 struct list entry;
92 DMUS_PMSG msg;
95 static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg)
97 return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL;
100 static struct message *performance_get_message(struct performance *This, DWORD *timeout)
102 static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
103 IDirectMusicPerformance *performance = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface;
104 REFERENCE_TIME latency, offset = 0;
105 struct message *message;
106 struct list *ptr;
108 if (!(ptr = list_head(&This->messages)))
109 return NULL;
110 message = LIST_ENTRY(ptr, struct message, entry);
112 if (FAILED(IDirectMusicPerformance_GetLatencyTime(performance, &latency)))
113 return NULL;
115 switch (message->msg.dwFlags & delivery_flags)
117 default:
118 WARN("No delivery flag found for message %p\n", &message->msg);
119 break;
120 case DMUS_PMSGF_TOOL_QUEUE:
121 offset = This->dwBumperLength * 10000;
122 /* fallthrough */
123 case DMUS_PMSGF_TOOL_ATTIME:
124 if (message->msg.rtTime >= offset && message->msg.rtTime - offset >= latency)
126 *timeout = (message->msg.rtTime - offset - latency) / 10000;
127 return NULL;
129 break;
132 return message;
135 static HRESULT performance_process_message(struct performance *This, DMUS_PMSG *msg)
137 IDirectMusicPerformance *performance = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface;
138 IDirectMusicTool *tool;
139 HRESULT hr;
141 if (!(tool = msg->pTool)) tool = &This->IDirectMusicTool_iface;
143 hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg);
145 if (hr == DMUS_S_FREE) hr = IDirectMusicPerformance_FreePMsg(performance, msg);
146 if (FAILED(hr)) WARN("Failed to process message, hr %#lx\n", hr);
147 return hr;
150 static HRESULT performance_queue_message(struct performance *This, struct message *message)
152 static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
153 struct message *prev;
154 HRESULT hr;
156 while ((message->msg.dwFlags & delivery_flags) == DMUS_PMSGF_TOOL_IMMEDIATE)
158 hr = performance_process_message(This, &message->msg);
159 if (hr != DMUS_S_REQUEUE)
160 return hr;
163 LIST_FOR_EACH_ENTRY_REV(prev, &This->messages, struct message, entry)
165 if (&prev->entry == &This->messages) break;
166 if (prev->msg.rtTime <= message->msg.rtTime) break;
169 list_add_after(&prev->entry, &message->entry);
171 return S_OK;
174 static DWORD WINAPI message_thread_proc(void *args)
176 struct performance *This = args;
177 HRESULT hr;
179 TRACE("performance %p message thread\n", This);
180 SetThreadDescription(GetCurrentThread(), L"wine_dmime_message");
182 EnterCriticalSection(&This->safe);
184 while (This->message_thread)
186 DWORD timeout = INFINITE;
187 struct message *message;
189 if (!(message = performance_get_message(This, &timeout)))
191 SleepConditionVariableCS(&This->cond, &This->safe, timeout);
192 continue;
195 list_remove(&message->entry);
196 list_init(&message->entry);
198 hr = performance_process_message(This, &message->msg);
199 if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message);
202 LeaveCriticalSection(&This->safe);
204 TRACE("(%p): Exiting\n", This);
205 return 0;
208 static HRESULT performance_send_pmsg(struct performance *This, MUSIC_TIME music_time, DWORD flags,
209 DWORD type, IUnknown *object)
211 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
212 IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface;
213 DMUS_PMSG *msg;
214 HRESULT hr;
216 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), &msg)))
217 return hr;
219 msg->mtTime = music_time;
220 msg->dwFlags = DMUS_PMSGF_MUSICTIME | flags;
221 msg->dwType = type;
222 if ((msg->punkUser = object)) IUnknown_AddRef(object);
224 if ((type < DMUS_PMSGT_INTERNAL_FIRST && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, msg)))
225 || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg)))
226 IDirectMusicPerformance8_FreePMsg(performance, msg);
228 return hr;
231 static HRESULT performance_send_notification_pmsg(struct performance *This, MUSIC_TIME music_time, BOOL stamp,
232 GUID type, DWORD option, IUnknown *object)
234 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
235 IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface;
236 DMUS_NOTIFICATION_PMSG *msg;
237 HRESULT hr;
239 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg)))
240 return hr;
242 msg->mtTime = music_time;
243 msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE;
244 msg->dwType = DMUS_PMSGT_NOTIFICATION;
245 if ((msg->punkUser = object)) IUnknown_AddRef(object);
246 msg->guidNotificationType = type;
247 msg->dwNotificationOption = option;
249 /* only stamp the message if notifications are enabled, otherwise send them directly to the output tool */
250 if ((stamp && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)))
251 || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)msg)))
252 IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)msg);
254 return hr;
257 static int channel_block_compare(const void *key, const struct wine_rb_entry *entry)
259 const struct channel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct channel_block, entry);
260 return *(DWORD *)key - b->block_num;
263 static void channel_block_free(struct wine_rb_entry *entry, void *context)
265 struct channel_block *block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry);
266 UINT i;
268 for (i = 0; i < ARRAY_SIZE(block->channels); i++)
270 struct channel *channel = block->channels + i;
271 if (channel->port) IDirectMusicPort_Release(channel->port);
274 free(block);
277 static struct channel *performance_get_channel(struct performance *This, DWORD channel_num)
279 DWORD block_num = channel_num / 16;
280 struct wine_rb_entry *entry;
281 if (!(entry = wine_rb_get(&This->channel_blocks, &block_num))) return NULL;
282 return WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry)->channels + channel_num % 16;
285 static HRESULT channel_block_init(struct performance *This, DWORD block_num,
286 IDirectMusicPort *port, DWORD midi_group)
288 struct channel_block *block;
289 struct wine_rb_entry *entry;
290 UINT i;
292 if ((entry = wine_rb_get(&This->channel_blocks, &block_num)))
293 block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry);
294 else
296 if (!(block = calloc(1, sizeof(*block)))) return E_OUTOFMEMORY;
297 block->block_num = block_num;
298 wine_rb_put(&This->channel_blocks, &block_num, &block->entry);
301 for (i = 0; i < ARRAY_SIZE(block->channels); ++i)
303 struct channel *channel = block->channels + i;
304 channel->midi_group = midi_group;
305 channel->midi_channel = i;
306 if (channel->port) IDirectMusicPort_Release(channel->port);
307 if ((channel->port = port)) IDirectMusicPort_AddRef(channel->port);
310 return S_OK;
313 static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface)
315 return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface);
318 HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time,
319 IDirectMusicSegmentState *state)
321 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
322 HRESULT hr;
324 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance,
325 GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL)))
326 return hr;
327 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment,
328 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state)))
329 return hr;
330 if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE,
331 DMUS_PMSGT_DIRTY, NULL)))
332 return hr;
334 return S_OK;
337 HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time,
338 IDirectMusicSegmentState *state)
340 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
341 REFERENCE_TIME time;
342 HRESULT hr;
344 if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface, music_time, &time)))
345 return hr;
346 if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, time + 2000000, &music_time)))
347 return hr;
348 if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_QUEUE,
349 DMUS_PMSGT_INTERNAL_SEGMENT_TICK, (IUnknown *)state)))
350 return hr;
352 return S_OK;
355 HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time,
356 IDirectMusicSegmentState *state, BOOL abort)
358 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
359 HRESULT hr;
361 if (!abort)
363 if (FAILED(hr = performance_send_notification_pmsg(This, music_time - 1450, This->notification_segment,
364 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state)))
365 return hr;
366 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment,
367 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state)))
368 return hr;
371 if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE,
372 DMUS_PMSGT_DIRTY, NULL)))
373 return hr;
374 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance,
375 GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL)))
376 return hr;
377 if (FAILED(hr = performance_send_pmsg(This, music_time, abort ? DMUS_PMSGF_TOOL_IMMEDIATE : DMUS_PMSGF_TOOL_ATTIME,
378 DMUS_PMSGT_INTERNAL_SEGMENT_END, (IUnknown *)state)))
379 return hr;
381 return S_OK;
384 static void performance_set_primary_segment(struct performance *This, IDirectMusicSegment *segment)
386 if (This->primary_segment) IDirectMusicSegment_Release(This->primary_segment);
387 if ((This->primary_segment = segment)) IDirectMusicSegment_AddRef(This->primary_segment);
390 static void performance_set_control_segment(struct performance *This, IDirectMusicSegment *segment)
392 if (This->control_segment) IDirectMusicSegment_Release(This->control_segment);
393 if ((This->control_segment = segment)) IDirectMusicSegment_AddRef(This->control_segment);
396 /* IDirectMusicPerformance8 IUnknown part: */
397 static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface)
399 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
401 TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface);
403 if (IsEqualGUID(riid, &IID_IUnknown)
404 || IsEqualGUID(riid, &IID_IDirectMusicPerformance)
405 || IsEqualGUID(riid, &IID_IDirectMusicPerformance2)
406 || IsEqualGUID(riid, &IID_IDirectMusicPerformance8))
408 *ret_iface = iface;
409 IUnknown_AddRef(iface);
410 return S_OK;
413 if (IsEqualGUID(riid, &IID_IDirectMusicGraph))
415 *ret_iface = &This->IDirectMusicGraph_iface;
416 IDirectMusicGraph_AddRef(&This->IDirectMusicGraph_iface);
417 return S_OK;
420 if (IsEqualGUID(riid, &IID_IDirectMusicTool))
422 *ret_iface = &This->IDirectMusicTool_iface;
423 IDirectMusicTool_AddRef(&This->IDirectMusicTool_iface);
424 return S_OK;
427 *ret_iface = NULL;
428 WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface);
429 return E_NOINTERFACE;
432 static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface)
434 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
435 ULONG ref = InterlockedIncrement(&This->ref);
437 TRACE("(%p): ref=%ld\n", This, ref);
439 return ref;
442 static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface)
444 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
445 ULONG ref = InterlockedDecrement(&This->ref);
447 TRACE("(%p): ref=%ld\n", This, ref);
449 if (ref == 0) {
450 wine_rb_destroy(&This->channel_blocks, channel_block_free, NULL);
451 This->safe.DebugInfo->Spare[0] = 0;
452 DeleteCriticalSection(&This->safe);
453 free(This);
456 return ref;
459 static HRESULT performance_init_dsound(struct performance *This, HWND hwnd)
461 IDirectSound *dsound;
462 HRESULT hr;
464 if (FAILED(hr = DirectSoundCreate(NULL, &dsound, NULL))) return hr;
466 if (!hwnd) hwnd = GetForegroundWindow();
467 hr = IDirectSound_SetCooperativeLevel(dsound, hwnd, DSSCL_PRIORITY);
469 if (SUCCEEDED(hr)) This->dsound = dsound;
470 else IDirectSound_Release(dsound);
472 return hr;
475 static HRESULT performance_init_dmusic(struct performance *This, IDirectSound *dsound)
477 IDirectMusic *dmusic;
478 HRESULT hr;
480 if (FAILED(hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER,
481 &IID_IDirectMusic8, (void **)&dmusic)))
482 return hr;
484 hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL);
486 if (SUCCEEDED(hr)) This->dmusic = dmusic;
487 else IDirectSound_Release(dmusic);
489 return hr;
492 static HRESULT performance_send_midi_pmsg(struct performance *This, DMUS_PMSG *msg, UINT flags,
493 BYTE status, BYTE byte1, BYTE byte2)
495 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
496 DMUS_MIDI_PMSG *midi;
497 HRESULT hr;
499 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*midi),
500 (DMUS_PMSG **)&midi)))
501 return hr;
503 if (flags & DMUS_PMSGF_REFTIME) midi->rtTime = msg->rtTime;
504 if (flags & DMUS_PMSGF_MUSICTIME) midi->mtTime = msg->mtTime;
505 midi->dwFlags = flags;
506 midi->dwPChannel = msg->dwPChannel;
507 midi->dwVirtualTrackID = msg->dwVirtualTrackID;
508 midi->dwVoiceID = msg->dwVoiceID;
509 midi->dwGroupID = msg->dwGroupID;
510 midi->dwType = DMUS_PMSGT_MIDI;
511 midi->bStatus = status;
512 midi->bByte1 = byte1;
513 midi->bByte2 = byte2;
515 if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)midi)))
516 IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)midi);
518 return hr;
521 static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic,
522 IDirectSound *dsound, HWND hwnd)
524 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
525 HRESULT hr;
527 TRACE("(%p, %p, %p, %p)\n", iface, dmusic, dsound, hwnd);
529 if (This->dmusic) return DMUS_E_ALREADY_INITED;
531 if ((This->dsound = dsound)) IDirectMusic8_AddRef(This->dsound);
532 else if (FAILED(hr = performance_init_dsound(This, hwnd))) return hr;
534 if (dmusic && (This->dmusic = *dmusic)) IDirectMusic_AddRef(This->dmusic);
535 else if (FAILED(hr = performance_init_dmusic(This, This->dsound)))
537 IDirectMusicPerformance_CloseDown(iface);
538 return hr;
541 if (FAILED(hr = IDirectMusic_GetMasterClock(This->dmusic, NULL, &This->master_clock))
542 || FAILED(hr = IDirectMusicPerformance8_GetTime(iface, &This->init_time, NULL)))
544 IDirectMusicPerformance_CloseDown(iface);
545 return hr;
548 if (!(This->message_thread = CreateThread(NULL, 0, message_thread_proc, This, 0, NULL)))
550 ERR("Failed to start performance message thread, error %lu\n", GetLastError());
551 IDirectMusicPerformance_CloseDown(iface);
552 return HRESULT_FROM_WIN32(GetLastError());
555 if (dmusic && !*dmusic)
557 *dmusic = This->dmusic;
558 IDirectMusic_AddRef(*dmusic);
560 return S_OK;
563 static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment,
564 DWORD segment_flags, INT64 start_time, IDirectMusicSegmentState **ret_state)
566 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
568 TRACE("(%p, %p, %ld, %I64d, %p)\n", This, segment, segment_flags, start_time, ret_state);
570 return IDirectMusicPerformance8_PlaySegmentEx(iface, (IUnknown *)segment, NULL, NULL,
571 segment_flags, start_time, ret_state, NULL, NULL);
574 struct state_entry
576 struct list entry;
577 IDirectMusicSegmentState *state;
580 static void state_entry_destroy(struct state_entry *entry)
582 list_remove(&entry->entry);
583 IDirectMusicSegmentState_Release(entry->state);
584 free(entry);
587 static void enum_segment_states(struct performance *This, IDirectMusicSegment *segment, struct list *list)
589 struct state_entry *entry;
590 struct message *message;
592 LIST_FOR_EACH_ENTRY(message, &This->messages, struct message, entry)
594 IDirectMusicSegmentState *message_state;
596 if (message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_TICK
597 && message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_END)
598 continue;
600 message_state = (IDirectMusicSegmentState *)message->msg.punkUser;
601 if (segment && !segment_state_has_segment(message_state, segment)) continue;
603 if (!(entry = malloc(sizeof(*entry)))) return;
604 entry->state = message_state;
605 IDirectMusicSegmentState_AddRef(entry->state);
606 list_add_tail(list, &entry->entry);
610 static BOOL message_needs_flushing(struct message *message, IDirectMusicSegmentState *state)
612 if (!state) return TRUE;
614 switch (message->msg.dwType)
616 case DMUS_PMSGT_MIDI:
617 case DMUS_PMSGT_NOTE:
618 case DMUS_PMSGT_CURVE:
619 case DMUS_PMSGT_PATCH:
620 case DMUS_PMSGT_WAVE:
621 if (!segment_state_has_track(state, message->msg.dwVirtualTrackID)) return FALSE;
622 break;
624 case DMUS_PMSGT_NOTIFICATION:
626 DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)&message->msg;
627 if (!IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) return FALSE;
628 if ((IDirectMusicSegmentState *)message->msg.punkUser != state) return FALSE;
629 break;
632 case DMUS_PMSGT_INTERNAL_SEGMENT_TICK:
633 case DMUS_PMSGT_INTERNAL_SEGMENT_END:
634 if ((IDirectMusicSegmentState *)message->msg.punkUser != state) return FALSE;
635 break;
637 default:
638 FIXME("Unhandled message type %#lx\n", message->msg.dwType);
639 break;
642 return TRUE;
645 static void performance_flush_messages(struct performance *This, IDirectMusicSegmentState *state)
647 IDirectMusicPerformance *iface = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface;
648 struct message *message, *next;
649 HRESULT hr;
651 LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry)
653 if (!message_needs_flushing(message, state)) continue;
655 list_remove(&message->entry);
656 list_init(&message->entry);
658 if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg)))
659 ERR("Failed to free message %p, hr %#lx\n", message, hr);
662 LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->notifications, struct message, entry)
664 if (!message_needs_flushing(message, state)) continue;
666 list_remove(&message->entry);
667 list_init(&message->entry);
669 if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg)))
670 ERR("Failed to free message %p, hr %#lx\n", message, hr);
674 static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment,
675 IDirectMusicSegmentState *state, MUSIC_TIME music_time, DWORD flags)
677 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
678 struct list states = LIST_INIT(states);
679 struct state_entry *entry, *next;
680 DMUS_PMSG msg = {.mtTime = -1};
681 HRESULT hr;
683 FIXME("(%p, %p, %p, %ld, %ld): semi-stub\n", This, segment, state, music_time, flags);
685 if (music_time) FIXME("time parameter %lu not implemented\n", music_time);
686 if (flags != DMUS_SEGF_DEFAULT) FIXME("flags parameter %#lx not implemented\n", flags);
688 if (!music_time && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time)))
689 return hr;
691 EnterCriticalSection(&This->safe);
693 if (!state)
694 enum_segment_states(This, segment, &states);
695 else if ((entry = malloc(sizeof(*entry))))
697 entry->state = state;
698 IDirectMusicSegmentState_AddRef(entry->state);
699 list_add_tail(&states, &entry->entry);
702 if (!segment && !state)
703 performance_flush_messages(This, NULL);
704 else LIST_FOR_EACH_ENTRY(entry, &states, struct state_entry, entry)
705 performance_flush_messages(This, entry->state);
707 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry)
709 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment,
710 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGABORT, (IUnknown *)entry->state)))
711 ERR("Failed to send DMUS_NOTIFICATION_SEGABORT, hr %#lx\n", hr);
712 if (FAILED(hr = segment_state_stop(entry->state, iface)))
713 ERR("Failed to stop segment state, hr %#lx\n", hr);
715 IDirectMusicSegmentState_Release(entry->state);
716 list_remove(&entry->entry);
717 free(entry);
720 if (!state && !segment)
722 if (FAILED(hr = performance_send_midi_pmsg(This, &msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
723 MIDI_SYSTEM_RESET, 0, 0)))
724 ERR("Failed to send MIDI_SYSTEM_RESET message, hr %#lx\n", hr);
727 LeaveCriticalSection(&This->safe);
729 return S_OK;
732 static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface,
733 IDirectMusicSegmentState **state, MUSIC_TIME time)
735 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
736 struct list *ptr, states = LIST_INIT(states);
737 struct state_entry *entry, *next;
738 HRESULT hr = S_OK;
740 TRACE("(%p, %p, %ld)\n", This, state, time);
742 if (!state) return E_POINTER;
744 EnterCriticalSection(&This->safe);
746 enum_segment_states(This, This->primary_segment, &states);
748 if (!(ptr = list_head(&states))) hr = DMUS_E_NOT_FOUND;
749 else
751 entry = LIST_ENTRY(ptr, struct state_entry, entry);
753 *state = entry->state;
754 IDirectMusicSegmentState_AddRef(entry->state);
756 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry)
757 state_entry_destroy(entry);
760 LeaveCriticalSection(&This->safe);
762 return hr;
765 static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds)
767 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
769 TRACE("(%p, %ld)\n", This, dwMilliSeconds);
770 This->dwPrepareTime = dwMilliSeconds;
771 return S_OK;
774 static HRESULT WINAPI performance_GetPrepareTime(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds)
776 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
778 TRACE("(%p, %p)\n", This, pdwMilliSeconds);
779 if (NULL == pdwMilliSeconds) {
780 return E_POINTER;
782 *pdwMilliSeconds = This->dwPrepareTime;
783 return S_OK;
786 static HRESULT WINAPI performance_SetBumperLength(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds)
788 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
790 TRACE("(%p, %ld)\n", This, dwMilliSeconds);
791 This->dwBumperLength = dwMilliSeconds;
792 return S_OK;
795 static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds)
797 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
799 TRACE("(%p, %p)\n", This, pdwMilliSeconds);
800 if (NULL == pdwMilliSeconds) {
801 return E_POINTER;
803 *pdwMilliSeconds = This->dwBumperLength;
804 return S_OK;
807 static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg)
809 const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
810 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
811 struct message *message;
812 HRESULT hr;
814 TRACE("(%p, %p)\n", This, msg);
816 if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER;
817 if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK;
818 if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG;
820 EnterCriticalSection(&This->safe);
822 if (!list_empty(&message->entry))
823 hr = DMUS_E_ALREADY_SENT;
824 else
826 if (!(msg->dwFlags & delivery_flags)) msg->dwFlags |= DMUS_PMSGF_TOOL_IMMEDIATE;
827 if (!(msg->dwFlags & DMUS_PMSGF_MUSICTIME))
829 if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface,
830 msg->rtTime, &msg->mtTime)))
831 goto done;
832 msg->dwFlags |= DMUS_PMSGF_MUSICTIME;
834 if (!(msg->dwFlags & DMUS_PMSGF_REFTIME))
836 if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface,
837 msg->mtTime == -1 ? 0 : msg->mtTime, &msg->rtTime)))
838 goto done;
839 msg->dwFlags |= DMUS_PMSGF_REFTIME;
842 hr = performance_queue_message(This, message);
845 done:
846 LeaveCriticalSection(&This->safe);
847 if (SUCCEEDED(hr)) WakeConditionVariable(&This->cond);
849 return hr;
852 static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface,
853 MUSIC_TIME music_time, REFERENCE_TIME *time)
855 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
856 MUSIC_TIME tempo_time, next = 0;
857 DMUS_TEMPO_PARAM param;
858 double tempo, duration;
859 HRESULT hr;
861 TRACE("(%p, %ld, %p)\n", This, music_time, time);
863 if (!time) return E_POINTER;
864 *time = 0;
866 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
868 EnterCriticalSection(&This->safe);
870 for (tempo = 120.0, duration = tempo_time = 0; music_time > 0; tempo_time += next)
872 if (FAILED(hr = IDirectMusicPerformance_GetParam(iface, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS,
873 tempo_time, &next, &param)))
874 break;
876 if (!next) next = music_time;
877 else next = min(next, music_time);
879 if (param.mtTime <= 0) tempo = param.dblTempo;
880 duration += (600000000.0 * next) / (tempo * DMUS_PPQ);
881 music_time -= next;
884 duration += (600000000.0 * music_time) / (tempo * DMUS_PPQ);
885 *time = This->init_time + duration;
887 LeaveCriticalSection(&This->safe);
889 return S_OK;
892 static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface,
893 REFERENCE_TIME time, MUSIC_TIME *music_time)
895 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
896 MUSIC_TIME tempo_time, next = 0;
897 double tempo, duration, step;
898 DMUS_TEMPO_PARAM param;
899 HRESULT hr;
901 TRACE("(%p, %I64d, %p)\n", This, time, music_time);
903 if (!music_time) return E_POINTER;
904 *music_time = 0;
906 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
908 EnterCriticalSection(&This->safe);
910 duration = time - This->init_time;
912 for (tempo = 120.0, tempo_time = 0; duration > 0; tempo_time += next, duration -= step)
914 if (FAILED(hr = IDirectMusicPerformance_GetParam(iface, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS,
915 tempo_time, &next, &param)))
916 break;
918 if (param.mtTime <= 0) tempo = param.dblTempo;
919 step = (600000000.0 * next) / (tempo * DMUS_PPQ);
920 if (!next || duration < step) break;
921 *music_time = *music_time + next;
924 *music_time = *music_time + round((duration * tempo * DMUS_PPQ) / 600000000.0);
926 LeaveCriticalSection(&This->safe);
928 return S_OK;
931 static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface,
932 IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegState)
934 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
936 FIXME("(%p, %p, %p): stub\n", This, pSegment, pSegState);
937 return S_FALSE;
940 static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *time, MUSIC_TIME *music_time)
942 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
943 REFERENCE_TIME now;
944 HRESULT hr;
946 TRACE("(%p, %p, %p)\n", iface, time, music_time);
948 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
949 if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &now))) return hr;
951 if (time) *time = now;
952 if (music_time) hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, now, music_time);
954 return hr;
957 static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG size, DMUS_PMSG **msg)
959 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
960 struct message *message;
962 TRACE("(%p, %ld, %p)\n", This, size, msg);
964 if (!msg) return E_POINTER;
965 if (size < sizeof(DMUS_PMSG)) return E_INVALIDARG;
967 if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(struct message)))) return E_OUTOFMEMORY;
968 message->msg.dwSize = size;
969 list_init(&message->entry);
970 *msg = &message->msg;
972 return S_OK;
975 static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg)
977 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
978 struct message *message;
979 HRESULT hr;
981 TRACE("(%p, %p)\n", This, msg);
983 if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER;
985 EnterCriticalSection(&This->safe);
986 hr = !list_empty(&message->entry) ? DMUS_E_CANNOT_FREE : S_OK;
987 LeaveCriticalSection(&This->safe);
989 if (SUCCEEDED(hr))
991 if (msg->pTool) IDirectMusicTool_Release(msg->pTool);
992 if (msg->pGraph) IDirectMusicGraph_Release(msg->pGraph);
993 if (msg->punkUser) IUnknown_Release(msg->punkUser);
994 free(message);
997 return hr;
1000 static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph **graph)
1002 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1004 TRACE("(%p, %p)\n", This, graph);
1006 if (!graph)
1007 return E_POINTER;
1009 *graph = This->pToolGraph;
1010 if (This->pToolGraph) {
1011 IDirectMusicGraph_AddRef(*graph);
1014 return *graph ? S_OK : DMUS_E_NOT_FOUND;
1017 static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph *pGraph)
1019 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1021 FIXME("(%p, %p): to check\n", This, pGraph);
1023 if (NULL != This->pToolGraph) {
1024 /* Todo clean buffers and tools before */
1025 IDirectMusicGraph_Release(This->pToolGraph);
1027 This->pToolGraph = pGraph;
1028 if (NULL != This->pToolGraph) {
1029 IDirectMusicGraph_AddRef(This->pToolGraph);
1031 return S_OK;
1034 static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 *iface,
1035 HANDLE notification_event, REFERENCE_TIME minimum_time)
1037 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1039 TRACE("(%p, %p, %I64d)\n", This, notification_event, minimum_time);
1041 This->notification_event = notification_event;
1042 if (minimum_time)
1043 This->notification_timeout = minimum_time;
1044 else if (!This->notification_timeout)
1045 This->notification_timeout = 20000000; /* 2 seconds */
1047 return S_OK;
1050 static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 *iface,
1051 DMUS_NOTIFICATION_PMSG **ret_msg)
1053 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1054 struct list *entry;
1056 TRACE("(%p, %p)\n", This, ret_msg);
1058 if (!ret_msg) return E_POINTER;
1060 EnterCriticalSection(&This->safe);
1061 if ((entry = list_head(&This->notifications)))
1063 struct message *message = LIST_ENTRY(entry, struct message, entry);
1064 list_remove(&message->entry);
1065 list_init(&message->entry);
1066 *ret_msg = (DMUS_NOTIFICATION_PMSG *)&message->msg;
1068 LeaveCriticalSection(&This->safe);
1070 return entry ? S_OK : S_FALSE;
1073 static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID type)
1075 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1076 HRESULT hr = S_OK;
1078 FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type));
1080 if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE))
1082 hr = This->notification_performance ? S_FALSE : S_OK;
1083 This->notification_performance = TRUE;
1085 if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT))
1087 hr = This->notification_segment ? S_FALSE : S_OK;
1088 This->notification_segment = TRUE;
1091 return hr;
1094 static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID type)
1096 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1097 HRESULT hr = S_FALSE;
1099 FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type));
1101 if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE))
1103 hr = This->notification_performance ? S_OK : S_FALSE;
1104 This->notification_performance = FALSE;
1106 if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT))
1108 hr = This->notification_segment ? S_OK : S_FALSE;
1109 This->notification_segment = FALSE;
1112 return hr;
1115 static void performance_update_latency_time(struct performance *This, IDirectMusicPort *port,
1116 REFERENCE_TIME *ret_time)
1118 IDirectMusicPerformance8 *iface = &This->IDirectMusicPerformance8_iface;
1119 REFERENCE_TIME latency_time, current_time;
1120 IReferenceClock *latency_clock;
1121 HRESULT hr;
1123 if (!ret_time) ret_time = &latency_time;
1124 if (SUCCEEDED(hr = IDirectMusicPort_GetLatencyClock(port, &latency_clock)))
1126 hr = IReferenceClock_GetTime(latency_clock, ret_time);
1127 if (SUCCEEDED(hr)) hr = IDirectMusicPerformance8_GetTime(iface, &current_time, NULL);
1128 if (SUCCEEDED(hr) && This->latency_offset < (*ret_time - current_time))
1130 TRACE("Updating performance %p latency %I64d -> %I64d\n", This,
1131 This->latency_offset, *ret_time - current_time);
1132 This->latency_offset = *ret_time - current_time;
1134 IReferenceClock_Release(latency_clock);
1137 if (FAILED(hr)) ERR("Failed to update performance %p latency, hr %#lx\n", This, hr);
1140 static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params)
1142 IDirectMusicPort *port;
1143 GUID guid;
1144 unsigned int i;
1145 HRESULT hr;
1147 if (FAILED(hr = IDirectMusic8_GetDefaultPort(perf->dmusic, &guid)))
1148 return hr;
1150 if (FAILED(hr = IDirectMusic8_CreatePort(perf->dmusic, &guid, params, &port, NULL)))
1151 return hr;
1153 if (FAILED(hr = IDirectMusicPort_SetDirectSound(port, perf->dsound, NULL))
1154 || FAILED(hr = IDirectMusicPort_Activate(port, TRUE)))
1156 IDirectMusicPort_Release(port);
1157 return hr;
1160 wine_rb_destroy(&perf->channel_blocks, channel_block_free, NULL);
1162 for (i = 0; i < params->dwChannelGroups; i++)
1164 if (FAILED(hr = channel_block_init(perf, i, port, i + 1)))
1165 ERR("Failed to init channel block, hr %#lx\n", hr);
1168 performance_update_latency_time(perf, port, NULL);
1169 IDirectMusicPort_Release(port);
1170 return S_OK;
1173 static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDirectMusicPort *port)
1175 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1177 FIXME("(%p, %p): semi-stub\n", This, port);
1179 if (!This->dmusic) return DMUS_E_NOT_INIT;
1180 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
1182 if (!port) {
1183 DMUS_PORTPARAMS params = {
1184 .dwSize = sizeof(params),
1185 .dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS,
1186 .dwChannelGroups = 1
1189 return perf_dmport_create(This, &params);
1192 IDirectMusicPort_AddRef(port);
1194 * We should remember added Ports (for example using a list)
1195 * and control if Port is registered for each api who use ports
1198 performance_update_latency_time(This, port, NULL);
1199 return S_OK;
1202 static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, IDirectMusicPort *pPort)
1204 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1206 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
1208 FIXME("(%p, %p): stub\n", This, pPort);
1209 IDirectMusicPort_Release(pPort);
1210 return S_OK;
1213 static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface,
1214 DWORD block_num, IDirectMusicPort *port, DWORD midi_group)
1216 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1218 FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, midi_group);
1220 if (!port) return E_POINTER;
1221 if (block_num > MAXDWORD / 16) return E_INVALIDARG;
1222 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
1224 return channel_block_init(This, block_num, port, midi_group);
1227 static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD channel_num,
1228 IDirectMusicPort *port, DWORD midi_group, DWORD midi_channel)
1230 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1231 struct channel *channel;
1232 HRESULT hr;
1234 FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, channel_num, port, midi_group, midi_channel);
1236 if (!port) return E_POINTER;
1237 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
1239 if (!(channel = performance_get_channel(This, channel_num)))
1241 if (FAILED(hr = IDirectMusicPerformance8_AssignPChannelBlock(iface,
1242 channel_num / 16, port, 0)))
1243 return hr;
1244 if (!(channel = performance_get_channel(This, channel_num)))
1245 return DMUS_E_NOT_FOUND;
1248 channel->midi_group = midi_group;
1249 channel->midi_channel = midi_channel;
1250 if (channel->port) IDirectMusicPort_Release(channel->port);
1251 if ((channel->port = port)) IDirectMusicPort_AddRef(channel->port);
1253 return S_OK;
1256 static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD channel_num,
1257 IDirectMusicPort **port, DWORD *midi_group, DWORD *midi_channel)
1259 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1260 struct channel *channel;
1262 TRACE("(%p)->(%ld, %p, %p, %p)\n", This, channel_num, port, midi_group, midi_channel);
1264 if (!(channel = performance_get_channel(This, channel_num))) return E_INVALIDARG;
1265 if (port)
1267 *port = channel->port;
1268 IDirectMusicPort_AddRef(*port);
1270 if (midi_group) *midi_group = channel->midi_group;
1271 if (midi_channel) *midi_channel = channel->midi_channel;
1273 return S_OK;
1276 static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *iface,
1277 IDirectMusicInstrument *instrument, DWORD port_channel,
1278 IDirectMusicDownloadedInstrument **downloaded, DMUS_NOTERANGE *note_ranges,
1279 DWORD note_range_count, IDirectMusicPort **port, DWORD *group, DWORD *music_channel)
1281 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1282 IDirectMusicPort *tmp_port = NULL;
1283 HRESULT hr;
1285 TRACE("(%p, %p, %ld, %p, %p, %ld, %p, %p, %p)\n", This, instrument, port_channel, downloaded,
1286 note_ranges, note_range_count, port, group, music_channel);
1288 if (!port) port = &tmp_port;
1289 if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(iface, port_channel, port, group, music_channel)))
1290 return hr;
1292 hr = IDirectMusicPort_DownloadInstrument(*port, instrument, downloaded, note_ranges, note_range_count);
1293 if (tmp_port) IDirectMusicPort_Release(tmp_port);
1294 return hr;
1297 static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DWORD dwFlags)
1299 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1301 FIXME("(%p, %ld, %ld): stub\n", This, mtTime, dwFlags);
1302 return S_OK;
1305 static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID type,
1306 DWORD group, DWORD index, MUSIC_TIME music_time, MUSIC_TIME *next_time, void *param)
1308 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1309 HRESULT hr;
1311 TRACE("(%p, %s, %ld, %ld, %ld, %p, %p)\n", This, debugstr_dmguid(type), group, index, music_time, next_time, param);
1313 if (next_time) *next_time = 0;
1315 if (!This->control_segment) hr = DMUS_E_NOT_FOUND;
1316 else hr = IDirectMusicSegment_GetParam(This->control_segment, type, group, index, music_time, next_time, param);
1318 if (FAILED(hr))
1320 if (!This->primary_segment) hr = DMUS_E_NOT_FOUND;
1321 else hr = IDirectMusicSegment_GetParam(This->primary_segment, type, group, index, music_time, next_time, param);
1324 return hr;
1327 static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
1328 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam)
1330 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1332 FIXME("(%p, %s, %ld, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pParam);
1333 return S_OK;
1336 static HRESULT WINAPI performance_GetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
1337 void *pParam, DWORD dwSize)
1339 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1341 TRACE("(%p, %s, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), pParam, dwSize);
1343 if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload))
1344 memcpy(pParam, &This->fAutoDownload, sizeof(This->fAutoDownload));
1345 if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel))
1346 memcpy(pParam, &This->cMasterGrooveLevel, sizeof(This->cMasterGrooveLevel));
1347 if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo))
1348 memcpy(pParam, &This->fMasterTempo, sizeof(This->fMasterTempo));
1349 if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume))
1350 memcpy(pParam, &This->lMasterVolume, sizeof(This->lMasterVolume));
1352 return S_OK;
1355 static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
1356 void *pParam, DWORD dwSize)
1358 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1360 TRACE("(%p, %s, %p, %ld)\n", This, debugstr_dmguid(rguidType), pParam, dwSize);
1362 if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload)) {
1363 memcpy(&This->fAutoDownload, pParam, dwSize);
1364 TRACE("=> AutoDownload set to %d\n", This->fAutoDownload);
1366 if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel)) {
1367 memcpy(&This->cMasterGrooveLevel, pParam, dwSize);
1368 TRACE("=> MasterGrooveLevel set to %i\n", This->cMasterGrooveLevel);
1370 if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo)) {
1371 memcpy(&This->fMasterTempo, pParam, dwSize);
1372 TRACE("=> MasterTempo set to %f\n", This->fMasterTempo);
1374 if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume)) {
1375 memcpy(&This->lMasterVolume, pParam, dwSize);
1376 TRACE("=> MasterVolume set to %li\n", This->lMasterVolume);
1379 return S_OK;
1382 static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *ret_time)
1384 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1385 REFERENCE_TIME current_time;
1386 HRESULT hr;
1388 TRACE("(%p, %p)\n", This, ret_time);
1390 if (SUCCEEDED(hr = IDirectMusicPerformance8_GetTime(iface, &current_time, NULL)))
1391 *ret_time = current_time + This->latency_offset;
1393 return hr;
1396 static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime)
1399 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1401 FIXME("(%p, %p): stub\n", This, prtTime);
1402 return S_OK;
1405 static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtAmount)
1407 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1409 FIXME("(%p, 0x%s): stub\n", This, wine_dbgstr_longlong(rtAmount));
1410 return S_OK;
1413 static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface)
1415 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1416 struct list states = LIST_INIT(states);
1417 struct state_entry *entry, *next;
1418 DMUS_PMSG msg = {.mtTime = -1};
1419 HANDLE message_thread;
1420 HRESULT hr;
1422 FIXME("(%p): semi-stub\n", This);
1424 if ((message_thread = This->message_thread))
1426 EnterCriticalSection(&This->safe);
1427 This->message_thread = NULL;
1428 LeaveCriticalSection(&This->safe);
1429 WakeConditionVariable(&This->cond);
1431 WaitForSingleObject(message_thread, INFINITE);
1432 CloseHandle(message_thread);
1435 This->notification_performance = FALSE;
1436 This->notification_segment = FALSE;
1438 enum_segment_states(This, NULL, &states);
1439 performance_flush_messages(This, NULL);
1441 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry)
1443 if (FAILED(hr = segment_state_end_play(entry->state, iface)))
1444 ERR("Failed to stop segment state, hr %#lx\n", hr);
1446 IDirectMusicSegmentState_Release(entry->state);
1447 list_remove(&entry->entry);
1448 free(entry);
1451 if (FAILED(hr = performance_send_midi_pmsg(This, &msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1452 MIDI_SYSTEM_RESET, 0, 0)))
1453 ERR("Failed to send MIDI_SYSTEM_RESET message, hr %#lx\n", hr);
1455 performance_set_primary_segment(This, NULL);
1456 performance_set_control_segment(This, NULL);
1458 if (This->master_clock)
1460 IReferenceClock_Release(This->master_clock);
1461 This->master_clock = NULL;
1463 if (This->dsound) {
1464 IDirectSound_Release(This->dsound);
1465 This->dsound = NULL;
1467 if (This->dmusic) {
1468 IDirectMusic8_SetDirectSound(This->dmusic, NULL, NULL);
1469 IDirectMusic8_Release(This->dmusic);
1470 This->dmusic = NULL;
1472 This->audio_paths_enabled = FALSE;
1474 return S_OK;
1477 static HRESULT WINAPI performance_GetResolvedTime(IDirectMusicPerformance8 *iface,
1478 REFERENCE_TIME rtTime, REFERENCE_TIME *prtResolved, DWORD dwTimeResolveFlags)
1480 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1482 FIXME("(%p, 0x%s, %p, %ld): stub\n", This, wine_dbgstr_longlong(rtTime),
1483 prtResolved, dwTimeResolveFlags);
1484 return S_OK;
1487 static HRESULT WINAPI performance_MIDIToMusic(IDirectMusicPerformance8 *iface, BYTE bMIDIValue,
1488 DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, WORD *pwMusicValue)
1490 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1492 FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, bMIDIValue, pChord, bPlayMode, bChordLevel, pwMusicValue);
1493 return S_OK;
1496 static HRESULT WINAPI performance_MusicToMIDI(IDirectMusicPerformance8 *iface, WORD wMusicValue,
1497 DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, BYTE *pbMIDIValue)
1499 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1501 FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, wMusicValue, pChord, bPlayMode, bChordLevel, pbMIDIValue);
1502 return S_OK;
1505 static HRESULT WINAPI performance_TimeToRhythm(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime,
1506 DMUS_TIMESIGNATURE *pTimeSig, WORD *pwMeasure, BYTE *pbBeat, BYTE *pbGrid, short *pnOffset)
1508 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1510 FIXME("(%p, %ld, %p, %p, %p, %p, %p): stub\n", This, mtTime, pTimeSig, pwMeasure, pbBeat, pbGrid, pnOffset);
1511 return S_OK;
1514 static HRESULT WINAPI performance_RhythmToTime(IDirectMusicPerformance8 *iface, WORD wMeasure,
1515 BYTE bBeat, BYTE bGrid, short nOffset, DMUS_TIMESIGNATURE *pTimeSig, MUSIC_TIME *pmtTime)
1517 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1519 FIXME("(%p, %d, %d, %d, %i, %p, %p): stub\n", This, wMeasure, bBeat, bGrid, nOffset, pTimeSig, pmtTime);
1520 return S_OK;
1523 /* IDirectMusicPerformance8 Interface part follow: */
1524 static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic,
1525 IDirectSound **dsound, HWND hwnd, DWORD default_path_type, DWORD num_channels, DWORD flags,
1526 DMUS_AUDIOPARAMS *params)
1528 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1529 HRESULT hr = S_OK;
1531 TRACE("(%p, %p, %p, %p, %lx, %lu, %lx, %p)\n", This, dmusic, dsound, hwnd, default_path_type,
1532 num_channels, flags, params);
1534 if (flags) FIXME("flags parameter not used\n");
1535 if (params) FIXME("params parameter not used\n");
1537 if (FAILED(hr = IDirectMusicPerformance8_Init(iface, dmusic && *dmusic ? dmusic : NULL,
1538 dsound ? *dsound : NULL, hwnd)))
1539 return hr;
1541 This->audio_paths_enabled = TRUE;
1542 if (default_path_type)
1544 hr = IDirectMusicPerformance8_CreateStandardAudioPath(iface, default_path_type,
1545 num_channels, FALSE, &This->pDefaultPath);
1546 if (FAILED(hr))
1548 IDirectMusicPerformance_CloseDown(iface);
1549 return hr;
1553 if (dsound && !*dsound) {
1554 *dsound = This->dsound;
1555 IDirectSound_AddRef(*dsound);
1557 if (dmusic && !*dmusic) {
1558 *dmusic = This->dmusic;
1559 IDirectMusic_AddRef(*dmusic);
1562 return S_OK;
1565 static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *source,
1566 WCHAR *segment_name, IUnknown *transition, DWORD segment_flags, INT64 start_time,
1567 IDirectMusicSegmentState **segment_state, IUnknown *from, IUnknown *audio_path)
1569 BOOL primary = !(segment_flags & DMUS_SEGF_SECONDARY), control = (segment_flags & DMUS_SEGF_CONTROL);
1570 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1571 IDirectMusicSegmentState *state;
1572 IDirectMusicSegment *segment;
1573 MUSIC_TIME music_time;
1574 HRESULT hr;
1576 FIXME("(%p, %p, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name),
1577 transition, segment_flags, start_time, segment_state, from, audio_path);
1579 /* NOTE: The time is in music time unless the DMUS_SEGF_REFTIME flag is set. */
1580 if (segment_flags) FIXME("flags %#lx not implemented\n", segment_flags);
1581 if (start_time) FIXME("start_time %I64d not implemented\n", start_time);
1583 if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment)))
1584 return hr;
1586 EnterCriticalSection(&This->safe);
1588 if (primary) performance_set_primary_segment(This, segment);
1589 if (control) performance_set_control_segment(This, segment);
1591 if ((!(music_time = start_time) && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time)))
1592 || FAILED(hr = segment_state_create(segment, music_time, iface, &state)))
1594 if (primary) performance_set_primary_segment(This, NULL);
1595 if (control) performance_set_control_segment(This, NULL);
1596 LeaveCriticalSection(&This->safe);
1598 IDirectMusicSegment_Release(segment);
1599 return hr;
1602 if (FAILED(hr = segment_state_play(state, iface)))
1604 ERR("Failed to play segment state, hr %#lx\n", hr);
1605 if (primary) performance_set_primary_segment(This, NULL);
1606 if (control) performance_set_control_segment(This, NULL);
1608 else if (segment_state)
1610 *segment_state = state;
1611 IDirectMusicSegmentState_AddRef(state);
1614 LeaveCriticalSection(&This->safe);
1616 IDirectMusicSegmentState_Release(state);
1617 IDirectMusicSegment_Release(segment);
1618 return hr;
1621 static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnknown *pObjectToStop,
1622 __int64 i64StopTime, DWORD dwFlags)
1624 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1626 FIXME("(%p, %p, 0x%s, %ld): stub\n", This, pObjectToStop,
1627 wine_dbgstr_longlong(i64StopTime), dwFlags);
1628 return S_OK;
1631 static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg, DMUS_PMSG **clone)
1633 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1634 HRESULT hr;
1636 TRACE("(%p, %p, %p)\n", This, msg, clone);
1638 if (!msg || !clone) return E_POINTER;
1639 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(iface, msg->dwSize, clone))) return hr;
1641 memcpy(*clone, msg, msg->dwSize);
1642 if (msg->pTool) IDirectMusicTool_AddRef(msg->pTool);
1643 if (msg->pGraph) IDirectMusicGraph_AddRef(msg->pGraph);
1644 if (msg->punkUser) IUnknown_AddRef(msg->punkUser);
1646 return S_OK;
1649 static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface,
1650 IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ret_iface)
1652 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1653 IDirectMusicAudioPath *pPath;
1655 FIXME("(%p, %p, %d, %p): stub\n", This, pSourceConfig, fActivate, ret_iface);
1657 if (!ret_iface) return E_POINTER;
1658 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1660 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void **)&pPath);
1661 set_audiopath_perf_pointer(pPath, iface);
1663 /** TODO */
1664 *ret_iface = pPath;
1665 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1668 static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface,
1669 DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ret_iface)
1671 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1672 IDirectMusicAudioPath *pPath;
1673 DSBUFFERDESC desc;
1674 WAVEFORMATEX format;
1675 DMUS_PORTPARAMS params = {0};
1676 IDirectSoundBuffer *buffer, *primary_buffer;
1677 HRESULT hr = S_OK;
1679 FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This, dwType, pchannel_count, fActivate, ret_iface);
1681 if (!ret_iface) return E_POINTER;
1682 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1684 *ret_iface = NULL;
1686 /* Secondary buffer description */
1687 memset(&format, 0, sizeof(format));
1688 format.wFormatTag = WAVE_FORMAT_PCM;
1689 format.nChannels = 1;
1690 format.nSamplesPerSec = 44000;
1691 format.nAvgBytesPerSec = 44000*2;
1692 format.nBlockAlign = 2;
1693 format.wBitsPerSample = 16;
1694 format.cbSize = 0;
1696 memset(&desc, 0, sizeof(desc));
1697 desc.dwSize = sizeof(desc);
1698 desc.dwFlags = DSBCAPS_CTRLFX | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
1699 desc.dwBufferBytes = DSBSIZE_MIN;
1700 desc.dwReserved = 0;
1701 desc.lpwfxFormat = &format;
1702 desc.guid3DAlgorithm = GUID_NULL;
1704 switch(dwType) {
1705 case DMUS_APATH_DYNAMIC_3D:
1706 desc.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE;
1707 break;
1708 case DMUS_APATH_DYNAMIC_MONO:
1709 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1710 break;
1711 case DMUS_APATH_SHARED_STEREOPLUSREVERB:
1712 /* normally we have to create 2 buffers (one for music other for reverb)
1713 * in this case. See msdn
1715 case DMUS_APATH_DYNAMIC_STEREO:
1716 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1717 format.nChannels = 2;
1718 format.nBlockAlign *= 2;
1719 format.nAvgBytesPerSec *=2;
1720 break;
1721 default:
1722 return E_INVALIDARG;
1725 /* Create a port */
1726 params.dwSize = sizeof(params);
1727 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS | DMUS_PORTPARAMS_AUDIOCHANNELS;
1728 params.dwChannelGroups = (pchannel_count + 15) / 16;
1729 params.dwAudioChannels = format.nChannels;
1730 if (FAILED(hr = perf_dmport_create(This, &params)))
1731 return hr;
1733 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &buffer, NULL);
1734 if (FAILED(hr))
1735 return DSERR_BUFFERLOST;
1737 /* Update description for creating primary buffer */
1738 desc.dwFlags |= DSBCAPS_PRIMARYBUFFER;
1739 desc.dwFlags &= ~DSBCAPS_CTRLFX;
1740 desc.dwBufferBytes = 0;
1741 desc.lpwfxFormat = NULL;
1743 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &primary_buffer, NULL);
1744 if (FAILED(hr)) {
1745 IDirectSoundBuffer_Release(buffer);
1746 return DSERR_BUFFERLOST;
1749 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void**)&pPath);
1750 set_audiopath_perf_pointer(pPath, iface);
1751 set_audiopath_dsound_buffer(pPath, buffer);
1752 set_audiopath_primary_dsound_buffer(pPath, primary_buffer);
1754 *ret_iface = pPath;
1755 TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface);
1756 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1759 static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *audio_path)
1761 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1763 FIXME("(%p, %p): semi-stub\n", This, audio_path);
1765 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1767 if (This->pDefaultPath) IDirectMusicAudioPath_Release(This->pDefaultPath);
1768 if ((This->pDefaultPath = audio_path))
1770 IDirectMusicAudioPath_AddRef(This->pDefaultPath);
1771 set_audiopath_perf_pointer(This->pDefaultPath, iface);
1774 return S_OK;
1777 static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface,
1778 IDirectMusicAudioPath **ret_iface)
1780 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1782 FIXME("(%p, %p): semi-stub (%p)\n", This, ret_iface, This->pDefaultPath);
1784 if (!ret_iface) return E_POINTER;
1785 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1787 if ((*ret_iface = This->pDefaultPath)) IDirectMusicAudioPath_AddRef(*ret_iface);
1789 return S_OK;
1792 static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID,
1793 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
1795 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1797 FIXME("(%p, %s, %ld, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwTrackID, dwGroupBits, dwIndex, mtTime, pmtNext, pParam);
1799 return S_OK;
1802 static const IDirectMusicPerformance8Vtbl performance_vtbl =
1804 performance_QueryInterface,
1805 performance_AddRef,
1806 performance_Release,
1807 performance_Init,
1808 performance_PlaySegment,
1809 performance_Stop,
1810 performance_GetSegmentState,
1811 performance_SetPrepareTime,
1812 performance_GetPrepareTime,
1813 performance_SetBumperLength,
1814 performance_GetBumperLength,
1815 performance_SendPMsg,
1816 performance_MusicToReferenceTime,
1817 performance_ReferenceToMusicTime,
1818 performance_IsPlaying,
1819 performance_GetTime,
1820 performance_AllocPMsg,
1821 performance_FreePMsg,
1822 performance_GetGraph,
1823 performance_SetGraph,
1824 performance_SetNotificationHandle,
1825 performance_GetNotificationPMsg,
1826 performance_AddNotificationType,
1827 performance_RemoveNotificationType,
1828 performance_AddPort,
1829 performance_RemovePort,
1830 performance_AssignPChannelBlock,
1831 performance_AssignPChannel,
1832 performance_PChannelInfo,
1833 performance_DownloadInstrument,
1834 performance_Invalidate,
1835 performance_GetParam,
1836 performance_SetParam,
1837 performance_GetGlobalParam,
1838 performance_SetGlobalParam,
1839 performance_GetLatencyTime,
1840 performance_GetQueueTime,
1841 performance_AdjustTime,
1842 performance_CloseDown,
1843 performance_GetResolvedTime,
1844 performance_MIDIToMusic,
1845 performance_MusicToMIDI,
1846 performance_TimeToRhythm,
1847 performance_RhythmToTime,
1848 performance_InitAudio,
1849 performance_PlaySegmentEx,
1850 performance_StopEx,
1851 performance_ClonePMsg,
1852 performance_CreateAudioPath,
1853 performance_CreateStandardAudioPath,
1854 performance_SetDefaultAudioPath,
1855 performance_GetDefaultAudioPath,
1856 performance_GetParamEx,
1859 static inline struct performance *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface)
1861 return CONTAINING_RECORD(iface, struct performance, IDirectMusicGraph_iface);
1864 static HRESULT WINAPI performance_graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface)
1866 struct performance *This = impl_from_IDirectMusicGraph(iface);
1867 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
1870 static ULONG WINAPI performance_graph_AddRef(IDirectMusicGraph *iface)
1872 struct performance *This = impl_from_IDirectMusicGraph(iface);
1873 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
1876 static ULONG WINAPI performance_graph_Release(IDirectMusicGraph *iface)
1878 struct performance *This = impl_from_IDirectMusicGraph(iface);
1879 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
1882 static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg)
1884 struct performance *This = impl_from_IDirectMusicGraph(iface);
1885 HRESULT hr;
1887 TRACE("(%p, %p)\n", This, msg);
1889 if (!msg) return E_POINTER;
1891 /* FIXME: Implement segment and audio path graphs support */
1892 if (!This->pToolGraph) hr = DMUS_S_LAST_TOOL;
1893 else if (FAILED(hr = IDirectMusicGraph_StampPMsg(This->pToolGraph, msg))) return hr;
1895 if (msg->pGraph)
1897 IDirectMusicTool_Release(msg->pGraph);
1898 msg->pGraph = NULL;
1901 if (hr == DMUS_S_LAST_TOOL)
1903 const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
1904 msg->dwFlags &= ~delivery_flags;
1905 msg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
1907 if (msg->pTool) IDirectMusicTool_Release(msg->pTool);
1908 msg->pTool = &This->IDirectMusicTool_iface;
1909 IDirectMusicTool_AddRef(msg->pTool);
1910 return S_OK;
1913 if (SUCCEEDED(hr))
1915 msg->pGraph = &This->IDirectMusicGraph_iface;
1916 IDirectMusicTool_AddRef(msg->pGraph);
1919 return hr;
1922 static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool,
1923 DWORD *channels, DWORD channels_count, LONG index)
1925 struct performance *This = impl_from_IDirectMusicGraph(iface);
1926 TRACE("(%p, %p, %p, %lu, %ld)\n", This, tool, channels, channels_count, index);
1927 return E_NOTIMPL;
1930 static HRESULT WINAPI performance_graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **tool)
1932 struct performance *This = impl_from_IDirectMusicGraph(iface);
1933 TRACE("(%p, %lu, %p)\n", This, index, tool);
1934 return E_NOTIMPL;
1937 static HRESULT WINAPI performance_graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool)
1939 struct performance *This = impl_from_IDirectMusicGraph(iface);
1940 TRACE("(%p, %p)\n", This, tool);
1941 return E_NOTIMPL;
1944 static const IDirectMusicGraphVtbl performance_graph_vtbl =
1946 performance_graph_QueryInterface,
1947 performance_graph_AddRef,
1948 performance_graph_Release,
1949 performance_graph_StampPMsg,
1950 performance_graph_InsertTool,
1951 performance_graph_GetTool,
1952 performance_graph_RemoveTool,
1955 static inline struct performance *impl_from_IDirectMusicTool(IDirectMusicTool *iface)
1957 return CONTAINING_RECORD(iface, struct performance, IDirectMusicTool_iface);
1960 static HRESULT WINAPI performance_tool_QueryInterface(IDirectMusicTool *iface, REFIID riid, void **ret_iface)
1962 struct performance *This = impl_from_IDirectMusicTool(iface);
1963 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
1966 static ULONG WINAPI performance_tool_AddRef(IDirectMusicTool *iface)
1968 struct performance *This = impl_from_IDirectMusicTool(iface);
1969 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
1972 static ULONG WINAPI performance_tool_Release(IDirectMusicTool *iface)
1974 struct performance *This = impl_from_IDirectMusicTool(iface);
1975 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
1978 static HRESULT WINAPI performance_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph)
1980 struct performance *This = impl_from_IDirectMusicTool(iface);
1981 TRACE("(%p, %p)\n", This, graph);
1982 return E_NOTIMPL;
1985 static HRESULT WINAPI performance_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type)
1987 struct performance *This = impl_from_IDirectMusicTool(iface);
1988 TRACE("(%p, %p)\n", This, type);
1989 *type = DMUS_PMSGF_TOOL_IMMEDIATE;
1990 return S_OK;
1993 static HRESULT WINAPI performance_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size)
1995 struct performance *This = impl_from_IDirectMusicTool(iface);
1996 TRACE("(%p, %p)\n", This, size);
1997 *size = 0;
1998 return S_OK;
2001 static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size)
2003 struct performance *This = impl_from_IDirectMusicTool(iface);
2004 TRACE("(%p, %p, %lu)\n", This, types, size);
2005 return E_NOTIMPL;
2008 static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface,
2009 IDirectMusicPerformance *performance, DMUS_PMSG *msg)
2011 struct performance *This = impl_from_IDirectMusicTool(iface);
2012 struct message *message = message_from_DMUS_PMSG(msg);
2013 HRESULT hr;
2015 TRACE("(%p, %p, %p)\n", This, performance, msg);
2017 switch (msg->dwType)
2019 case DMUS_PMSGT_MIDI:
2021 static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD);
2022 DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size};
2023 DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg;
2024 REFERENCE_TIME latency_time;
2025 IDirectMusicBuffer *buffer;
2026 IDirectMusicPort *port;
2027 DWORD group, channel;
2028 UINT value = 0;
2030 if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(performance, msg->dwPChannel,
2031 &port, &group, &channel)))
2033 WARN("Failed to get message port, hr %#lx\n", hr);
2034 return DMUS_S_FREE;
2036 performance_update_latency_time(This, port, &latency_time);
2038 value |= channel;
2039 value |= (UINT)midi->bStatus;
2040 value |= (UINT)midi->bByte1 << 8;
2041 value |= (UINT)midi->bByte2 << 16;
2043 if (SUCCEEDED(hr = IDirectMusic_CreateMusicBuffer(This->dmusic, &desc, &buffer, NULL)))
2045 if (msg->rtTime == -1) msg->rtTime = latency_time;
2046 hr = IDirectMusicBuffer_PackStructured(buffer, msg->rtTime, group, value);
2047 if (SUCCEEDED(hr)) hr = IDirectMusicPort_PlayBuffer(port, buffer);
2048 IDirectMusicBuffer_Release(buffer);
2051 IDirectMusicPort_Release(port);
2052 break;
2055 case DMUS_PMSGT_NOTE:
2057 DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg;
2059 msg->mtTime += note->nOffset;
2061 if (note->bFlags & DMUS_NOTEF_NOTEON)
2063 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2064 MIDI_NOTE_ON, note->bMidiValue, note->bVelocity)))
2065 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2067 if (note->mtDuration)
2069 msg->mtTime -= note->nOffset;
2070 msg->mtTime += max(1, note->mtDuration - 1);
2071 if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(performance, msg->mtTime, &msg->rtTime)))
2072 return hr;
2073 note->bFlags &= ~DMUS_NOTEF_NOTEON;
2074 return DMUS_S_REQUEUE;
2078 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2079 MIDI_NOTE_OFF, note->bMidiValue, 0)))
2080 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2082 break;
2085 case DMUS_PMSGT_CURVE:
2087 DMUS_CURVE_PMSG *curve = (DMUS_CURVE_PMSG *)msg;
2089 msg->mtTime += curve->nOffset;
2090 switch (curve->dwType)
2092 case DMUS_CURVET_CCCURVE:
2093 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2094 MIDI_CONTROL_CHANGE, curve->bCCData, curve->nStartValue)))
2095 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2096 break;
2097 case DMUS_CURVET_RPNCURVE:
2098 case DMUS_CURVET_NRPNCURVE:
2099 FIXME("Unhandled curve type %#lx\n", curve->dwType);
2100 break;
2103 break;
2106 case DMUS_PMSGT_PATCH:
2108 DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg;
2110 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2111 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_MSB, patch->byMSB)))
2112 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2114 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2115 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_LSB, patch->byLSB)))
2116 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2118 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2119 MIDI_PROGRAM_CHANGE, patch->byInstrument, 0)))
2120 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2122 break;
2125 case DMUS_PMSGT_NOTIFICATION:
2127 DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg;
2128 struct message *previous;
2129 BOOL enabled = FALSE;
2131 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE))
2132 enabled = This->notification_performance;
2133 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_SEGMENT))
2134 enabled = This->notification_segment;
2135 if (!enabled) return DMUS_S_FREE;
2137 if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE)
2139 /* re-send the message for queueing at the expected time */
2140 msg->dwFlags &= ~DMUS_PMSGF_TOOL_IMMEDIATE;
2141 msg->dwFlags |= DMUS_PMSGF_TOOL_ATTIME;
2143 if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg)))
2145 ERR("Failed to send notification message, hr %#lx\n", hr);
2146 return DMUS_S_FREE;
2149 return S_OK;
2152 list_add_tail(&This->notifications, &message->entry);
2154 /* discard old notification messages */
2157 previous = LIST_ENTRY(list_head(&This->notifications), struct message, entry);
2158 if (This->notification_timeout <= 0) break; /* negative values may be used to keep everything */
2159 if (message->msg.rtTime - previous->msg.rtTime <= This->notification_timeout) break;
2160 list_remove(&previous->entry);
2161 list_init(&previous->entry);
2162 } while (SUCCEEDED(hr = IDirectMusicPerformance_FreePMsg(performance, &previous->msg)));
2164 SetEvent(This->notification_event);
2165 return S_OK;
2168 case DMUS_PMSGT_WAVE:
2169 if (FAILED(hr = IDirectSoundBuffer_Play((IDirectSoundBuffer *)msg->punkUser, 0, 0, 0)))
2170 WARN("Failed to play wave buffer, hr %#lx\n", hr);
2171 break;
2173 case DMUS_PMSGT_INTERNAL_SEGMENT_TICK:
2174 msg->rtTime += 10000000;
2175 msg->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
2177 /* re-send the tick message until segment_state_tick returns S_FALSE */
2178 if (FAILED(hr = segment_state_tick((IDirectMusicSegmentState *)msg->punkUser,
2179 (IDirectMusicPerformance8 *)performance)))
2180 ERR("Failed to tick segment state %p, hr %#lx\n", msg->punkUser, hr);
2181 else if (hr == S_FALSE)
2182 return DMUS_S_FREE; /* done ticking */
2183 else if (FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, msg)))
2184 ERR("Failed to queue tick for segment state %p, hr %#lx\n", msg->punkUser, hr);
2186 return S_OK;
2188 case DMUS_PMSGT_INTERNAL_SEGMENT_END:
2189 if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)msg->punkUser,
2190 (IDirectMusicPerformance8 *)performance)))
2191 WARN("Failed to end segment state %p, hr %#lx\n", msg->punkUser, hr);
2192 break;
2194 default:
2195 FIXME("Unhandled message type %#lx\n", msg->dwType);
2196 break;
2199 return DMUS_S_FREE;
2202 static HRESULT WINAPI performance_tool_Flush(IDirectMusicTool *iface,
2203 IDirectMusicPerformance *performance, DMUS_PMSG *msg, REFERENCE_TIME time)
2205 struct performance *This = impl_from_IDirectMusicTool(iface);
2206 FIXME("(%p, %p, %p, %I64d): stub\n", This, performance, msg, time);
2207 return E_NOTIMPL;
2210 static const IDirectMusicToolVtbl performance_tool_vtbl =
2212 performance_tool_QueryInterface,
2213 performance_tool_AddRef,
2214 performance_tool_Release,
2215 performance_tool_Init,
2216 performance_tool_GetMsgDeliveryType,
2217 performance_tool_GetMediaTypeArraySize,
2218 performance_tool_GetMediaTypes,
2219 performance_tool_ProcessPMsg,
2220 performance_tool_Flush,
2223 /* for ClassFactory */
2224 HRESULT create_dmperformance(REFIID iid, void **ret_iface)
2226 struct performance *obj;
2227 HRESULT hr;
2229 TRACE("(%s, %p)\n", debugstr_guid(iid), ret_iface);
2231 *ret_iface = NULL;
2232 if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
2233 obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl;
2234 obj->IDirectMusicGraph_iface.lpVtbl = &performance_graph_vtbl;
2235 obj->IDirectMusicTool_iface.lpVtbl = &performance_tool_vtbl;
2236 obj->ref = 1;
2238 obj->pDefaultPath = NULL;
2239 InitializeCriticalSection(&obj->safe);
2240 obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe");
2241 wine_rb_init(&obj->channel_blocks, channel_block_compare);
2243 list_init(&obj->messages);
2244 list_init(&obj->notifications);
2246 obj->latency_offset = 50;
2247 obj->dwBumperLength = 50; /* 50 ms default */
2248 obj->dwPrepareTime = 1000; /* 1000 ms default */
2250 hr = IDirectMusicPerformance8_QueryInterface(&obj->IDirectMusicPerformance8_iface, iid, ret_iface);
2251 IDirectMusicPerformance_Release(&obj->IDirectMusicPerformance8_iface);
2252 return hr;
2255 static inline struct performance *unsafe_impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface)
2257 if (iface->lpVtbl != &performance_vtbl) return NULL;
2258 return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface);
2261 HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound)
2263 struct performance *This = unsafe_impl_from_IDirectMusicPerformance8(iface);
2264 if (!This || !(*dsound = This->dsound)) return E_FAIL;
2265 IDirectSound_AddRef(*dsound);
2266 return S_OK;