dmime: Implement IDirectMusicPerformance_GetSegmentState semi-stub.
[wine.git] / dlls / dmime / performance.c
blob17fbe772f5bbc02963eea1e9d8f3e7c13d15fb48
1 /* IDirectMusicPerformance Implementation
3 * Copyright (C) 2003-2004 Rok Mandeljc
4 * Copyright (C) 2003-2004 Raphael Junqueira
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "dmime_private.h"
22 #include "dmusic_midi.h"
23 #include "wine/rbtree.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(dmime);
27 enum dmus_internal_message_type
29 DMUS_PMSGT_INTERNAL_FIRST = 0x10,
30 DMUS_PMSGT_INTERNAL_SEGMENT_END = DMUS_PMSGT_INTERNAL_FIRST,
31 DMUS_PMSGT_INTERNAL_SEGMENT_TICK,
34 struct pchannel_block {
35 DWORD block_num; /* Block 0 is PChannels 0-15, Block 1 is PChannels 16-31, etc */
36 struct {
37 DWORD channel; /* MIDI channel */
38 DWORD group; /* MIDI group */
39 IDirectMusicPort *port;
40 } pchannel[16];
41 struct wine_rb_entry entry;
44 struct performance
46 IDirectMusicPerformance8 IDirectMusicPerformance8_iface;
47 IDirectMusicGraph IDirectMusicGraph_iface;
48 IDirectMusicTool IDirectMusicTool_iface;
49 LONG ref;
50 IDirectMusic *dmusic;
51 IDirectSound *dsound;
52 IDirectMusicGraph *pToolGraph;
53 BOOL fAutoDownload;
54 char cMasterGrooveLevel;
55 float fMasterTempo;
56 long lMasterVolume;
57 /* performance channels */
58 struct wine_rb_tree pchannels;
60 BOOL audio_paths_enabled;
61 IDirectMusicAudioPath *pDefaultPath;
62 REFERENCE_TIME latency_offset;
63 DWORD dwBumperLength;
64 DWORD dwPrepareTime;
66 HANDLE message_thread;
67 CRITICAL_SECTION safe;
68 CONDITION_VARIABLE cond;
70 IReferenceClock *master_clock;
71 REFERENCE_TIME init_time;
72 struct list messages;
74 struct list notifications;
75 REFERENCE_TIME notification_timeout;
76 HANDLE notification_event;
77 BOOL notification_performance;
78 BOOL notification_segment;
80 IDirectMusicSegment *primary_segment;
83 struct message
85 struct list entry;
86 DMUS_PMSG msg;
89 static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg)
91 return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL;
94 static void performance_queue_message(struct performance *This, struct message *message, struct list *hint)
96 struct message *prev;
98 LIST_FOR_EACH_ENTRY_REV(prev, hint ? hint : &This->messages, struct message, entry)
100 if (&prev->entry == &This->messages) break;
101 if (prev->msg.rtTime <= message->msg.rtTime) break;
104 list_add_after(&prev->entry, &message->entry);
107 static HRESULT performance_process_message(struct performance *This, DMUS_PMSG *msg, DWORD *timeout)
109 static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
110 IDirectMusicPerformance *performance = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface;
111 HRESULT hr;
115 REFERENCE_TIME latency, offset = 0;
116 IDirectMusicTool *tool;
118 if (FAILED(hr = IDirectMusicPerformance_GetLatencyTime(performance, &latency))) return hr;
119 if (!(tool = msg->pTool)) tool = &This->IDirectMusicTool_iface;
121 switch (msg->dwFlags & delivery_flags)
123 default:
124 WARN("No delivery flag found for message %p\n", msg);
125 /* fallthrough */
126 case DMUS_PMSGF_TOOL_IMMEDIATE:
127 hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg);
128 break;
129 case DMUS_PMSGF_TOOL_QUEUE:
130 offset = This->dwBumperLength * 10000;
131 /* fallthrough */
132 case DMUS_PMSGF_TOOL_ATTIME:
133 if (msg->rtTime >= offset && msg->rtTime - offset >= latency)
135 if (timeout) *timeout = (msg->rtTime - offset - latency) / 10000;
136 return DMUS_S_REQUEUE;
139 hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg);
140 break;
142 } while (hr == DMUS_S_REQUEUE);
144 if (hr == DMUS_S_FREE) hr = IDirectMusicPerformance_FreePMsg(performance, msg);
145 if (FAILED(hr)) WARN("Failed to process message, hr %#lx\n", hr);
146 return hr;
149 static DWORD WINAPI message_thread_proc(void *args)
151 struct performance *This = args;
152 HRESULT hr = DMUS_S_REQUEUE;
153 struct list *ptr;
155 TRACE("performance %p message thread\n", This);
156 SetThreadDescription(GetCurrentThread(), L"wine_dmime_message");
158 EnterCriticalSection(&This->safe);
160 while (This->message_thread)
162 DWORD timeout = INFINITE;
164 while ((ptr = list_head(&This->messages)))
166 struct message *message = LIST_ENTRY(ptr, struct message, entry);
167 struct list *next = ptr->next;
168 list_remove(&message->entry);
169 list_init(&message->entry);
171 hr = performance_process_message(This, &message->msg, &timeout);
172 if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message, next);
173 if (hr != S_OK) break;
176 SleepConditionVariableCS(&This->cond, &This->safe, timeout);
179 LeaveCriticalSection(&This->safe);
181 TRACE("(%p): Exiting\n", This);
182 return 0;
185 static HRESULT performance_send_pmsg(struct performance *This, MUSIC_TIME music_time, DWORD flags,
186 DWORD type, IUnknown *object)
188 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
189 IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface;
190 DMUS_PMSG *msg;
191 HRESULT hr;
193 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), &msg)))
194 return hr;
196 msg->mtTime = music_time;
197 msg->dwFlags = DMUS_PMSGF_MUSICTIME | flags;
198 msg->dwType = type;
199 if ((msg->punkUser = object)) IUnknown_AddRef(object);
201 if ((type < DMUS_PMSGT_INTERNAL_FIRST && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, msg)))
202 || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg)))
203 IDirectMusicPerformance8_FreePMsg(performance, msg);
205 return hr;
208 static HRESULT performance_send_notification_pmsg(struct performance *This, MUSIC_TIME music_time, BOOL stamp,
209 GUID type, DWORD option, IUnknown *object)
211 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
212 IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface;
213 DMUS_NOTIFICATION_PMSG *msg;
214 HRESULT hr;
216 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg)))
217 return hr;
219 msg->mtTime = music_time;
220 msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE;
221 msg->dwType = DMUS_PMSGT_NOTIFICATION;
222 if ((msg->punkUser = object)) IUnknown_AddRef(object);
223 msg->guidNotificationType = type;
224 msg->dwNotificationOption = option;
226 /* only stamp the message if notifications are enabled, otherwise send them directly to the output tool */
227 if ((stamp && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)))
228 || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)msg)))
229 IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)msg);
231 return hr;
234 static int pchannel_block_compare(const void *key, const struct wine_rb_entry *entry)
236 const struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct pchannel_block, entry);
238 return *(DWORD *)key - b->block_num;
241 static void pchannel_block_free(struct wine_rb_entry *entry, void *context)
243 struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry);
245 free(b);
248 static struct pchannel_block *pchannel_block_set(struct wine_rb_tree *tree, DWORD block_num,
249 IDirectMusicPort *port, DWORD group, BOOL only_set_new)
251 struct pchannel_block *block;
252 struct wine_rb_entry *entry;
253 unsigned int i;
255 entry = wine_rb_get(tree, &block_num);
256 if (entry) {
257 block = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry);
258 if (only_set_new)
259 return block;
260 } else {
261 if (!(block = malloc(sizeof(*block)))) return NULL;
262 block->block_num = block_num;
265 for (i = 0; i < 16; ++i) {
266 block->pchannel[i].port = port;
267 block->pchannel[i].group = group;
268 block->pchannel[i].channel = i;
270 if (!entry)
271 wine_rb_put(tree, &block->block_num, &block->entry);
273 return block;
276 static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface)
278 return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface);
281 HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time,
282 IDirectMusicSegmentState *state)
284 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
285 HRESULT hr;
287 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance,
288 GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL)))
289 return hr;
290 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment,
291 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state)))
292 return hr;
293 if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE,
294 DMUS_PMSGT_DIRTY, NULL)))
295 return hr;
297 return S_OK;
300 HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time,
301 IDirectMusicSegmentState *state)
303 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
304 REFERENCE_TIME time;
305 HRESULT hr;
307 if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface, music_time, &time)))
308 return hr;
309 if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, time + 2000000, &music_time)))
310 return hr;
311 if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_QUEUE,
312 DMUS_PMSGT_INTERNAL_SEGMENT_TICK, (IUnknown *)state)))
313 return hr;
315 return S_OK;
318 HRESULT performance_send_segment_end(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 - 1450, This->notification_segment,
325 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state)))
326 return hr;
327 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment,
328 GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (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;
333 if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance,
334 GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL)))
335 return hr;
336 if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_ATTIME,
337 DMUS_PMSGT_INTERNAL_SEGMENT_END, (IUnknown *)state)))
338 return hr;
340 return S_OK;
343 static void performance_set_primary_segment(struct performance *This, IDirectMusicSegment *segment)
345 if (This->primary_segment) IDirectMusicSegment_Release(This->primary_segment);
346 if ((This->primary_segment = segment)) IDirectMusicSegment_AddRef(This->primary_segment);
349 /* IDirectMusicPerformance8 IUnknown part: */
350 static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface)
352 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
354 TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface);
356 if (IsEqualGUID(riid, &IID_IUnknown)
357 || IsEqualGUID(riid, &IID_IDirectMusicPerformance)
358 || IsEqualGUID(riid, &IID_IDirectMusicPerformance2)
359 || IsEqualGUID(riid, &IID_IDirectMusicPerformance8))
361 *ret_iface = iface;
362 IUnknown_AddRef(iface);
363 return S_OK;
366 if (IsEqualGUID(riid, &IID_IDirectMusicGraph))
368 *ret_iface = &This->IDirectMusicGraph_iface;
369 IDirectMusicGraph_AddRef(&This->IDirectMusicGraph_iface);
370 return S_OK;
373 if (IsEqualGUID(riid, &IID_IDirectMusicTool))
375 *ret_iface = &This->IDirectMusicTool_iface;
376 IDirectMusicTool_AddRef(&This->IDirectMusicTool_iface);
377 return S_OK;
380 *ret_iface = NULL;
381 WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface);
382 return E_NOINTERFACE;
385 static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface)
387 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
388 ULONG ref = InterlockedIncrement(&This->ref);
390 TRACE("(%p): ref=%ld\n", This, ref);
392 return ref;
395 static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface)
397 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
398 ULONG ref = InterlockedDecrement(&This->ref);
400 TRACE("(%p): ref=%ld\n", This, ref);
402 if (ref == 0) {
403 wine_rb_destroy(&This->pchannels, pchannel_block_free, NULL);
404 This->safe.DebugInfo->Spare[0] = 0;
405 DeleteCriticalSection(&This->safe);
406 free(This);
409 return ref;
412 static HRESULT performance_init_dsound(struct performance *This, HWND hwnd)
414 IDirectSound *dsound;
415 HRESULT hr;
417 if (FAILED(hr = DirectSoundCreate(NULL, &dsound, NULL))) return hr;
419 if (!hwnd) hwnd = GetForegroundWindow();
420 hr = IDirectSound_SetCooperativeLevel(dsound, hwnd, DSSCL_PRIORITY);
422 if (SUCCEEDED(hr)) This->dsound = dsound;
423 else IDirectSound_Release(dsound);
425 return hr;
428 static HRESULT performance_init_dmusic(struct performance *This, IDirectSound *dsound)
430 IDirectMusic *dmusic;
431 HRESULT hr;
433 if (FAILED(hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER,
434 &IID_IDirectMusic8, (void **)&dmusic)))
435 return hr;
437 hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL);
439 if (SUCCEEDED(hr)) This->dmusic = dmusic;
440 else IDirectSound_Release(dmusic);
442 return hr;
445 static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic,
446 IDirectSound *dsound, HWND hwnd)
448 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
449 HRESULT hr;
451 TRACE("(%p, %p, %p, %p)\n", iface, dmusic, dsound, hwnd);
453 if (This->dmusic) return DMUS_E_ALREADY_INITED;
455 if ((This->dsound = dsound)) IDirectMusic8_AddRef(This->dsound);
456 else if (FAILED(hr = performance_init_dsound(This, hwnd))) return hr;
458 if (dmusic && (This->dmusic = *dmusic)) IDirectMusic_AddRef(This->dmusic);
459 else if (FAILED(hr = performance_init_dmusic(This, This->dsound)))
461 IDirectMusicPerformance_CloseDown(iface);
462 return hr;
465 if (FAILED(hr = IDirectMusic_GetMasterClock(This->dmusic, NULL, &This->master_clock))
466 || FAILED(hr = IDirectMusicPerformance8_GetTime(iface, &This->init_time, NULL)))
468 IDirectMusicPerformance_CloseDown(iface);
469 return hr;
472 if (!(This->message_thread = CreateThread(NULL, 0, message_thread_proc, This, 0, NULL)))
474 ERR("Failed to start performance message thread, error %lu\n", GetLastError());
475 IDirectMusicPerformance_CloseDown(iface);
476 return HRESULT_FROM_WIN32(GetLastError());
479 if (dmusic && !*dmusic)
481 *dmusic = This->dmusic;
482 IDirectMusic_AddRef(*dmusic);
484 return S_OK;
487 static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment,
488 DWORD segment_flags, INT64 start_time, IDirectMusicSegmentState **ret_state)
490 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
492 TRACE("(%p, %p, %ld, %I64d, %p)\n", This, segment, segment_flags, start_time, ret_state);
494 return IDirectMusicPerformance8_PlaySegmentEx(iface, (IUnknown *)segment, NULL, NULL,
495 segment_flags, start_time, ret_state, NULL, NULL);
498 struct state_entry
500 struct list entry;
501 IDirectMusicSegmentState *state;
504 static void state_entry_destroy(struct state_entry *entry)
506 list_remove(&entry->entry);
507 IDirectMusicSegmentState_Release(entry->state);
508 free(entry);
511 static void enum_segment_states(struct performance *This, IDirectMusicSegment *segment, struct list *list)
513 struct state_entry *entry;
514 struct message *message;
516 LIST_FOR_EACH_ENTRY(message, &This->messages, struct message, entry)
518 IDirectMusicSegmentState *message_state;
520 if (message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_TICK
521 && message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_END)
522 continue;
524 message_state = (IDirectMusicSegmentState *)message->msg.punkUser;
525 if (segment && !segment_state_has_segment(message_state, segment)) continue;
527 if (!(entry = malloc(sizeof(*entry)))) return;
528 entry->state = message_state;
529 IDirectMusicSegmentState_AddRef(entry->state);
530 list_add_tail(list, &entry->entry);
534 static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment,
535 IDirectMusicSegmentState *pSegmentState, MUSIC_TIME mtTime, DWORD dwFlags)
537 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
539 FIXME("(%p, %p, %p, %ld, %ld): stub\n", This, pSegment, pSegmentState, mtTime, dwFlags);
540 return S_OK;
543 static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface,
544 IDirectMusicSegmentState **state, MUSIC_TIME time)
546 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
547 struct list *ptr, states = LIST_INIT(states);
548 struct state_entry *entry, *next;
549 HRESULT hr = S_OK;
551 TRACE("(%p, %p, %ld)\n", This, state, time);
553 if (!state) return E_POINTER;
555 EnterCriticalSection(&This->safe);
557 enum_segment_states(This, This->primary_segment, &states);
559 if (!(ptr = list_head(&states))) hr = DMUS_E_NOT_FOUND;
560 else
562 entry = LIST_ENTRY(ptr, struct state_entry, entry);
564 *state = entry->state;
565 IDirectMusicSegmentState_AddRef(entry->state);
567 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry)
568 state_entry_destroy(entry);
571 LeaveCriticalSection(&This->safe);
573 return hr;
576 static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds)
578 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
580 TRACE("(%p, %ld)\n", This, dwMilliSeconds);
581 This->dwPrepareTime = dwMilliSeconds;
582 return S_OK;
585 static HRESULT WINAPI performance_GetPrepareTime(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds)
587 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
589 TRACE("(%p, %p)\n", This, pdwMilliSeconds);
590 if (NULL == pdwMilliSeconds) {
591 return E_POINTER;
593 *pdwMilliSeconds = This->dwPrepareTime;
594 return S_OK;
597 static HRESULT WINAPI performance_SetBumperLength(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds)
599 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
601 TRACE("(%p, %ld)\n", This, dwMilliSeconds);
602 This->dwBumperLength = dwMilliSeconds;
603 return S_OK;
606 static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds)
608 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
610 TRACE("(%p, %p)\n", This, pdwMilliSeconds);
611 if (NULL == pdwMilliSeconds) {
612 return E_POINTER;
614 *pdwMilliSeconds = This->dwBumperLength;
615 return S_OK;
618 static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg)
620 const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
621 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
622 struct message *message;
623 HRESULT hr;
625 TRACE("(%p, %p)\n", This, msg);
627 if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER;
628 if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK;
629 if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG;
631 EnterCriticalSection(&This->safe);
633 if (!list_empty(&message->entry))
634 hr = DMUS_E_ALREADY_SENT;
635 else
637 if (!(msg->dwFlags & delivery_flags)) msg->dwFlags |= DMUS_PMSGF_TOOL_IMMEDIATE;
638 if (!(msg->dwFlags & DMUS_PMSGF_MUSICTIME))
640 if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface,
641 msg->rtTime, &msg->mtTime)))
642 goto done;
643 msg->dwFlags |= DMUS_PMSGF_MUSICTIME;
645 if (!(msg->dwFlags & DMUS_PMSGF_REFTIME))
647 if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface,
648 msg->mtTime == -1 ? 0 : msg->mtTime, &msg->rtTime)))
649 goto done;
650 msg->dwFlags |= DMUS_PMSGF_REFTIME;
653 if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE)
655 hr = performance_process_message(This, &message->msg, NULL);
656 if (hr != DMUS_S_REQUEUE) goto done;
659 performance_queue_message(This, message, NULL);
660 hr = S_OK;
663 done:
664 LeaveCriticalSection(&This->safe);
665 if (SUCCEEDED(hr)) WakeConditionVariable(&This->cond);
667 return hr;
670 static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface,
671 MUSIC_TIME music_time, REFERENCE_TIME *time)
673 static int once;
674 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
676 if (!once++) FIXME("(%p, %ld, %p): semi-stub\n", This, music_time, time);
677 else TRACE("(%p, %ld, %p)\n", This, music_time, time);
679 if (!time) return E_POINTER;
680 *time = 0;
682 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
684 /* FIXME: This should be (music_time * 60) / (DMUS_PPQ * tempo)
685 * but it gives innacurate results */
686 *time = This->init_time + (music_time * 6510);
688 return S_OK;
691 static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface,
692 REFERENCE_TIME time, MUSIC_TIME *music_time)
694 static int once;
695 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
697 if (!once++) FIXME("(%p, %I64d, %p): semi-stub\n", This, time, music_time);
698 else TRACE("(%p, %I64d, %p)\n", This, time, music_time);
700 if (!music_time) return E_POINTER;
701 *music_time = 0;
703 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
705 /* FIXME: This should be (time * DMUS_PPQ * tempo) / 60
706 * but it gives innacurate results */
707 *music_time = (time - This->init_time) / 6510;
709 return S_OK;
712 static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface,
713 IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegState)
715 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
717 FIXME("(%p, %p, %p): stub\n", This, pSegment, pSegState);
718 return S_FALSE;
721 static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *time, MUSIC_TIME *music_time)
723 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
724 REFERENCE_TIME now;
725 HRESULT hr;
727 TRACE("(%p, %p, %p)\n", iface, time, music_time);
729 if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK;
730 if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &now))) return hr;
732 if (time) *time = now;
733 if (music_time) hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, now, music_time);
735 return hr;
738 static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG size, DMUS_PMSG **msg)
740 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
741 struct message *message;
743 TRACE("(%p, %ld, %p)\n", This, size, msg);
745 if (!msg) return E_POINTER;
746 if (size < sizeof(DMUS_PMSG)) return E_INVALIDARG;
748 if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(struct message)))) return E_OUTOFMEMORY;
749 message->msg.dwSize = size;
750 list_init(&message->entry);
751 *msg = &message->msg;
753 return S_OK;
756 static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg)
758 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
759 struct message *message;
760 HRESULT hr;
762 TRACE("(%p, %p)\n", This, msg);
764 if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER;
766 EnterCriticalSection(&This->safe);
767 hr = !list_empty(&message->entry) ? DMUS_E_CANNOT_FREE : S_OK;
768 LeaveCriticalSection(&This->safe);
770 if (SUCCEEDED(hr))
772 if (msg->pTool) IDirectMusicTool_Release(msg->pTool);
773 if (msg->pGraph) IDirectMusicGraph_Release(msg->pGraph);
774 if (msg->punkUser) IUnknown_Release(msg->punkUser);
775 free(message);
778 return hr;
781 static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph **graph)
783 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
785 TRACE("(%p, %p)\n", This, graph);
787 if (!graph)
788 return E_POINTER;
790 *graph = This->pToolGraph;
791 if (This->pToolGraph) {
792 IDirectMusicGraph_AddRef(*graph);
795 return *graph ? S_OK : DMUS_E_NOT_FOUND;
798 static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph *pGraph)
800 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
802 FIXME("(%p, %p): to check\n", This, pGraph);
804 if (NULL != This->pToolGraph) {
805 /* Todo clean buffers and tools before */
806 IDirectMusicGraph_Release(This->pToolGraph);
808 This->pToolGraph = pGraph;
809 if (NULL != This->pToolGraph) {
810 IDirectMusicGraph_AddRef(This->pToolGraph);
812 return S_OK;
815 static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 *iface,
816 HANDLE notification_event, REFERENCE_TIME minimum_time)
818 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
820 TRACE("(%p, %p, %I64d)\n", This, notification_event, minimum_time);
822 This->notification_event = notification_event;
823 if (minimum_time)
824 This->notification_timeout = minimum_time;
825 else if (!This->notification_timeout)
826 This->notification_timeout = 20000000; /* 2 seconds */
828 return S_OK;
831 static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 *iface,
832 DMUS_NOTIFICATION_PMSG **ret_msg)
834 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
835 struct list *entry;
837 TRACE("(%p, %p)\n", This, ret_msg);
839 if (!ret_msg) return E_POINTER;
841 EnterCriticalSection(&This->safe);
842 if ((entry = list_head(&This->notifications)))
844 struct message *message = LIST_ENTRY(entry, struct message, entry);
845 list_remove(&message->entry);
846 list_init(&message->entry);
847 *ret_msg = (DMUS_NOTIFICATION_PMSG *)&message->msg;
849 LeaveCriticalSection(&This->safe);
851 return entry ? S_OK : S_FALSE;
854 static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID type)
856 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
857 HRESULT hr = S_OK;
859 FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type));
861 if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE))
863 hr = This->notification_performance ? S_FALSE : S_OK;
864 This->notification_performance = TRUE;
866 if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT))
868 hr = This->notification_segment ? S_FALSE : S_OK;
869 This->notification_segment = TRUE;
872 return hr;
875 static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID type)
877 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
878 HRESULT hr = S_FALSE;
880 FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type));
882 if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE))
884 hr = This->notification_performance ? S_OK : S_FALSE;
885 This->notification_performance = FALSE;
887 if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT))
889 hr = This->notification_segment ? S_OK : S_FALSE;
890 This->notification_segment = FALSE;
893 return hr;
896 static void performance_update_latency_time(struct performance *This, IDirectMusicPort *port,
897 REFERENCE_TIME *ret_time)
899 IDirectMusicPerformance8 *iface = &This->IDirectMusicPerformance8_iface;
900 REFERENCE_TIME latency_time, current_time;
901 IReferenceClock *latency_clock;
902 HRESULT hr;
904 if (!ret_time) ret_time = &latency_time;
905 if (SUCCEEDED(hr = IDirectMusicPort_GetLatencyClock(port, &latency_clock)))
907 hr = IReferenceClock_GetTime(latency_clock, ret_time);
908 if (SUCCEEDED(hr)) hr = IDirectMusicPerformance8_GetTime(iface, &current_time, NULL);
909 if (SUCCEEDED(hr) && This->latency_offset < (*ret_time - current_time))
911 TRACE("Updating performance %p latency %I64d -> %I64d\n", This,
912 This->latency_offset, *ret_time - current_time);
913 This->latency_offset = *ret_time - current_time;
915 IReferenceClock_Release(latency_clock);
918 if (FAILED(hr)) ERR("Failed to update performance %p latency, hr %#lx\n", This, hr);
921 static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params)
923 IDirectMusicPort *port;
924 GUID guid;
925 unsigned int i;
926 HRESULT hr;
928 if (FAILED(hr = IDirectMusic8_GetDefaultPort(perf->dmusic, &guid)))
929 return hr;
931 if (FAILED(hr = IDirectMusic8_CreatePort(perf->dmusic, &guid, params, &port, NULL)))
932 return hr;
934 if (FAILED(hr = IDirectMusicPort_SetDirectSound(port, perf->dsound, NULL))
935 || FAILED(hr = IDirectMusicPort_Activate(port, TRUE)))
937 IDirectMusicPort_Release(port);
938 return hr;
941 for (i = 0; i < params->dwChannelGroups; i++)
942 pchannel_block_set(&perf->pchannels, i, port, i + 1, FALSE);
944 performance_update_latency_time(perf, port, NULL);
945 return S_OK;
948 static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDirectMusicPort *port)
950 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
952 FIXME("(%p, %p): semi-stub\n", This, port);
954 if (!This->dmusic) return DMUS_E_NOT_INIT;
955 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
957 if (!port) {
958 DMUS_PORTPARAMS params = {
959 .dwSize = sizeof(params),
960 .dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS,
961 .dwChannelGroups = 1
964 return perf_dmport_create(This, &params);
967 IDirectMusicPort_AddRef(port);
969 * We should remember added Ports (for example using a list)
970 * and control if Port is registered for each api who use ports
973 performance_update_latency_time(This, port, NULL);
974 return S_OK;
977 static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, IDirectMusicPort *pPort)
979 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
981 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
983 FIXME("(%p, %p): stub\n", This, pPort);
984 IDirectMusicPort_Release(pPort);
985 return S_OK;
988 static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface,
989 DWORD block_num, IDirectMusicPort *port, DWORD group)
991 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
993 FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, group);
995 if (!port) return E_POINTER;
996 if (block_num > MAXDWORD / 16) return E_INVALIDARG;
997 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
999 pchannel_block_set(&This->pchannels, block_num, port, group, FALSE);
1001 return S_OK;
1004 static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD pchannel,
1005 IDirectMusicPort *port, DWORD group, DWORD channel)
1007 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1008 struct pchannel_block *block;
1010 FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, pchannel, port, group, channel);
1012 if (!port) return E_POINTER;
1013 if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE;
1015 block = pchannel_block_set(&This->pchannels, pchannel / 16, port, 0, TRUE);
1016 if (block) {
1017 block->pchannel[pchannel % 16].group = group;
1018 block->pchannel[pchannel % 16].channel = channel;
1021 return S_OK;
1024 static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD pchannel,
1025 IDirectMusicPort **port, DWORD *group, DWORD *channel)
1027 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1028 struct pchannel_block *block;
1029 struct wine_rb_entry *entry;
1030 DWORD block_num = pchannel / 16;
1031 unsigned int index = pchannel % 16;
1033 TRACE("(%p)->(%ld, %p, %p, %p)\n", This, pchannel, port, group, channel);
1035 entry = wine_rb_get(&This->pchannels, &block_num);
1036 if (!entry)
1037 return E_INVALIDARG;
1038 block = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry);
1040 if (port) {
1041 *port = block->pchannel[index].port;
1042 IDirectMusicPort_AddRef(*port);
1044 if (group)
1045 *group = block->pchannel[index].group;
1046 if (channel)
1047 *channel = block->pchannel[index].channel;
1049 return S_OK;
1052 static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *iface,
1053 IDirectMusicInstrument *instrument, DWORD port_channel,
1054 IDirectMusicDownloadedInstrument **downloaded, DMUS_NOTERANGE *note_ranges,
1055 DWORD note_range_count, IDirectMusicPort **port, DWORD *group, DWORD *music_channel)
1057 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1058 IDirectMusicPort *tmp_port = NULL;
1059 HRESULT hr;
1061 TRACE("(%p, %p, %ld, %p, %p, %ld, %p, %p, %p)\n", This, instrument, port_channel, downloaded,
1062 note_ranges, note_range_count, port, group, music_channel);
1064 if (!port) port = &tmp_port;
1065 if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(iface, port_channel, port, group, music_channel)))
1066 return hr;
1068 hr = IDirectMusicPort_DownloadInstrument(*port, instrument, downloaded, note_ranges, note_range_count);
1069 if (tmp_port) IDirectMusicPort_Release(tmp_port);
1070 return hr;
1073 static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DWORD dwFlags)
1075 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1077 FIXME("(%p, %ld, %ld): stub\n", This, mtTime, dwFlags);
1078 return S_OK;
1081 static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
1082 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
1084 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1086 FIXME("(%p, %s, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pmtNext, pParam);
1087 return S_OK;
1090 static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
1091 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam)
1093 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1095 FIXME("(%p, %s, %ld, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pParam);
1096 return S_OK;
1099 static HRESULT WINAPI performance_GetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
1100 void *pParam, DWORD dwSize)
1102 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1104 TRACE("(%p, %s, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), pParam, dwSize);
1106 if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload))
1107 memcpy(pParam, &This->fAutoDownload, sizeof(This->fAutoDownload));
1108 if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel))
1109 memcpy(pParam, &This->cMasterGrooveLevel, sizeof(This->cMasterGrooveLevel));
1110 if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo))
1111 memcpy(pParam, &This->fMasterTempo, sizeof(This->fMasterTempo));
1112 if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume))
1113 memcpy(pParam, &This->lMasterVolume, sizeof(This->lMasterVolume));
1115 return S_OK;
1118 static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType,
1119 void *pParam, DWORD dwSize)
1121 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1123 TRACE("(%p, %s, %p, %ld)\n", This, debugstr_dmguid(rguidType), pParam, dwSize);
1125 if (IsEqualGUID (rguidType, &GUID_PerfAutoDownload)) {
1126 memcpy(&This->fAutoDownload, pParam, dwSize);
1127 TRACE("=> AutoDownload set to %d\n", This->fAutoDownload);
1129 if (IsEqualGUID (rguidType, &GUID_PerfMasterGrooveLevel)) {
1130 memcpy(&This->cMasterGrooveLevel, pParam, dwSize);
1131 TRACE("=> MasterGrooveLevel set to %i\n", This->cMasterGrooveLevel);
1133 if (IsEqualGUID (rguidType, &GUID_PerfMasterTempo)) {
1134 memcpy(&This->fMasterTempo, pParam, dwSize);
1135 TRACE("=> MasterTempo set to %f\n", This->fMasterTempo);
1137 if (IsEqualGUID (rguidType, &GUID_PerfMasterVolume)) {
1138 memcpy(&This->lMasterVolume, pParam, dwSize);
1139 TRACE("=> MasterVolume set to %li\n", This->lMasterVolume);
1142 return S_OK;
1145 static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *ret_time)
1147 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1148 REFERENCE_TIME current_time;
1149 HRESULT hr;
1151 TRACE("(%p, %p)\n", This, ret_time);
1153 if (SUCCEEDED(hr = IDirectMusicPerformance8_GetTime(iface, &current_time, NULL)))
1154 *ret_time = current_time + This->latency_offset;
1156 return hr;
1159 static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime)
1162 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1164 FIXME("(%p, %p): stub\n", This, prtTime);
1165 return S_OK;
1168 static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtAmount)
1170 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1172 FIXME("(%p, 0x%s): stub\n", This, wine_dbgstr_longlong(rtAmount));
1173 return S_OK;
1176 static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface)
1178 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1179 struct message *message, *next;
1180 HANDLE message_thread;
1181 HRESULT hr;
1183 FIXME("(%p): semi-stub\n", This);
1185 if ((message_thread = This->message_thread))
1187 EnterCriticalSection(&This->safe);
1188 This->message_thread = NULL;
1189 LeaveCriticalSection(&This->safe);
1190 WakeConditionVariable(&This->cond);
1192 WaitForSingleObject(message_thread, INFINITE);
1193 CloseHandle(message_thread);
1196 This->notification_performance = FALSE;
1197 This->notification_segment = FALSE;
1199 LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry)
1201 list_remove(&message->entry);
1202 list_init(&message->entry);
1204 if (message->msg.dwType == DMUS_PMSGT_INTERNAL_SEGMENT_END)
1205 hr = IDirectMusicTool_ProcessPMsg(&This->IDirectMusicTool_iface,
1206 (IDirectMusicPerformance *)iface, &message->msg);
1207 else
1208 hr = DMUS_S_FREE;
1210 if (hr == DMUS_S_FREE && FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg)))
1211 WARN("Failed to free message %p, hr %#lx\n", message, hr);
1214 LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->notifications, struct message, entry)
1216 list_remove(&message->entry);
1217 list_init(&message->entry);
1219 if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg)))
1220 WARN("Failed to free message %p, hr %#lx\n", message, hr);
1223 performance_set_primary_segment(This, NULL);
1225 if (This->master_clock)
1227 IReferenceClock_Release(This->master_clock);
1228 This->master_clock = NULL;
1230 if (This->dsound) {
1231 IDirectSound_Release(This->dsound);
1232 This->dsound = NULL;
1234 if (This->dmusic) {
1235 IDirectMusic8_SetDirectSound(This->dmusic, NULL, NULL);
1236 IDirectMusic8_Release(This->dmusic);
1237 This->dmusic = NULL;
1239 This->audio_paths_enabled = FALSE;
1241 return S_OK;
1244 static HRESULT WINAPI performance_GetResolvedTime(IDirectMusicPerformance8 *iface,
1245 REFERENCE_TIME rtTime, REFERENCE_TIME *prtResolved, DWORD dwTimeResolveFlags)
1247 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1249 FIXME("(%p, 0x%s, %p, %ld): stub\n", This, wine_dbgstr_longlong(rtTime),
1250 prtResolved, dwTimeResolveFlags);
1251 return S_OK;
1254 static HRESULT WINAPI performance_MIDIToMusic(IDirectMusicPerformance8 *iface, BYTE bMIDIValue,
1255 DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, WORD *pwMusicValue)
1257 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1259 FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, bMIDIValue, pChord, bPlayMode, bChordLevel, pwMusicValue);
1260 return S_OK;
1263 static HRESULT WINAPI performance_MusicToMIDI(IDirectMusicPerformance8 *iface, WORD wMusicValue,
1264 DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, BYTE *pbMIDIValue)
1266 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1268 FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, wMusicValue, pChord, bPlayMode, bChordLevel, pbMIDIValue);
1269 return S_OK;
1272 static HRESULT WINAPI performance_TimeToRhythm(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime,
1273 DMUS_TIMESIGNATURE *pTimeSig, WORD *pwMeasure, BYTE *pbBeat, BYTE *pbGrid, short *pnOffset)
1275 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1277 FIXME("(%p, %ld, %p, %p, %p, %p, %p): stub\n", This, mtTime, pTimeSig, pwMeasure, pbBeat, pbGrid, pnOffset);
1278 return S_OK;
1281 static HRESULT WINAPI performance_RhythmToTime(IDirectMusicPerformance8 *iface, WORD wMeasure,
1282 BYTE bBeat, BYTE bGrid, short nOffset, DMUS_TIMESIGNATURE *pTimeSig, MUSIC_TIME *pmtTime)
1284 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1286 FIXME("(%p, %d, %d, %d, %i, %p, %p): stub\n", This, wMeasure, bBeat, bGrid, nOffset, pTimeSig, pmtTime);
1287 return S_OK;
1290 /* IDirectMusicPerformance8 Interface part follow: */
1291 static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic,
1292 IDirectSound **dsound, HWND hwnd, DWORD default_path_type, DWORD num_channels, DWORD flags,
1293 DMUS_AUDIOPARAMS *params)
1295 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1296 HRESULT hr = S_OK;
1298 TRACE("(%p, %p, %p, %p, %lx, %lu, %lx, %p)\n", This, dmusic, dsound, hwnd, default_path_type,
1299 num_channels, flags, params);
1301 if (flags) FIXME("flags parameter not used\n");
1302 if (params) FIXME("params parameter not used\n");
1304 if (FAILED(hr = IDirectMusicPerformance8_Init(iface, dmusic && *dmusic ? dmusic : NULL,
1305 dsound ? *dsound : NULL, hwnd)))
1306 return hr;
1308 This->audio_paths_enabled = TRUE;
1309 if (default_path_type)
1311 hr = IDirectMusicPerformance8_CreateStandardAudioPath(iface, default_path_type,
1312 num_channels, FALSE, &This->pDefaultPath);
1313 if (FAILED(hr))
1315 IDirectMusicPerformance_CloseDown(iface);
1316 return hr;
1320 if (dsound && !*dsound) {
1321 *dsound = This->dsound;
1322 IDirectSound_AddRef(*dsound);
1324 if (dmusic && !*dmusic) {
1325 *dmusic = (IDirectMusic *)This->dmusic;
1326 IDirectMusic_AddRef(*dmusic);
1329 return S_OK;
1332 static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *source,
1333 WCHAR *segment_name, IUnknown *transition, DWORD segment_flags, INT64 start_time,
1334 IDirectMusicSegmentState **segment_state, IUnknown *from, IUnknown *audio_path)
1336 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1337 IDirectMusicSegmentState *state;
1338 IDirectMusicSegment *segment;
1339 MUSIC_TIME music_time;
1340 HRESULT hr;
1342 FIXME("(%p, %p, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name),
1343 transition, segment_flags, start_time, segment_state, from, audio_path);
1345 /* NOTE: The time is in music time unless the DMUS_SEGF_REFTIME flag is set. */
1346 if (segment_flags) FIXME("flags %#lx not implemented\n", segment_flags);
1347 if (start_time) FIXME("start_time %I64d not implemented\n", start_time);
1349 if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment)))
1350 return hr;
1352 EnterCriticalSection(&This->safe);
1354 performance_set_primary_segment(This, segment);
1356 if ((!(music_time = start_time) && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time)))
1357 || FAILED(hr = segment_state_create(segment, music_time, iface, &state)))
1359 performance_set_primary_segment(This, NULL);
1360 LeaveCriticalSection(&This->safe);
1362 IDirectMusicSegment_Release(segment);
1363 return hr;
1366 if (FAILED(hr = segment_state_play(state, iface)))
1368 ERR("Failed to play segment state, hr %#lx\n", hr);
1369 performance_set_primary_segment(This, NULL);
1371 else if (segment_state)
1373 *segment_state = state;
1374 IDirectMusicSegmentState_AddRef(state);
1377 LeaveCriticalSection(&This->safe);
1379 IDirectMusicSegmentState_Release(state);
1380 IDirectMusicSegment_Release(segment);
1381 return hr;
1384 static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnknown *pObjectToStop,
1385 __int64 i64StopTime, DWORD dwFlags)
1387 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1389 FIXME("(%p, %p, 0x%s, %ld): stub\n", This, pObjectToStop,
1390 wine_dbgstr_longlong(i64StopTime), dwFlags);
1391 return S_OK;
1394 static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg, DMUS_PMSG **clone)
1396 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1397 HRESULT hr;
1399 TRACE("(%p, %p, %p)\n", This, msg, clone);
1401 if (!msg || !clone) return E_POINTER;
1402 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(iface, msg->dwSize, clone))) return hr;
1404 memcpy(*clone, msg, msg->dwSize);
1405 if (msg->pTool) IDirectMusicTool_AddRef(msg->pTool);
1406 if (msg->pGraph) IDirectMusicGraph_AddRef(msg->pGraph);
1407 if (msg->punkUser) IUnknown_AddRef(msg->punkUser);
1409 return S_OK;
1412 static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface,
1413 IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ret_iface)
1415 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1416 IDirectMusicAudioPath *pPath;
1418 FIXME("(%p, %p, %d, %p): stub\n", This, pSourceConfig, fActivate, ret_iface);
1420 if (!ret_iface) return E_POINTER;
1421 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1423 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void **)&pPath);
1424 set_audiopath_perf_pointer(pPath, iface);
1426 /** TODO */
1427 *ret_iface = pPath;
1428 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1431 static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface,
1432 DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ret_iface)
1434 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1435 IDirectMusicAudioPath *pPath;
1436 DSBUFFERDESC desc;
1437 WAVEFORMATEX format;
1438 DMUS_PORTPARAMS params = {0};
1439 IDirectSoundBuffer *buffer, *primary_buffer;
1440 HRESULT hr = S_OK;
1442 FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This, dwType, pchannel_count, fActivate, ret_iface);
1444 if (!ret_iface) return E_POINTER;
1445 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1447 *ret_iface = NULL;
1449 /* Secondary buffer description */
1450 memset(&format, 0, sizeof(format));
1451 format.wFormatTag = WAVE_FORMAT_PCM;
1452 format.nChannels = 1;
1453 format.nSamplesPerSec = 44000;
1454 format.nAvgBytesPerSec = 44000*2;
1455 format.nBlockAlign = 2;
1456 format.wBitsPerSample = 16;
1457 format.cbSize = 0;
1459 memset(&desc, 0, sizeof(desc));
1460 desc.dwSize = sizeof(desc);
1461 desc.dwFlags = DSBCAPS_CTRLFX | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
1462 desc.dwBufferBytes = DSBSIZE_MIN;
1463 desc.dwReserved = 0;
1464 desc.lpwfxFormat = &format;
1465 desc.guid3DAlgorithm = GUID_NULL;
1467 switch(dwType) {
1468 case DMUS_APATH_DYNAMIC_3D:
1469 desc.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE;
1470 break;
1471 case DMUS_APATH_DYNAMIC_MONO:
1472 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1473 break;
1474 case DMUS_APATH_SHARED_STEREOPLUSREVERB:
1475 /* normally we have to create 2 buffers (one for music other for reverb)
1476 * in this case. See msdn
1478 case DMUS_APATH_DYNAMIC_STEREO:
1479 desc.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
1480 format.nChannels = 2;
1481 format.nBlockAlign *= 2;
1482 format.nAvgBytesPerSec *=2;
1483 break;
1484 default:
1485 return E_INVALIDARG;
1488 /* Create a port */
1489 params.dwSize = sizeof(params);
1490 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS | DMUS_PORTPARAMS_AUDIOCHANNELS;
1491 params.dwChannelGroups = (pchannel_count + 15) / 16;
1492 params.dwAudioChannels = format.nChannels;
1493 if (FAILED(hr = perf_dmport_create(This, &params)))
1494 return hr;
1496 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &buffer, NULL);
1497 if (FAILED(hr))
1498 return DSERR_BUFFERLOST;
1500 /* Update description for creating primary buffer */
1501 desc.dwFlags |= DSBCAPS_PRIMARYBUFFER;
1502 desc.dwFlags &= ~DSBCAPS_CTRLFX;
1503 desc.dwBufferBytes = 0;
1504 desc.lpwfxFormat = NULL;
1506 hr = IDirectSound_CreateSoundBuffer(This->dsound, &desc, &primary_buffer, NULL);
1507 if (FAILED(hr)) {
1508 IDirectSoundBuffer_Release(buffer);
1509 return DSERR_BUFFERLOST;
1512 create_dmaudiopath(&IID_IDirectMusicAudioPath, (void**)&pPath);
1513 set_audiopath_perf_pointer(pPath, iface);
1514 set_audiopath_dsound_buffer(pPath, buffer);
1515 set_audiopath_primary_dsound_buffer(pPath, primary_buffer);
1517 *ret_iface = pPath;
1518 TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface);
1519 return IDirectMusicAudioPath_Activate(*ret_iface, fActivate);
1522 static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *audio_path)
1524 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1526 FIXME("(%p, %p): semi-stub\n", This, audio_path);
1528 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1530 if (This->pDefaultPath) IDirectMusicAudioPath_Release(This->pDefaultPath);
1531 if ((This->pDefaultPath = audio_path))
1533 IDirectMusicAudioPath_AddRef(This->pDefaultPath);
1534 set_audiopath_perf_pointer(This->pDefaultPath, iface);
1537 return S_OK;
1540 static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface,
1541 IDirectMusicAudioPath **ret_iface)
1543 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1545 FIXME("(%p, %p): semi-stub (%p)\n", This, ret_iface, This->pDefaultPath);
1547 if (!ret_iface) return E_POINTER;
1548 if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE;
1550 if ((*ret_iface = This->pDefaultPath)) IDirectMusicAudioPath_AddRef(*ret_iface);
1552 return S_OK;
1555 static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID,
1556 DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
1558 struct performance *This = impl_from_IDirectMusicPerformance8(iface);
1560 FIXME("(%p, %s, %ld, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwTrackID, dwGroupBits, dwIndex, mtTime, pmtNext, pParam);
1562 return S_OK;
1565 static const IDirectMusicPerformance8Vtbl performance_vtbl =
1567 performance_QueryInterface,
1568 performance_AddRef,
1569 performance_Release,
1570 performance_Init,
1571 performance_PlaySegment,
1572 performance_Stop,
1573 performance_GetSegmentState,
1574 performance_SetPrepareTime,
1575 performance_GetPrepareTime,
1576 performance_SetBumperLength,
1577 performance_GetBumperLength,
1578 performance_SendPMsg,
1579 performance_MusicToReferenceTime,
1580 performance_ReferenceToMusicTime,
1581 performance_IsPlaying,
1582 performance_GetTime,
1583 performance_AllocPMsg,
1584 performance_FreePMsg,
1585 performance_GetGraph,
1586 performance_SetGraph,
1587 performance_SetNotificationHandle,
1588 performance_GetNotificationPMsg,
1589 performance_AddNotificationType,
1590 performance_RemoveNotificationType,
1591 performance_AddPort,
1592 performance_RemovePort,
1593 performance_AssignPChannelBlock,
1594 performance_AssignPChannel,
1595 performance_PChannelInfo,
1596 performance_DownloadInstrument,
1597 performance_Invalidate,
1598 performance_GetParam,
1599 performance_SetParam,
1600 performance_GetGlobalParam,
1601 performance_SetGlobalParam,
1602 performance_GetLatencyTime,
1603 performance_GetQueueTime,
1604 performance_AdjustTime,
1605 performance_CloseDown,
1606 performance_GetResolvedTime,
1607 performance_MIDIToMusic,
1608 performance_MusicToMIDI,
1609 performance_TimeToRhythm,
1610 performance_RhythmToTime,
1611 performance_InitAudio,
1612 performance_PlaySegmentEx,
1613 performance_StopEx,
1614 performance_ClonePMsg,
1615 performance_CreateAudioPath,
1616 performance_CreateStandardAudioPath,
1617 performance_SetDefaultAudioPath,
1618 performance_GetDefaultAudioPath,
1619 performance_GetParamEx,
1622 static inline struct performance *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface)
1624 return CONTAINING_RECORD(iface, struct performance, IDirectMusicGraph_iface);
1627 static HRESULT WINAPI performance_graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface)
1629 struct performance *This = impl_from_IDirectMusicGraph(iface);
1630 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
1633 static ULONG WINAPI performance_graph_AddRef(IDirectMusicGraph *iface)
1635 struct performance *This = impl_from_IDirectMusicGraph(iface);
1636 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
1639 static ULONG WINAPI performance_graph_Release(IDirectMusicGraph *iface)
1641 struct performance *This = impl_from_IDirectMusicGraph(iface);
1642 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
1645 static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg)
1647 struct performance *This = impl_from_IDirectMusicGraph(iface);
1648 HRESULT hr;
1650 TRACE("(%p, %p)\n", This, msg);
1652 if (!msg) return E_POINTER;
1654 /* FIXME: Implement segment and audio path graphs support */
1655 if (!This->pToolGraph) hr = DMUS_S_LAST_TOOL;
1656 else if (FAILED(hr = IDirectMusicGraph_StampPMsg(This->pToolGraph, msg))) return hr;
1658 if (msg->pGraph)
1660 IDirectMusicTool_Release(msg->pGraph);
1661 msg->pGraph = NULL;
1664 if (hr == DMUS_S_LAST_TOOL)
1666 const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME;
1667 msg->dwFlags &= ~delivery_flags;
1668 msg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE;
1670 if (msg->pTool) IDirectMusicTool_Release(msg->pTool);
1671 msg->pTool = &This->IDirectMusicTool_iface;
1672 IDirectMusicTool_AddRef(msg->pTool);
1673 return S_OK;
1676 if (SUCCEEDED(hr))
1678 msg->pGraph = &This->IDirectMusicGraph_iface;
1679 IDirectMusicTool_AddRef(msg->pGraph);
1682 return hr;
1685 static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool,
1686 DWORD *channels, DWORD channels_count, LONG index)
1688 struct performance *This = impl_from_IDirectMusicGraph(iface);
1689 TRACE("(%p, %p, %p, %lu, %ld)\n", This, tool, channels, channels_count, index);
1690 return E_NOTIMPL;
1693 static HRESULT WINAPI performance_graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **tool)
1695 struct performance *This = impl_from_IDirectMusicGraph(iface);
1696 TRACE("(%p, %lu, %p)\n", This, index, tool);
1697 return E_NOTIMPL;
1700 static HRESULT WINAPI performance_graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool)
1702 struct performance *This = impl_from_IDirectMusicGraph(iface);
1703 TRACE("(%p, %p)\n", This, tool);
1704 return E_NOTIMPL;
1707 static const IDirectMusicGraphVtbl performance_graph_vtbl =
1709 performance_graph_QueryInterface,
1710 performance_graph_AddRef,
1711 performance_graph_Release,
1712 performance_graph_StampPMsg,
1713 performance_graph_InsertTool,
1714 performance_graph_GetTool,
1715 performance_graph_RemoveTool,
1718 static inline struct performance *impl_from_IDirectMusicTool(IDirectMusicTool *iface)
1720 return CONTAINING_RECORD(iface, struct performance, IDirectMusicTool_iface);
1723 static HRESULT WINAPI performance_tool_QueryInterface(IDirectMusicTool *iface, REFIID riid, void **ret_iface)
1725 struct performance *This = impl_from_IDirectMusicTool(iface);
1726 return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface);
1729 static ULONG WINAPI performance_tool_AddRef(IDirectMusicTool *iface)
1731 struct performance *This = impl_from_IDirectMusicTool(iface);
1732 return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface);
1735 static ULONG WINAPI performance_tool_Release(IDirectMusicTool *iface)
1737 struct performance *This = impl_from_IDirectMusicTool(iface);
1738 return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface);
1741 static HRESULT WINAPI performance_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph)
1743 struct performance *This = impl_from_IDirectMusicTool(iface);
1744 TRACE("(%p, %p)\n", This, graph);
1745 return E_NOTIMPL;
1748 static HRESULT WINAPI performance_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type)
1750 struct performance *This = impl_from_IDirectMusicTool(iface);
1751 TRACE("(%p, %p)\n", This, type);
1752 *type = DMUS_PMSGF_TOOL_IMMEDIATE;
1753 return S_OK;
1756 static HRESULT WINAPI performance_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size)
1758 struct performance *This = impl_from_IDirectMusicTool(iface);
1759 TRACE("(%p, %p)\n", This, size);
1760 *size = 0;
1761 return S_OK;
1764 static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size)
1766 struct performance *This = impl_from_IDirectMusicTool(iface);
1767 TRACE("(%p, %p, %lu)\n", This, types, size);
1768 return E_NOTIMPL;
1771 static HRESULT performance_send_midi_pmsg(struct performance *This, DMUS_PMSG *msg, UINT flags,
1772 BYTE status, BYTE byte1, BYTE byte2)
1774 IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface;
1775 DMUS_MIDI_PMSG *midi;
1776 HRESULT hr;
1778 if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*midi),
1779 (DMUS_PMSG **)&midi)))
1780 return hr;
1782 if (flags & DMUS_PMSGF_REFTIME) midi->rtTime = msg->rtTime;
1783 if (flags & DMUS_PMSGF_MUSICTIME) midi->mtTime = msg->mtTime;
1784 midi->dwFlags = flags;
1785 midi->dwPChannel = msg->dwPChannel;
1786 midi->dwVirtualTrackID = msg->dwVirtualTrackID;
1787 midi->dwVoiceID = msg->dwVoiceID;
1788 midi->dwGroupID = msg->dwGroupID;
1789 midi->dwType = DMUS_PMSGT_MIDI;
1790 midi->bStatus = status;
1791 midi->bByte1 = byte1;
1792 midi->bByte2 = byte2;
1794 if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)midi)))
1795 IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)midi);
1797 return hr;
1800 static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface,
1801 IDirectMusicPerformance *performance, DMUS_PMSG *msg)
1803 struct performance *This = impl_from_IDirectMusicTool(iface);
1804 struct message *message = message_from_DMUS_PMSG(msg);
1805 HRESULT hr;
1807 TRACE("(%p, %p, %p)\n", This, performance, msg);
1809 switch (msg->dwType)
1811 case DMUS_PMSGT_MIDI:
1813 static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD);
1814 DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size};
1815 DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg;
1816 REFERENCE_TIME latency_time;
1817 IDirectMusicBuffer *buffer;
1818 IDirectMusicPort *port;
1819 DWORD group, channel;
1820 UINT value = 0;
1822 if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(performance, msg->dwPChannel,
1823 &port, &group, &channel)))
1825 WARN("Failed to get message port, hr %#lx\n", hr);
1826 return DMUS_S_FREE;
1828 performance_update_latency_time(This, port, &latency_time);
1830 value |= channel;
1831 value |= (UINT)midi->bStatus;
1832 value |= (UINT)midi->bByte1 << 8;
1833 value |= (UINT)midi->bByte2 << 16;
1835 if (SUCCEEDED(hr = IDirectMusic_CreateMusicBuffer(This->dmusic, &desc, &buffer, NULL)))
1837 if (msg->rtTime == -1) msg->rtTime = latency_time;
1838 hr = IDirectMusicBuffer_PackStructured(buffer, msg->rtTime, group, value);
1839 if (SUCCEEDED(hr)) hr = IDirectMusicPort_PlayBuffer(port, buffer);
1840 IDirectMusicBuffer_Release(buffer);
1843 IDirectMusicPort_Release(port);
1844 break;
1847 case DMUS_PMSGT_NOTE:
1849 DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg;
1851 msg->mtTime += note->nOffset;
1852 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1853 MIDI_NOTE_ON, note->bMidiValue, note->bVelocity)))
1854 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1856 msg->mtTime += note->mtDuration;
1857 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE,
1858 MIDI_NOTE_OFF, note->bMidiValue, 0)))
1859 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1861 break;
1864 case DMUS_PMSGT_CURVE:
1866 DMUS_CURVE_PMSG *curve = (DMUS_CURVE_PMSG *)msg;
1868 msg->mtTime += curve->nOffset;
1869 switch (curve->dwType)
1871 case DMUS_CURVET_CCCURVE:
1872 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1873 MIDI_CONTROL_CHANGE, curve->bCCData, curve->nStartValue)))
1874 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1875 break;
1876 case DMUS_CURVET_RPNCURVE:
1877 case DMUS_CURVET_NRPNCURVE:
1878 FIXME("Unhandled curve type %#lx\n", curve->dwType);
1879 break;
1882 break;
1885 case DMUS_PMSGT_PATCH:
1887 DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg;
1889 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1890 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_MSB, patch->byMSB)))
1891 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1893 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1894 MIDI_CONTROL_CHANGE, MIDI_CC_BANK_LSB, patch->byLSB)))
1895 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1897 if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE,
1898 MIDI_PROGRAM_CHANGE, patch->byInstrument, 0)))
1899 WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
1901 break;
1904 case DMUS_PMSGT_NOTIFICATION:
1906 DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg;
1907 struct message *previous;
1908 BOOL enabled = FALSE;
1910 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE))
1911 enabled = This->notification_performance;
1912 if (IsEqualGUID(&notif->guidNotificationType, &GUID_NOTIFICATION_SEGMENT))
1913 enabled = This->notification_segment;
1914 if (!enabled) return DMUS_S_FREE;
1916 if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE)
1918 /* re-send the message for queueing at the expected time */
1919 msg->dwFlags &= ~DMUS_PMSGF_TOOL_IMMEDIATE;
1920 msg->dwFlags |= DMUS_PMSGF_TOOL_ATTIME;
1922 if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)msg)))
1924 ERR("Failed to send notification message, hr %#lx\n", hr);
1925 return DMUS_S_FREE;
1928 return S_OK;
1931 list_add_tail(&This->notifications, &message->entry);
1933 /* discard old notification messages */
1936 previous = LIST_ENTRY(list_head(&This->notifications), struct message, entry);
1937 if (This->notification_timeout <= 0) break; /* negative values may be used to keep everything */
1938 if (message->msg.rtTime - previous->msg.rtTime <= This->notification_timeout) break;
1939 list_remove(&previous->entry);
1940 list_init(&previous->entry);
1941 } while (SUCCEEDED(hr = IDirectMusicPerformance_FreePMsg(performance, &previous->msg)));
1943 SetEvent(This->notification_event);
1944 return S_OK;
1947 case DMUS_PMSGT_WAVE:
1948 if (FAILED(hr = IDirectSoundBuffer_Play((IDirectSoundBuffer *)msg->punkUser, 0, 0, 0)))
1949 WARN("Failed to play wave buffer, hr %#lx\n", hr);
1950 break;
1952 case DMUS_PMSGT_INTERNAL_SEGMENT_TICK:
1953 msg->rtTime += 10000000;
1954 msg->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
1956 /* re-send the tick message until segment_state_tick returns S_FALSE */
1957 if (FAILED(hr = segment_state_tick((IDirectMusicSegmentState *)msg->punkUser,
1958 (IDirectMusicPerformance8 *)performance)))
1959 ERR("Failed to tick segment state %p, hr %#lx\n", msg->punkUser, hr);
1960 else if (hr == S_FALSE)
1961 return DMUS_S_FREE; /* done ticking */
1962 else if (FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, msg)))
1963 ERR("Failed to queue tick for segment state %p, hr %#lx\n", msg->punkUser, hr);
1965 return S_OK;
1967 case DMUS_PMSGT_INTERNAL_SEGMENT_END:
1968 if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)msg->punkUser,
1969 (IDirectMusicPerformance8 *)performance)))
1970 WARN("Failed to end segment state %p, hr %#lx\n", msg->punkUser, hr);
1971 break;
1973 default:
1974 FIXME("Unhandled message type %#lx\n", msg->dwType);
1975 break;
1978 return DMUS_S_FREE;
1981 static HRESULT WINAPI performance_tool_Flush(IDirectMusicTool *iface,
1982 IDirectMusicPerformance *performance, DMUS_PMSG *msg, REFERENCE_TIME time)
1984 struct performance *This = impl_from_IDirectMusicTool(iface);
1985 FIXME("(%p, %p, %p, %I64d): stub\n", This, performance, msg, time);
1986 return E_NOTIMPL;
1989 static const IDirectMusicToolVtbl performance_tool_vtbl =
1991 performance_tool_QueryInterface,
1992 performance_tool_AddRef,
1993 performance_tool_Release,
1994 performance_tool_Init,
1995 performance_tool_GetMsgDeliveryType,
1996 performance_tool_GetMediaTypeArraySize,
1997 performance_tool_GetMediaTypes,
1998 performance_tool_ProcessPMsg,
1999 performance_tool_Flush,
2002 /* for ClassFactory */
2003 HRESULT create_dmperformance(REFIID iid, void **ret_iface)
2005 struct performance *obj;
2006 HRESULT hr;
2008 TRACE("(%s, %p)\n", debugstr_guid(iid), ret_iface);
2010 *ret_iface = NULL;
2011 if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
2012 obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl;
2013 obj->IDirectMusicGraph_iface.lpVtbl = &performance_graph_vtbl;
2014 obj->IDirectMusicTool_iface.lpVtbl = &performance_tool_vtbl;
2015 obj->ref = 1;
2017 obj->pDefaultPath = NULL;
2018 InitializeCriticalSection(&obj->safe);
2019 obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe");
2020 wine_rb_init(&obj->pchannels, pchannel_block_compare);
2022 list_init(&obj->messages);
2023 list_init(&obj->notifications);
2025 obj->latency_offset = 50;
2026 obj->dwBumperLength = 50; /* 50 ms default */
2027 obj->dwPrepareTime = 1000; /* 1000 ms default */
2029 hr = IDirectMusicPerformance8_QueryInterface(&obj->IDirectMusicPerformance8_iface, iid, ret_iface);
2030 IDirectMusicPerformance_Release(&obj->IDirectMusicPerformance8_iface);
2031 return hr;
2034 static inline struct performance *unsafe_impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface)
2036 if (iface->lpVtbl != &performance_vtbl) return NULL;
2037 return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface);
2040 HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound)
2042 struct performance *This = unsafe_impl_from_IDirectMusicPerformance8(iface);
2043 if (!This || !(*dsound = This->dsound)) return E_FAIL;
2044 IDirectSound_AddRef(*dsound);
2045 return S_OK;