ddraw: Set dwMaxVertexCount to 2048.
[wine.git] / dlls / dmime / performance.c
blob500bc8b536f67fa62cd37050c83cb23c41902ac0
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;
1654 IDirectMusicObject *dmo;
1655 IDirectSoundBuffer *buffer, *primary_buffer;
1656 DMUS_OBJECTDESC objDesc;
1657 DMUS_PORTPARAMS8 port_params;
1658 HRESULT hr;
1659 WAVEFORMATEX format;
1660 DSBUFFERDESC desc;
1662 FIXME("(%p, %p, %d, %p): semi-stub\n", This, pSourceConfig, fActivate, ret_iface);
1664 if (!ret_iface || !pSourceConfig) return E_POINTER;
1665 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1667 hr = IUnknown_QueryInterface(pSourceConfig, &IID_IDirectMusicObject, (void **)&dmo);
1668 if (FAILED(hr))
1669 return hr;
1671 hr = IDirectMusicObject_GetDescriptor(dmo, &objDesc);
1672 IDirectMusicObject_Release(dmo);
1673 if (FAILED(hr))
1674 return hr;
1676 if (!IsEqualCLSID(&objDesc.guidClass, &CLSID_DirectMusicAudioPathConfig))
1678 ERR("Unexpected object class %s for source config.\n", debugstr_dmguid(&objDesc.guidClass));
1679 return E_INVALIDARG;
1682 hr = path_config_get_audio_path_params(pSourceConfig, &format, &desc, &port_params);
1683 if (FAILED(hr))
1684 return hr;
1686 hr = perf_dmport_create(This, &port_params);
1687 if (FAILED(hr))
1688 return hr;
1690 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &buffer, NULL);
1691 if (FAILED(hr))
1692 return DSERR_BUFFERLOST;
1694 /* Update description for creating primary buffer */
1695 desc.dwFlags |= DSBCAPS_PRIMARYBUFFER;
1696 desc.dwFlags &= ~DSBCAPS_CTRLFX;
1697 desc.dwBufferBytes = 0;
1698 desc.lpwfxFormat = NULL;
1700 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &primary_buffer, NULL);
1701 if (FAILED(hr))
1703 IDirectSoundBuffer_Release(buffer);
1704 return DSERR_BUFFERLOST;
1707 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void **)&pPath);
1708 set_audiopath_perf_pointer(pPath, iface);
1709 set_audiopath_dsound_buffer(pPath, buffer);
1710 set_audiopath_primary_dsound_buffer(pPath, primary_buffer);
1711 TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface);
1713 *ret_iface = pPath;
1714 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1717 static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface,
1718 DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ret_iface)
1720 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1721 IDirectMusicAudioPath *pPath;
1722 DSBUFFERDESC desc;
1723 WAVEFORMATEX format;
1724 DMUS_PORTPARAMS params = {0};
1725 IDirectSoundBuffer *buffer, *primary_buffer;
1726 HRESULT hr = S_OK;
1728 FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This, dwType, pchannel_count, fActivate, ret_iface);
1730 if (!ret_iface) return E_POINTER;
1731 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1733 *ret_iface = NULL;
1735 /* Secondary buffer description */
1736 memset(&format, 0, sizeof(format));
1737 format.wFormatTag = WAVE_FORMAT_PCM;
1738 format.nChannels = 1;
1739 format.nSamplesPerSec = 44000;
1740 format.nAvgBytesPerSec = 44000*2;
1741 format.nBlockAlign = 2;
1742 format.wBitsPerSample = 16;
1743 format.cbSize = 0;
1745 memset(&desc, 0, sizeof(desc));
1746 desc.dwSize = sizeof(desc);
1747 desc.dwFlags = DSBCAPS_CTRLFX | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
1748 desc.dwBufferBytes = DSBSIZE_MIN;
1749 desc.dwReserved = 0;
1750 desc.lpwfxFormat = &format;
1751 desc.guid3DAlgorithm = GUID_NULL;
1753 switch(dwType) {
1754 case DMUS_APATH_DYNAMIC_3D:
1755 desc.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE;
1756 break;
1757 case DMUS_APATH_DYNAMIC_MONO:
1758 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1759 break;
1760 case DMUS_APATH_SHARED_STEREOPLUSREVERB:
1761 /* normally we have to create 2 buffers (one for music other for reverb)
1762 * in this case. See msdn
1764 case DMUS_APATH_DYNAMIC_STEREO:
1765 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1766 format.nChannels = 2;
1767 format.nBlockAlign *= 2;
1768 format.nAvgBytesPerSec *=2;
1769 break;
1770 default:
1771 return E_INVALIDARG;
1774 /* Create a port */
1775 params.dwSize = sizeof(params);
1776 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS | DMUS_PORTPARAMS_AUDIOCHANNELS;
1777 params.dwChannelGroups = (pchannel_count + 15) / 16;
1778 params.dwAudioChannels = format.nChannels;
1779 if (FAILED(hr = perf_dmport_create(This, &params)))
1780 return hr;
1782 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &buffer, NULL);
1783 if (FAILED(hr))
1784 return DSERR_BUFFERLOST;
1786 /* Update description for creating primary buffer */
1787 desc.dwFlags |= DSBCAPS_PRIMARYBUFFER;
1788 desc.dwFlags &= ~DSBCAPS_CTRLFX;
1789 desc.dwBufferBytes = 0;
1790 desc.lpwfxFormat = NULL;
1792 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &primary_buffer, NULL);
1793 if (FAILED(hr)) {
1794 IDirectSoundBuffer_Release(buffer);
1795 return DSERR_BUFFERLOST;
1798 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void**)&pPath);
1799 set_audiopath_perf_pointer(pPath, iface);
1800 set_audiopath_dsound_buffer(pPath, buffer);
1801 set_audiopath_primary_dsound_buffer(pPath, primary_buffer);
1803 *ret_iface = pPath;
1804 TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface);
1805 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1808 static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *audio_path)
1810 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1812 FIXME("(%p, %p): semi-stub\n", This, audio_path);
1814 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1816 if (This->pDefaultPath) IDirectMusicAudioPath_Release(This->pDefaultPath);
1817 if ((This->pDefaultPath = audio_path))
1819 IDirectMusicAudioPath_AddRef(This->pDefaultPath);
1820 set_audiopath_perf_pointer(This->pDefaultPath, iface);
1823 return S_OK;
1826 static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface,
1827 IDirectMusicAudioPath **ret_iface)
1829 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1831 FIXME("(%p, %p): semi-stub (%p)\n", This, ret_iface, This->pDefaultPath);
1833 if (!ret_iface) return E_POINTER;
1834 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1836 if ((*ret_iface = This->pDefaultPath)) IDirectMusicAudioPath_AddRef(*ret_iface);
1838 return S_OK;
1841 static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID,
1842 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
1844 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1846 FIXME("(%p, %s, %ld, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwTrackID, dwGroupBits, dwIndex, mtTime, pmtNext, pParam);
1848 return S_OK;
1851 static const IDirectMusicPerformance8Vtbl performance_vtbl =
1853 performance_QueryInterface,
1854 performance_AddRef,
1855 performance_Release,
1856 performance_Init,
1857 performance_PlaySegment,
1858 performance_Stop,
1859 performance_GetSegmentState,
1860 performance_SetPrepareTime,
1861 performance_GetPrepareTime,
1862 performance_SetBumperLength,
1863 performance_GetBumperLength,
1864 performance_SendPMsg,
1865 performance_MusicToReferenceTime,
1866 performance_ReferenceToMusicTime,
1867 performance_IsPlaying,
1868 performance_GetTime,
1869 performance_AllocPMsg,
1870 performance_FreePMsg,
1871 performance_GetGraph,
1872 performance_SetGraph,
1873 performance_SetNotificationHandle,
1874 performance_GetNotificationPMsg,
1875 performance_AddNotificationType,
1876 performance_RemoveNotificationType,
1877 performance_AddPort,
1878 performance_RemovePort,
1879 performance_AssignPChannelBlock,
1880 performance_AssignPChannel,
1881 performance_PChannelInfo,
1882 performance_DownloadInstrument,
1883 performance_Invalidate,
1884 performance_GetParam,
1885 performance_SetParam,
1886 performance_GetGlobalParam,
1887 performance_SetGlobalParam,
1888 performance_GetLatencyTime,
1889 performance_GetQueueTime,
1890 performance_AdjustTime,
1891 performance_CloseDown,
1892 performance_GetResolvedTime,
1893 performance_MIDIToMusic,
1894 performance_MusicToMIDI,
1895 performance_TimeToRhythm,
1896 performance_RhythmToTime,
1897 performance_InitAudio,
1898 performance_PlaySegmentEx,
1899 performance_StopEx,
1900 performance_ClonePMsg,
1901 performance_CreateAudioPath,
1902 performance_CreateStandardAudioPath,
1903 performance_SetDefaultAudioPath,
1904 performance_GetDefaultAudioPath,
1905 performance_GetParamEx,
1908 static inline struct performance *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface)
1910 return CONTAINING_RECORD(iface, struct performance, IDirectMusicGraph_iface);
1913 static HRESULT WINAPI performance_graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface)
1915 struct performance *This = impl_from_IDirectMusicGraph(iface);
1916 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
1919 static ULONG WINAPI performance_graph_AddRef(IDirectMusicGraph *iface)
1921 struct performance *This = impl_from_IDirectMusicGraph(iface);
1922 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
1925 static ULONG WINAPI performance_graph_Release(IDirectMusicGraph *iface)
1927 struct performance *This = impl_from_IDirectMusicGraph(iface);
1928 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
1931 static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg)
1933 struct performance *This = impl_from_IDirectMusicGraph(iface);
1934 HRESULT hr;
1936 TRACE("(%p, %p)\n", This, msg);
1938 if (!msg) return E_POINTER;
1940 /* FIXME: Implement segment and audio path graphs support */
1941 if (!This->pToolGraph) hr = DMUS_S_LAST_TOOL;
1942 else if (FAILED(hr = IDirectMusicGraph_StampPMsg(This->pToolGraph, msg))) return hr;
1944 if (msg->pGraph)
1946 IDirectMusicTool_Release(msg->pGraph);
1947 msg->pGraph = NULL;
1950 if (hr == DMUS_S_LAST_TOOL)
1952 const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
1953 msg->dwFlags &= ~delivery_flags;
1954 msg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
1956 if (msg->pTool) IDirectMusicTool_Release(msg->pTool);
1957 msg->pTool = &This->IDirectMusicTool_iface;
1958 IDirectMusicTool_AddRef(msg->pTool);
1959 return S_OK;
1962 if (SUCCEEDED(hr))
1964 msg->pGraph = &This->IDirectMusicGraph_iface;
1965 IDirectMusicTool_AddRef(msg->pGraph);
1968 return hr;
1971 static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool,
1972 DWORD *channels, DWORD channels_count, LONG index)
1974 struct performance *This = impl_from_IDirectMusicGraph(iface);
1975 TRACE("(%p, %p, %p, %lu, %ld)\n", This, tool, channels, channels_count, index);
1976 return E_NOTIMPL;
1979 static HRESULT WINAPI performance_graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **tool)
1981 struct performance *This = impl_from_IDirectMusicGraph(iface);
1982 TRACE("(%p, %lu, %p)\n", This, index, tool);
1983 return E_NOTIMPL;
1986 static HRESULT WINAPI performance_graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool)
1988 struct performance *This = impl_from_IDirectMusicGraph(iface);
1989 TRACE("(%p, %p)\n", This, tool);
1990 return E_NOTIMPL;
1993 static const IDirectMusicGraphVtbl performance_graph_vtbl =
1995 performance_graph_QueryInterface,
1996 performance_graph_AddRef,
1997 performance_graph_Release,
1998 performance_graph_StampPMsg,
1999 performance_graph_InsertTool,
2000 performance_graph_GetTool,
2001 performance_graph_RemoveTool,
2004 static inline struct performance *impl_from_IDirectMusicTool(IDirectMusicTool *iface)
2006 return CONTAINING_RECORD(iface, struct performance, IDirectMusicTool_iface);
2009 static HRESULT WINAPI performance_tool_QueryInterface(IDirectMusicTool *iface, REFIID riid, void **ret_iface)
2011 struct performance *This = impl_from_IDirectMusicTool(iface);
2012 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
2015 static ULONG WINAPI performance_tool_AddRef(IDirectMusicTool *iface)
2017 struct performance *This = impl_from_IDirectMusicTool(iface);
2018 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
2021 static ULONG WINAPI performance_tool_Release(IDirectMusicTool *iface)
2023 struct performance *This = impl_from_IDirectMusicTool(iface);
2024 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
2027 static HRESULT WINAPI performance_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph)
2029 struct performance *This = impl_from_IDirectMusicTool(iface);
2030 TRACE("(%p, %p)\n", This, graph);
2031 return E_NOTIMPL;
2034 static HRESULT WINAPI performance_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type)
2036 struct performance *This = impl_from_IDirectMusicTool(iface);
2037 TRACE("(%p, %p)\n", This, type);
2038 *type = DMUS_PMSGF_TOOL_IMMEDIATE;
2039 return S_OK;
2042 static HRESULT WINAPI performance_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size)
2044 struct performance *This = impl_from_IDirectMusicTool(iface);
2045 TRACE("(%p, %p)\n", This, size);
2046 *size = 0;
2047 return S_OK;
2050 static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size)
2052 struct performance *This = impl_from_IDirectMusicTool(iface);
2053 TRACE("(%p, %p, %lu)\n", This, types, size);
2054 return E_NOTIMPL;
2057 static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface,
2058 IDirectMusicPerformance *performance, DMUS_PMSG *msg)
2060 struct performance *This = impl_from_IDirectMusicTool(iface);
2061 struct message *message = message_from_DMUS_PMSG(msg);
2062 HRESULT hr;
2064 TRACE("(%p, %p, %p)\n", This, performance, msg);
2066 switch (msg->dwType)
2068 case DMUS_PMSGT_MIDI:
2070 static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD);
2071 DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size};
2072 DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg;
2073 REFERENCE_TIME latency_time;
2074 IDirectMusicBuffer *buffer;
2075 IDirectMusicPort *port;
2076 DWORD group, channel;
2077 UINT value = 0;
2079 if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(performance, msg->dwPChannel,
2080 &port, &group, &channel)))
2082 WARN("Failed to get message port, hr %#lx\n", hr);
2083 return DMUS_S_FREE;
2085 performance_update_latency_time(This, port, &latency_time);
2087 value |= channel;
2088 value |= (UINT)midi->bStatus;
2089 value |= (UINT)midi->bByte1 << 8;
2090 value |= (UINT)midi->bByte2 << 16;
2092 if (SUCCEEDED(hr = IDirectMusic_CreateMusicBuffer(This->dmusic, &desc, &buffer, NULL)))
2094 if (msg->rtTime == -1) msg->rtTime = latency_time;
2095 hr = IDirectMusicBuffer_PackStructured(buffer, msg->rtTime, group, value);
2096 if (SUCCEEDED(hr)) hr = IDirectMusicPort_PlayBuffer(port, buffer);
2097 IDirectMusicBuffer_Release(buffer);
2100 IDirectMusicPort_Release(port);
2101 break;
2104 case DMUS_PMSGT_NOTE:
2106 DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg;
2108 msg->mtTime += note->nOffset;
2110 if (note->bFlags & DMUS_NOTEF_NOTEON)
2112 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2113 MIDI_NOTE_ON, note->bMidiValue, note->bVelocity)))
2114 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2116 if (note->mtDuration)
2118 msg->mtTime -= note->nOffset;
2119 msg->mtTime += max(1, note->mtDuration - 1);
2120 if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(performance, msg->mtTime, &msg->rtTime)))
2121 return hr;
2122 note->bFlags &= ~DMUS_NOTEF_NOTEON;
2123 return DMUS_S_REQUEUE;
2127 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2128 MIDI_NOTE_OFF, note->bMidiValue, 0)))
2129 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2131 break;
2134 case DMUS_PMSGT_CURVE:
2136 DMUS_CURVE_PMSG *curve = (DMUS_CURVE_PMSG *)msg;
2138 msg->mtTime += curve->nOffset;
2139 switch (curve->bType)
2141 case DMUS_CURVET_CCCURVE:
2142 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2143 MIDI_CONTROL_CHANGE, curve->bCCData, curve->nStartValue)))
2144 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2145 break;
2146 case DMUS_CURVET_RPNCURVE:
2147 case DMUS_CURVET_NRPNCURVE:
2148 case DMUS_CURVET_MATCURVE:
2149 case DMUS_CURVET_PATCURVE:
2150 case DMUS_CURVET_PBCURVE:
2151 FIXME("Unhandled curve type %#x\n", curve->bType);
2152 break;
2153 default:
2154 WARN("Invalid curve type %#x\n", curve->bType);
2155 break;
2158 break;
2161 case DMUS_PMSGT_PATCH:
2163 DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg;
2165 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2166 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_MSB, patch->byMSB)))
2167 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2169 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2170 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_LSB, patch->byLSB)))
2171 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2173 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
2174 MIDI_PROGRAM_CHANGE, patch->byInstrument, 0)))
2175 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
2177 break;
2180 case DMUS_PMSGT_NOTIFICATION:
2182 DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg;
2183 struct message *previous;
2184 BOOL enabled = FALSE;
2186 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE))
2187 enabled = This->notification_performance;
2188 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_SEGMENT))
2189 enabled = This->notification_segment;
2190 if (!enabled) return DMUS_S_FREE;
2192 if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE)
2194 /* re-send the message for queueing at the expected time */
2195 msg->dwFlags &= ~DMUS_PMSGF_TOOL_IMMEDIATE;
2196 msg->dwFlags |= DMUS_PMSGF_TOOL_ATTIME;
2198 if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg)))
2200 ERR("Failed to send notification message, hr %#lx\n", hr);
2201 return DMUS_S_FREE;
2204 return S_OK;
2207 list_add_tail(&This->notifications, &message->entry);
2209 /* discard old notification messages */
2212 previous = LIST_ENTRY(list_head(&This->notifications), struct message, entry);
2213 if (This->notification_timeout <= 0) break; /* negative values may be used to keep everything */
2214 if (message->msg.rtTime - previous->msg.rtTime <= This->notification_timeout) break;
2215 list_remove(&previous->entry);
2216 list_init(&previous->entry);
2217 } while (SUCCEEDED(hr = IDirectMusicPerformance_FreePMsg(performance, &previous->msg)));
2219 SetEvent(This->notification_event);
2220 return S_OK;
2223 case DMUS_PMSGT_WAVE:
2224 if (FAILED(hr = IDirectSoundBuffer_Play((IDirectSoundBuffer *)msg->punkUser, 0, 0, 0)))
2225 WARN("Failed to play wave buffer, hr %#lx\n", hr);
2226 break;
2228 case DMUS_PMSGT_INTERNAL_SEGMENT_TICK:
2229 msg->rtTime += 10000000;
2230 msg->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
2232 /* re-send the tick message until segment_state_tick returns S_FALSE */
2233 if (FAILED(hr = segment_state_tick((IDirectMusicSegmentState *)msg->punkUser,
2234 (IDirectMusicPerformance8 *)performance)))
2235 ERR("Failed to tick segment state %p, hr %#lx\n", msg->punkUser, hr);
2236 else if (hr == S_FALSE)
2237 return DMUS_S_FREE; /* done ticking */
2238 else if (FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, msg)))
2239 ERR("Failed to queue tick for segment state %p, hr %#lx\n", msg->punkUser, hr);
2241 return S_OK;
2243 case DMUS_PMSGT_INTERNAL_SEGMENT_END:
2244 if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)msg->punkUser,
2245 (IDirectMusicPerformance8 *)performance)))
2246 WARN("Failed to end segment state %p, hr %#lx\n", msg->punkUser, hr);
2247 break;
2249 default:
2250 FIXME("Unhandled message type %#lx\n", msg->dwType);
2251 break;
2254 return DMUS_S_FREE;
2257 static HRESULT WINAPI performance_tool_Flush(IDirectMusicTool *iface,
2258 IDirectMusicPerformance *performance, DMUS_PMSG *msg, REFERENCE_TIME time)
2260 struct performance *This = impl_from_IDirectMusicTool(iface);
2261 FIXME("(%p, %p, %p, %I64d): stub\n", This, performance, msg, time);
2262 return E_NOTIMPL;
2265 static const IDirectMusicToolVtbl performance_tool_vtbl =
2267 performance_tool_QueryInterface,
2268 performance_tool_AddRef,
2269 performance_tool_Release,
2270 performance_tool_Init,
2271 performance_tool_GetMsgDeliveryType,
2272 performance_tool_GetMediaTypeArraySize,
2273 performance_tool_GetMediaTypes,
2274 performance_tool_ProcessPMsg,
2275 performance_tool_Flush,
2278 /* for ClassFactory */
2279 HRESULT create_dmperformance(REFIID iid, void **ret_iface)
2281 struct performance *obj;
2282 HRESULT hr;
2284 TRACE("(%s, %p)\n", debugstr_guid(iid), ret_iface);
2286 *ret_iface = NULL;
2287 if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
2288 obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl;
2289 obj->IDirectMusicGraph_iface.lpVtbl = &performance_graph_vtbl;
2290 obj->IDirectMusicTool_iface.lpVtbl = &performance_tool_vtbl;
2291 obj->ref = 1;
2293 obj->pDefaultPath = NULL;
2294 InitializeCriticalSectionEx(&obj->safe, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
2295 obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe");
2296 wine_rb_init(&obj->channel_blocks, channel_block_compare);
2298 list_init(&obj->messages);
2299 list_init(&obj->notifications);
2301 obj->latency_offset = 50;
2302 obj->dwBumperLength = 50; /* 50 ms default */
2303 obj->dwPrepareTime = 1000; /* 1000 ms default */
2305 hr = IDirectMusicPerformance8_QueryInterface(&obj->IDirectMusicPerformance8_iface, iid, ret_iface);
2306 IDirectMusicPerformance_Release(&obj->IDirectMusicPerformance8_iface);
2307 return hr;
2310 static inline struct performance *unsafe_impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface)
2312 if (iface->lpVtbl != &performance_vtbl) return NULL;
2313 return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface);
2316 HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound)
2318 struct performance *This = unsafe_impl_from_IDirectMusicPerformance8(iface);
2319 if (!This || !(*dsound = This->dsound)) return E_FAIL;
2320 IDirectSound_AddRef(*dsound);
2321 return S_OK;