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"
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
,
39 IDirectMusicPort
*port
;
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
;
51 IDirectMusicPerformance8 IDirectMusicPerformance8_iface
;
52 IDirectMusicGraph IDirectMusicGraph_iface
;
53 IDirectMusicTool IDirectMusicTool_iface
;
57 IDirectMusicGraph
*pToolGraph
;
59 char cMasterGrooveLevel
;
62 /* performance channels */
63 struct wine_rb_tree channel_blocks
;
65 BOOL audio_paths_enabled
;
66 IDirectMusicAudioPath
*pDefaultPath
;
67 REFERENCE_TIME latency_offset
;
71 HANDLE message_thread
;
72 CRITICAL_SECTION safe
;
73 CONDITION_VARIABLE cond
;
75 IReferenceClock
*master_clock
;
76 REFERENCE_TIME init_time
;
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
;
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
;
108 if (!(ptr
= list_head(&This
->messages
)))
110 message
= LIST_ENTRY(ptr
, struct message
, entry
);
112 if (FAILED(IDirectMusicPerformance_GetLatencyTime(performance
, &latency
)))
115 switch (message
->msg
.dwFlags
& delivery_flags
)
118 WARN("No delivery flag found for message %p\n", &message
->msg
);
120 case DMUS_PMSGF_TOOL_QUEUE
:
121 offset
= This
->dwBumperLength
* 10000;
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;
135 static HRESULT
performance_process_message(struct performance
*This
, DMUS_PMSG
*msg
)
137 IDirectMusicPerformance
*performance
= (IDirectMusicPerformance
*)&This
->IDirectMusicPerformance8_iface
;
138 IDirectMusicTool
*tool
;
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
);
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
;
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
)
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
);
174 static DWORD WINAPI
message_thread_proc(void *args
)
176 struct performance
*This
= args
;
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
);
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
);
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
;
216 if (FAILED(hr
= IDirectMusicPerformance8_AllocPMsg(performance
, sizeof(*msg
), &msg
)))
219 msg
->mtTime
= music_time
;
220 msg
->dwFlags
= DMUS_PMSGF_MUSICTIME
| flags
;
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
);
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
;
239 if (FAILED(hr
= IDirectMusicPerformance8_AllocPMsg(performance
, sizeof(*msg
), (DMUS_PMSG
**)&msg
)))
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
);
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
);
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
);
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
;
292 if ((entry
= wine_rb_get(&This
->channel_blocks
, &block_num
)))
293 block
= WINE_RB_ENTRY_VALUE(entry
, struct channel_block
, entry
);
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
);
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
);
324 if (FAILED(hr
= performance_send_notification_pmsg(This
, music_time
, This
->notification_performance
,
325 GUID_NOTIFICATION_PERFORMANCE
, DMUS_NOTIFICATION_MUSICSTARTED
, NULL
)))
327 if (FAILED(hr
= performance_send_notification_pmsg(This
, music_time
, This
->notification_segment
,
328 GUID_NOTIFICATION_SEGMENT
, DMUS_NOTIFICATION_SEGSTART
, (IUnknown
*)state
)))
330 if (FAILED(hr
= performance_send_pmsg(This
, music_time
, DMUS_PMSGF_TOOL_IMMEDIATE
,
331 DMUS_PMSGT_DIRTY
, NULL
)))
337 HRESULT
performance_send_segment_tick(IDirectMusicPerformance8
*iface
, MUSIC_TIME music_time
,
338 IDirectMusicSegmentState
*state
)
340 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
344 if (FAILED(hr
= IDirectMusicPerformance8_MusicToReferenceTime(iface
, music_time
, &time
)))
346 if (FAILED(hr
= IDirectMusicPerformance8_ReferenceToMusicTime(iface
, time
+ 2000000, &music_time
)))
348 if (FAILED(hr
= performance_send_pmsg(This
, music_time
, DMUS_PMSGF_TOOL_QUEUE
,
349 DMUS_PMSGT_INTERNAL_SEGMENT_TICK
, (IUnknown
*)state
)))
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
);
363 if (FAILED(hr
= performance_send_notification_pmsg(This
, music_time
- 1450, This
->notification_segment
,
364 GUID_NOTIFICATION_SEGMENT
, DMUS_NOTIFICATION_SEGALMOSTEND
, (IUnknown
*)state
)))
366 if (FAILED(hr
= performance_send_notification_pmsg(This
, music_time
, This
->notification_segment
,
367 GUID_NOTIFICATION_SEGMENT
, DMUS_NOTIFICATION_SEGEND
, (IUnknown
*)state
)))
371 if (FAILED(hr
= performance_send_pmsg(This
, music_time
, DMUS_PMSGF_TOOL_IMMEDIATE
,
372 DMUS_PMSGT_DIRTY
, NULL
)))
374 if (FAILED(hr
= performance_send_notification_pmsg(This
, music_time
, This
->notification_performance
,
375 GUID_NOTIFICATION_PERFORMANCE
, DMUS_NOTIFICATION_MUSICSTOPPED
, NULL
)))
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
)))
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
))
409 IUnknown_AddRef(iface
);
413 if (IsEqualGUID(riid
, &IID_IDirectMusicGraph
))
415 *ret_iface
= &This
->IDirectMusicGraph_iface
;
416 IDirectMusicGraph_AddRef(&This
->IDirectMusicGraph_iface
);
420 if (IsEqualGUID(riid
, &IID_IDirectMusicTool
))
422 *ret_iface
= &This
->IDirectMusicTool_iface
;
423 IDirectMusicTool_AddRef(&This
->IDirectMusicTool_iface
);
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
);
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
);
450 wine_rb_destroy(&This
->channel_blocks
, channel_block_free
, NULL
);
451 This
->safe
.DebugInfo
->Spare
[0] = 0;
452 DeleteCriticalSection(&This
->safe
);
459 static HRESULT
performance_init_dsound(struct performance
*This
, HWND hwnd
)
461 IDirectSound
*dsound
;
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
);
475 static HRESULT
performance_init_dmusic(struct performance
*This
, IDirectSound
*dsound
)
477 IDirectMusic
*dmusic
;
480 if (FAILED(hr
= CoCreateInstance(&CLSID_DirectMusic
, NULL
, CLSCTX_INPROC_SERVER
,
481 &IID_IDirectMusic8
, (void **)&dmusic
)))
484 hr
= IDirectMusic_SetDirectSound(dmusic
, dsound
, NULL
);
486 if (SUCCEEDED(hr
)) This
->dmusic
= dmusic
;
487 else IDirectSound_Release(dmusic
);
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
;
499 if (FAILED(hr
= IDirectMusicPerformance8_AllocPMsg(performance
, sizeof(*midi
),
500 (DMUS_PMSG
**)&midi
)))
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
);
521 static HRESULT WINAPI
performance_Init(IDirectMusicPerformance8
*iface
, IDirectMusic
**dmusic
,
522 IDirectSound
*dsound
, HWND hwnd
)
524 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
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
);
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
);
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
);
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
);
577 IDirectMusicSegmentState
*state
;
580 static void state_entry_destroy(struct state_entry
*entry
)
582 list_remove(&entry
->entry
);
583 IDirectMusicSegmentState_Release(entry
->state
);
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
)
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
;
624 case DMUS_PMSGT_NOTIFICATION
:
626 DMUS_NOTIFICATION_PMSG
*notif
= (DMUS_NOTIFICATION_PMSG
*)&message
->msg
;
627 if (!IsEqualGUID(¬if
->guidNotificationType
, &GUID_NOTIFICATION_SEGMENT
)) return FALSE
;
628 if ((IDirectMusicSegmentState
*)message
->msg
.punkUser
!= state
) return FALSE
;
632 case DMUS_PMSGT_INTERNAL_SEGMENT_TICK
:
633 case DMUS_PMSGT_INTERNAL_SEGMENT_END
:
634 if ((IDirectMusicSegmentState
*)message
->msg
.punkUser
!= state
) return FALSE
;
638 FIXME("Unhandled message type %#lx\n", message
->msg
.dwType
);
645 static void performance_flush_messages(struct performance
*This
, IDirectMusicSegmentState
*state
)
647 IDirectMusicPerformance
*iface
= (IDirectMusicPerformance
*)&This
->IDirectMusicPerformance8_iface
;
648 struct message
*message
, *next
;
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};
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
)))
691 EnterCriticalSection(&This
->safe
);
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
);
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
);
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
;
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
;
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
);
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
;
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
) {
782 *pdwMilliSeconds
= This
->dwPrepareTime
;
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
;
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
) {
803 *pdwMilliSeconds
= This
->dwBumperLength
;
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
;
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
;
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
)))
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
)))
839 msg
->dwFlags
|= DMUS_PMSGF_REFTIME
;
842 hr
= performance_queue_message(This
, message
);
846 LeaveCriticalSection(&This
->safe
);
847 if (SUCCEEDED(hr
)) WakeConditionVariable(&This
->cond
);
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
;
861 TRACE("(%p, %ld, %p)\n", This
, music_time
, time
);
863 if (!time
) return E_POINTER
;
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
, ¶m
)))
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
);
884 duration
+= (600000000.0 * music_time
) / (tempo
* DMUS_PPQ
);
885 *time
= This
->init_time
+ duration
;
887 LeaveCriticalSection(&This
->safe
);
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
;
901 TRACE("(%p, %I64d, %p)\n", This
, time
, music_time
);
903 if (!music_time
) return E_POINTER
;
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
, ¶m
)))
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
);
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
);
940 static HRESULT WINAPI
performance_GetTime(IDirectMusicPerformance8
*iface
, REFERENCE_TIME
*time
, MUSIC_TIME
*music_time
)
942 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
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
);
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
;
975 static HRESULT WINAPI
performance_FreePMsg(IDirectMusicPerformance8
*iface
, DMUS_PMSG
*msg
)
977 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
978 struct message
*message
;
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
);
991 if (msg
->pTool
) IDirectMusicTool_Release(msg
->pTool
);
992 if (msg
->pGraph
) IDirectMusicGraph_Release(msg
->pGraph
);
993 if (msg
->punkUser
) IUnknown_Release(msg
->punkUser
);
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
);
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
);
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
;
1043 This
->notification_timeout
= minimum_time
;
1044 else if (!This
->notification_timeout
)
1045 This
->notification_timeout
= 20000000; /* 2 seconds */
1050 static HRESULT WINAPI
performance_GetNotificationPMsg(IDirectMusicPerformance8
*iface
,
1051 DMUS_NOTIFICATION_PMSG
**ret_msg
)
1053 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
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
);
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
;
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
;
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
;
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
, ¤t_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
;
1147 if (FAILED(hr
= IDirectMusic8_GetDefaultPort(perf
->dmusic
, &guid
)))
1150 if (FAILED(hr
= IDirectMusic8_CreatePort(perf
->dmusic
, &guid
, params
, &port
, NULL
)))
1153 if (FAILED(hr
= IDirectMusicPort_SetDirectSound(port
, perf
->dsound
, NULL
))
1154 || FAILED(hr
= IDirectMusicPort_Activate(port
, TRUE
)))
1156 IDirectMusicPort_Release(port
);
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
);
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
;
1183 DMUS_PORTPARAMS params
= {
1184 .dwSize
= sizeof(params
),
1185 .dwValidParams
= DMUS_PORTPARAMS_CHANNELGROUPS
,
1186 .dwChannelGroups
= 1
1189 return perf_dmport_create(This
, ¶ms
);
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
);
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
);
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
;
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)))
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
);
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
;
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
;
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
;
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
)))
1292 hr
= IDirectMusicPort_DownloadInstrument(*port
, instrument
, downloaded
, note_ranges
, note_range_count
);
1293 if (tmp_port
) IDirectMusicPort_Release(tmp_port
);
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
);
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
);
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
);
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
);
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
);
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
));
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
);
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
;
1388 TRACE("(%p, %p)\n", This
, ret_time
);
1390 if (SUCCEEDED(hr
= IDirectMusicPerformance8_GetTime(iface
, ¤t_time
, NULL
)))
1391 *ret_time
= current_time
+ This
->latency_offset
;
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
);
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
));
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
;
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
);
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
;
1464 IDirectSound_Release(This
->dsound
);
1465 This
->dsound
= NULL
;
1468 IDirectMusic8_SetDirectSound(This
->dmusic
, NULL
, NULL
);
1469 IDirectMusic8_Release(This
->dmusic
);
1470 This
->dmusic
= NULL
;
1472 This
->audio_paths_enabled
= FALSE
;
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
);
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
);
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
);
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
);
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
);
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
);
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
)))
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
);
1548 IDirectMusicPerformance_CloseDown(iface
);
1553 if (dsound
&& !*dsound
) {
1554 *dsound
= This
->dsound
;
1555 IDirectSound_AddRef(*dsound
);
1557 if (dmusic
&& !*dmusic
) {
1558 *dmusic
= This
->dmusic
;
1559 IDirectMusic_AddRef(*dmusic
);
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
;
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
)))
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
);
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
);
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
);
1631 static HRESULT WINAPI
performance_ClonePMsg(IDirectMusicPerformance8
*iface
, DMUS_PMSG
*msg
, DMUS_PMSG
**clone
)
1633 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
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
);
1649 static HRESULT WINAPI
performance_CreateAudioPath(IDirectMusicPerformance8
*iface
,
1650 IUnknown
*pSourceConfig
, BOOL fActivate
, IDirectMusicAudioPath
**ret_iface
)
1652 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
1653 IDirectMusicAudioPath
*pPath
;
1655 FIXME("(%p, %p, %d, %p): stub\n", This
, pSourceConfig
, fActivate
, ret_iface
);
1657 if (!ret_iface
) return E_POINTER
;
1658 if (!This
->audio_paths_enabled
) return DMUS_E_AUDIOPATH_INACTIVE
;
1660 create_dmaudiopath(&IID_IDirectMusicAudioPath
, (void **)&pPath
);
1661 set_audiopath_perf_pointer(pPath
, iface
);
1665 return IDirectMusicAudioPath_Activate(*ret_iface
, fActivate
);
1668 static HRESULT WINAPI
performance_CreateStandardAudioPath(IDirectMusicPerformance8
*iface
,
1669 DWORD dwType
, DWORD pchannel_count
, BOOL fActivate
, IDirectMusicAudioPath
**ret_iface
)
1671 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
1672 IDirectMusicAudioPath
*pPath
;
1674 WAVEFORMATEX format
;
1675 DMUS_PORTPARAMS params
= {0};
1676 IDirectSoundBuffer
*buffer
, *primary_buffer
;
1679 FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This
, dwType
, pchannel_count
, fActivate
, ret_iface
);
1681 if (!ret_iface
) return E_POINTER
;
1682 if (!This
->audio_paths_enabled
) return DMUS_E_AUDIOPATH_INACTIVE
;
1686 /* Secondary buffer description */
1687 memset(&format
, 0, sizeof(format
));
1688 format
.wFormatTag
= WAVE_FORMAT_PCM
;
1689 format
.nChannels
= 1;
1690 format
.nSamplesPerSec
= 44000;
1691 format
.nAvgBytesPerSec
= 44000*2;
1692 format
.nBlockAlign
= 2;
1693 format
.wBitsPerSample
= 16;
1696 memset(&desc
, 0, sizeof(desc
));
1697 desc
.dwSize
= sizeof(desc
);
1698 desc
.dwFlags
= DSBCAPS_CTRLFX
| DSBCAPS_CTRLVOLUME
| DSBCAPS_GLOBALFOCUS
;
1699 desc
.dwBufferBytes
= DSBSIZE_MIN
;
1700 desc
.dwReserved
= 0;
1701 desc
.lpwfxFormat
= &format
;
1702 desc
.guid3DAlgorithm
= GUID_NULL
;
1705 case DMUS_APATH_DYNAMIC_3D
:
1706 desc
.dwFlags
|= DSBCAPS_CTRL3D
| DSBCAPS_CTRLFREQUENCY
| DSBCAPS_MUTE3DATMAXDISTANCE
;
1708 case DMUS_APATH_DYNAMIC_MONO
:
1709 desc
.dwFlags
|= DSBCAPS_CTRLPAN
| DSBCAPS_CTRLFREQUENCY
;
1711 case DMUS_APATH_SHARED_STEREOPLUSREVERB
:
1712 /* normally we have to create 2 buffers (one for music other for reverb)
1713 * in this case. See msdn
1715 case DMUS_APATH_DYNAMIC_STEREO
:
1716 desc
.dwFlags
|= DSBCAPS_CTRLPAN
| DSBCAPS_CTRLFREQUENCY
;
1717 format
.nChannels
= 2;
1718 format
.nBlockAlign
*= 2;
1719 format
.nAvgBytesPerSec
*=2;
1722 return E_INVALIDARG
;
1726 params
.dwSize
= sizeof(params
);
1727 params
.dwValidParams
= DMUS_PORTPARAMS_CHANNELGROUPS
| DMUS_PORTPARAMS_AUDIOCHANNELS
;
1728 params
.dwChannelGroups
= (pchannel_count
+ 15) / 16;
1729 params
.dwAudioChannels
= format
.nChannels
;
1730 if (FAILED(hr
= perf_dmport_create(This
, ¶ms
)))
1733 hr
= IDirectSound_CreateSoundBuffer(This
->dsound
, &desc
, &buffer
, NULL
);
1735 return DSERR_BUFFERLOST
;
1737 /* Update description for creating primary buffer */
1738 desc
.dwFlags
|= DSBCAPS_PRIMARYBUFFER
;
1739 desc
.dwFlags
&= ~DSBCAPS_CTRLFX
;
1740 desc
.dwBufferBytes
= 0;
1741 desc
.lpwfxFormat
= NULL
;
1743 hr
= IDirectSound_CreateSoundBuffer(This
->dsound
, &desc
, &primary_buffer
, NULL
);
1745 IDirectSoundBuffer_Release(buffer
);
1746 return DSERR_BUFFERLOST
;
1749 create_dmaudiopath(&IID_IDirectMusicAudioPath
, (void**)&pPath
);
1750 set_audiopath_perf_pointer(pPath
, iface
);
1751 set_audiopath_dsound_buffer(pPath
, buffer
);
1752 set_audiopath_primary_dsound_buffer(pPath
, primary_buffer
);
1755 TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface
);
1756 return IDirectMusicAudioPath_Activate(*ret_iface
, fActivate
);
1759 static HRESULT WINAPI
performance_SetDefaultAudioPath(IDirectMusicPerformance8
*iface
, IDirectMusicAudioPath
*audio_path
)
1761 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
1763 FIXME("(%p, %p): semi-stub\n", This
, audio_path
);
1765 if (!This
->audio_paths_enabled
) return DMUS_E_AUDIOPATH_INACTIVE
;
1767 if (This
->pDefaultPath
) IDirectMusicAudioPath_Release(This
->pDefaultPath
);
1768 if ((This
->pDefaultPath
= audio_path
))
1770 IDirectMusicAudioPath_AddRef(This
->pDefaultPath
);
1771 set_audiopath_perf_pointer(This
->pDefaultPath
, iface
);
1777 static HRESULT WINAPI
performance_GetDefaultAudioPath(IDirectMusicPerformance8
*iface
,
1778 IDirectMusicAudioPath
**ret_iface
)
1780 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
1782 FIXME("(%p, %p): semi-stub (%p)\n", This
, ret_iface
, This
->pDefaultPath
);
1784 if (!ret_iface
) return E_POINTER
;
1785 if (!This
->audio_paths_enabled
) return DMUS_E_AUDIOPATH_INACTIVE
;
1787 if ((*ret_iface
= This
->pDefaultPath
)) IDirectMusicAudioPath_AddRef(*ret_iface
);
1792 static HRESULT WINAPI
performance_GetParamEx(IDirectMusicPerformance8
*iface
, REFGUID rguidType
, DWORD dwTrackID
,
1793 DWORD dwGroupBits
, DWORD dwIndex
, MUSIC_TIME mtTime
, MUSIC_TIME
*pmtNext
, void *pParam
)
1795 struct performance
*This
= impl_from_IDirectMusicPerformance8(iface
);
1797 FIXME("(%p, %s, %ld, %ld, %ld, %ld, %p, %p): stub\n", This
, debugstr_dmguid(rguidType
), dwTrackID
, dwGroupBits
, dwIndex
, mtTime
, pmtNext
, pParam
);
1802 static const IDirectMusicPerformance8Vtbl performance_vtbl
=
1804 performance_QueryInterface
,
1806 performance_Release
,
1808 performance_PlaySegment
,
1810 performance_GetSegmentState
,
1811 performance_SetPrepareTime
,
1812 performance_GetPrepareTime
,
1813 performance_SetBumperLength
,
1814 performance_GetBumperLength
,
1815 performance_SendPMsg
,
1816 performance_MusicToReferenceTime
,
1817 performance_ReferenceToMusicTime
,
1818 performance_IsPlaying
,
1819 performance_GetTime
,
1820 performance_AllocPMsg
,
1821 performance_FreePMsg
,
1822 performance_GetGraph
,
1823 performance_SetGraph
,
1824 performance_SetNotificationHandle
,
1825 performance_GetNotificationPMsg
,
1826 performance_AddNotificationType
,
1827 performance_RemoveNotificationType
,
1828 performance_AddPort
,
1829 performance_RemovePort
,
1830 performance_AssignPChannelBlock
,
1831 performance_AssignPChannel
,
1832 performance_PChannelInfo
,
1833 performance_DownloadInstrument
,
1834 performance_Invalidate
,
1835 performance_GetParam
,
1836 performance_SetParam
,
1837 performance_GetGlobalParam
,
1838 performance_SetGlobalParam
,
1839 performance_GetLatencyTime
,
1840 performance_GetQueueTime
,
1841 performance_AdjustTime
,
1842 performance_CloseDown
,
1843 performance_GetResolvedTime
,
1844 performance_MIDIToMusic
,
1845 performance_MusicToMIDI
,
1846 performance_TimeToRhythm
,
1847 performance_RhythmToTime
,
1848 performance_InitAudio
,
1849 performance_PlaySegmentEx
,
1851 performance_ClonePMsg
,
1852 performance_CreateAudioPath
,
1853 performance_CreateStandardAudioPath
,
1854 performance_SetDefaultAudioPath
,
1855 performance_GetDefaultAudioPath
,
1856 performance_GetParamEx
,
1859 static inline struct performance
*impl_from_IDirectMusicGraph(IDirectMusicGraph
*iface
)
1861 return CONTAINING_RECORD(iface
, struct performance
, IDirectMusicGraph_iface
);
1864 static HRESULT WINAPI
performance_graph_QueryInterface(IDirectMusicGraph
*iface
, REFIID riid
, void **ret_iface
)
1866 struct performance
*This
= impl_from_IDirectMusicGraph(iface
);
1867 return IDirectMusicPerformance8_QueryInterface(&This
->IDirectMusicPerformance8_iface
, riid
, ret_iface
);
1870 static ULONG WINAPI
performance_graph_AddRef(IDirectMusicGraph
*iface
)
1872 struct performance
*This
= impl_from_IDirectMusicGraph(iface
);
1873 return IDirectMusicPerformance8_AddRef(&This
->IDirectMusicPerformance8_iface
);
1876 static ULONG WINAPI
performance_graph_Release(IDirectMusicGraph
*iface
)
1878 struct performance
*This
= impl_from_IDirectMusicGraph(iface
);
1879 return IDirectMusicPerformance8_Release(&This
->IDirectMusicPerformance8_iface
);
1882 static HRESULT WINAPI
performance_graph_StampPMsg(IDirectMusicGraph
*iface
, DMUS_PMSG
*msg
)
1884 struct performance
*This
= impl_from_IDirectMusicGraph(iface
);
1887 TRACE("(%p, %p)\n", This
, msg
);
1889 if (!msg
) return E_POINTER
;
1891 /* FIXME: Implement segment and audio path graphs support */
1892 if (!This
->pToolGraph
) hr
= DMUS_S_LAST_TOOL
;
1893 else if (FAILED(hr
= IDirectMusicGraph_StampPMsg(This
->pToolGraph
, msg
))) return hr
;
1897 IDirectMusicTool_Release(msg
->pGraph
);
1901 if (hr
== DMUS_S_LAST_TOOL
)
1903 const DWORD delivery_flags
= DMUS_PMSGF_TOOL_IMMEDIATE
| DMUS_PMSGF_TOOL_QUEUE
| DMUS_PMSGF_TOOL_ATTIME
;
1904 msg
->dwFlags
&= ~delivery_flags
;
1905 msg
->dwFlags
|= DMUS_PMSGF_TOOL_QUEUE
;
1907 if (msg
->pTool
) IDirectMusicTool_Release(msg
->pTool
);
1908 msg
->pTool
= &This
->IDirectMusicTool_iface
;
1909 IDirectMusicTool_AddRef(msg
->pTool
);
1915 msg
->pGraph
= &This
->IDirectMusicGraph_iface
;
1916 IDirectMusicTool_AddRef(msg
->pGraph
);
1922 static HRESULT WINAPI
performance_graph_InsertTool(IDirectMusicGraph
*iface
, IDirectMusicTool
*tool
,
1923 DWORD
*channels
, DWORD channels_count
, LONG index
)
1925 struct performance
*This
= impl_from_IDirectMusicGraph(iface
);
1926 TRACE("(%p, %p, %p, %lu, %ld)\n", This
, tool
, channels
, channels_count
, index
);
1930 static HRESULT WINAPI
performance_graph_GetTool(IDirectMusicGraph
*iface
, DWORD index
, IDirectMusicTool
**tool
)
1932 struct performance
*This
= impl_from_IDirectMusicGraph(iface
);
1933 TRACE("(%p, %lu, %p)\n", This
, index
, tool
);
1937 static HRESULT WINAPI
performance_graph_RemoveTool(IDirectMusicGraph
*iface
, IDirectMusicTool
*tool
)
1939 struct performance
*This
= impl_from_IDirectMusicGraph(iface
);
1940 TRACE("(%p, %p)\n", This
, tool
);
1944 static const IDirectMusicGraphVtbl performance_graph_vtbl
=
1946 performance_graph_QueryInterface
,
1947 performance_graph_AddRef
,
1948 performance_graph_Release
,
1949 performance_graph_StampPMsg
,
1950 performance_graph_InsertTool
,
1951 performance_graph_GetTool
,
1952 performance_graph_RemoveTool
,
1955 static inline struct performance
*impl_from_IDirectMusicTool(IDirectMusicTool
*iface
)
1957 return CONTAINING_RECORD(iface
, struct performance
, IDirectMusicTool_iface
);
1960 static HRESULT WINAPI
performance_tool_QueryInterface(IDirectMusicTool
*iface
, REFIID riid
, void **ret_iface
)
1962 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
1963 return IDirectMusicPerformance8_QueryInterface(&This
->IDirectMusicPerformance8_iface
, riid
, ret_iface
);
1966 static ULONG WINAPI
performance_tool_AddRef(IDirectMusicTool
*iface
)
1968 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
1969 return IDirectMusicPerformance8_AddRef(&This
->IDirectMusicPerformance8_iface
);
1972 static ULONG WINAPI
performance_tool_Release(IDirectMusicTool
*iface
)
1974 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
1975 return IDirectMusicPerformance8_Release(&This
->IDirectMusicPerformance8_iface
);
1978 static HRESULT WINAPI
performance_tool_Init(IDirectMusicTool
*iface
, IDirectMusicGraph
*graph
)
1980 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
1981 TRACE("(%p, %p)\n", This
, graph
);
1985 static HRESULT WINAPI
performance_tool_GetMsgDeliveryType(IDirectMusicTool
*iface
, DWORD
*type
)
1987 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
1988 TRACE("(%p, %p)\n", This
, type
);
1989 *type
= DMUS_PMSGF_TOOL_IMMEDIATE
;
1993 static HRESULT WINAPI
performance_tool_GetMediaTypeArraySize(IDirectMusicTool
*iface
, DWORD
*size
)
1995 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
1996 TRACE("(%p, %p)\n", This
, size
);
2001 static HRESULT WINAPI
performance_tool_GetMediaTypes(IDirectMusicTool
*iface
, DWORD
**types
, DWORD size
)
2003 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
2004 TRACE("(%p, %p, %lu)\n", This
, types
, size
);
2008 static HRESULT WINAPI
performance_tool_ProcessPMsg(IDirectMusicTool
*iface
,
2009 IDirectMusicPerformance
*performance
, DMUS_PMSG
*msg
)
2011 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
2012 struct message
*message
= message_from_DMUS_PMSG(msg
);
2015 TRACE("(%p, %p, %p)\n", This
, performance
, msg
);
2017 switch (msg
->dwType
)
2019 case DMUS_PMSGT_MIDI
:
2021 static const UINT event_size
= sizeof(DMUS_EVENTHEADER
) + sizeof(DWORD
);
2022 DMUS_BUFFERDESC desc
= {.dwSize
= sizeof(desc
), .cbBuffer
= 2 * event_size
};
2023 DMUS_MIDI_PMSG
*midi
= (DMUS_MIDI_PMSG
*)msg
;
2024 REFERENCE_TIME latency_time
;
2025 IDirectMusicBuffer
*buffer
;
2026 IDirectMusicPort
*port
;
2027 DWORD group
, channel
;
2030 if (FAILED(hr
= IDirectMusicPerformance_PChannelInfo(performance
, msg
->dwPChannel
,
2031 &port
, &group
, &channel
)))
2033 WARN("Failed to get message port, hr %#lx\n", hr
);
2036 performance_update_latency_time(This
, port
, &latency_time
);
2039 value
|= (UINT
)midi
->bStatus
;
2040 value
|= (UINT
)midi
->bByte1
<< 8;
2041 value
|= (UINT
)midi
->bByte2
<< 16;
2043 if (SUCCEEDED(hr
= IDirectMusic_CreateMusicBuffer(This
->dmusic
, &desc
, &buffer
, NULL
)))
2045 if (msg
->rtTime
== -1) msg
->rtTime
= latency_time
;
2046 hr
= IDirectMusicBuffer_PackStructured(buffer
, msg
->rtTime
, group
, value
);
2047 if (SUCCEEDED(hr
)) hr
= IDirectMusicPort_PlayBuffer(port
, buffer
);
2048 IDirectMusicBuffer_Release(buffer
);
2051 IDirectMusicPort_Release(port
);
2055 case DMUS_PMSGT_NOTE
:
2057 DMUS_NOTE_PMSG
*note
= (DMUS_NOTE_PMSG
*)msg
;
2059 msg
->mtTime
+= note
->nOffset
;
2061 if (note
->bFlags
& DMUS_NOTEF_NOTEON
)
2063 if (FAILED(hr
= performance_send_midi_pmsg(This
, msg
, DMUS_PMSGF_MUSICTIME
| DMUS_PMSGF_TOOL_IMMEDIATE
,
2064 MIDI_NOTE_ON
, note
->bMidiValue
, note
->bVelocity
)))
2065 WARN("Failed to translate message to MIDI, hr %#lx\n", hr
);
2067 if (note
->mtDuration
)
2069 msg
->mtTime
-= note
->nOffset
;
2070 msg
->mtTime
+= max(1, note
->mtDuration
- 1);
2071 if (FAILED(hr
= IDirectMusicPerformance8_MusicToReferenceTime(performance
, msg
->mtTime
, &msg
->rtTime
)))
2073 note
->bFlags
&= ~DMUS_NOTEF_NOTEON
;
2074 return DMUS_S_REQUEUE
;
2078 if (FAILED(hr
= performance_send_midi_pmsg(This
, msg
, DMUS_PMSGF_MUSICTIME
| DMUS_PMSGF_TOOL_IMMEDIATE
,
2079 MIDI_NOTE_OFF
, note
->bMidiValue
, 0)))
2080 WARN("Failed to translate message to MIDI, hr %#lx\n", hr
);
2085 case DMUS_PMSGT_CURVE
:
2087 DMUS_CURVE_PMSG
*curve
= (DMUS_CURVE_PMSG
*)msg
;
2089 msg
->mtTime
+= curve
->nOffset
;
2090 switch (curve
->dwType
)
2092 case DMUS_CURVET_CCCURVE
:
2093 if (FAILED(hr
= performance_send_midi_pmsg(This
, msg
, DMUS_PMSGF_MUSICTIME
| DMUS_PMSGF_TOOL_IMMEDIATE
,
2094 MIDI_CONTROL_CHANGE
, curve
->bCCData
, curve
->nStartValue
)))
2095 WARN("Failed to translate message to MIDI, hr %#lx\n", hr
);
2097 case DMUS_CURVET_RPNCURVE
:
2098 case DMUS_CURVET_NRPNCURVE
:
2099 FIXME("Unhandled curve type %#lx\n", curve
->dwType
);
2106 case DMUS_PMSGT_PATCH
:
2108 DMUS_PATCH_PMSG
*patch
= (DMUS_PATCH_PMSG
*)msg
;
2110 if (FAILED(hr
= performance_send_midi_pmsg(This
, msg
, DMUS_PMSGF_REFTIME
| DMUS_PMSGF_MUSICTIME
| DMUS_PMSGF_TOOL_IMMEDIATE
,
2111 MIDI_CONTROL_CHANGE
, MIDI_CC_BANK_MSB
, patch
->byMSB
)))
2112 WARN("Failed to translate message to MIDI, hr %#lx\n", hr
);
2114 if (FAILED(hr
= performance_send_midi_pmsg(This
, msg
, DMUS_PMSGF_REFTIME
| DMUS_PMSGF_MUSICTIME
| DMUS_PMSGF_TOOL_IMMEDIATE
,
2115 MIDI_CONTROL_CHANGE
, MIDI_CC_BANK_LSB
, patch
->byLSB
)))
2116 WARN("Failed to translate message to MIDI, hr %#lx\n", hr
);
2118 if (FAILED(hr
= performance_send_midi_pmsg(This
, msg
, DMUS_PMSGF_REFTIME
| DMUS_PMSGF_MUSICTIME
| DMUS_PMSGF_TOOL_IMMEDIATE
,
2119 MIDI_PROGRAM_CHANGE
, patch
->byInstrument
, 0)))
2120 WARN("Failed to translate message to MIDI, hr %#lx\n", hr
);
2125 case DMUS_PMSGT_NOTIFICATION
:
2127 DMUS_NOTIFICATION_PMSG
*notif
= (DMUS_NOTIFICATION_PMSG
*)msg
;
2128 struct message
*previous
;
2129 BOOL enabled
= FALSE
;
2131 if (IsEqualGUID(¬if
->guidNotificationType
, &GUID_NOTIFICATION_PERFORMANCE
))
2132 enabled
= This
->notification_performance
;
2133 if (IsEqualGUID(¬if
->guidNotificationType
, &GUID_NOTIFICATION_SEGMENT
))
2134 enabled
= This
->notification_segment
;
2135 if (!enabled
) return DMUS_S_FREE
;
2137 if (msg
->dwFlags
& DMUS_PMSGF_TOOL_IMMEDIATE
)
2139 /* re-send the message for queueing at the expected time */
2140 msg
->dwFlags
&= ~DMUS_PMSGF_TOOL_IMMEDIATE
;
2141 msg
->dwFlags
|= DMUS_PMSGF_TOOL_ATTIME
;
2143 if (FAILED(hr
= IDirectMusicPerformance8_SendPMsg(performance
, msg
)))
2145 ERR("Failed to send notification message, hr %#lx\n", hr
);
2152 list_add_tail(&This
->notifications
, &message
->entry
);
2154 /* discard old notification messages */
2157 previous
= LIST_ENTRY(list_head(&This
->notifications
), struct message
, entry
);
2158 if (This
->notification_timeout
<= 0) break; /* negative values may be used to keep everything */
2159 if (message
->msg
.rtTime
- previous
->msg
.rtTime
<= This
->notification_timeout
) break;
2160 list_remove(&previous
->entry
);
2161 list_init(&previous
->entry
);
2162 } while (SUCCEEDED(hr
= IDirectMusicPerformance_FreePMsg(performance
, &previous
->msg
)));
2164 SetEvent(This
->notification_event
);
2168 case DMUS_PMSGT_WAVE
:
2169 if (FAILED(hr
= IDirectSoundBuffer_Play((IDirectSoundBuffer
*)msg
->punkUser
, 0, 0, 0)))
2170 WARN("Failed to play wave buffer, hr %#lx\n", hr
);
2173 case DMUS_PMSGT_INTERNAL_SEGMENT_TICK
:
2174 msg
->rtTime
+= 10000000;
2175 msg
->dwFlags
&= ~DMUS_PMSGF_MUSICTIME
;
2177 /* re-send the tick message until segment_state_tick returns S_FALSE */
2178 if (FAILED(hr
= segment_state_tick((IDirectMusicSegmentState
*)msg
->punkUser
,
2179 (IDirectMusicPerformance8
*)performance
)))
2180 ERR("Failed to tick segment state %p, hr %#lx\n", msg
->punkUser
, hr
);
2181 else if (hr
== S_FALSE
)
2182 return DMUS_S_FREE
; /* done ticking */
2183 else if (FAILED(hr
= IDirectMusicPerformance_SendPMsg(performance
, msg
)))
2184 ERR("Failed to queue tick for segment state %p, hr %#lx\n", msg
->punkUser
, hr
);
2188 case DMUS_PMSGT_INTERNAL_SEGMENT_END
:
2189 if (FAILED(hr
= segment_state_end_play((IDirectMusicSegmentState
*)msg
->punkUser
,
2190 (IDirectMusicPerformance8
*)performance
)))
2191 WARN("Failed to end segment state %p, hr %#lx\n", msg
->punkUser
, hr
);
2195 FIXME("Unhandled message type %#lx\n", msg
->dwType
);
2202 static HRESULT WINAPI
performance_tool_Flush(IDirectMusicTool
*iface
,
2203 IDirectMusicPerformance
*performance
, DMUS_PMSG
*msg
, REFERENCE_TIME time
)
2205 struct performance
*This
= impl_from_IDirectMusicTool(iface
);
2206 FIXME("(%p, %p, %p, %I64d): stub\n", This
, performance
, msg
, time
);
2210 static const IDirectMusicToolVtbl performance_tool_vtbl
=
2212 performance_tool_QueryInterface
,
2213 performance_tool_AddRef
,
2214 performance_tool_Release
,
2215 performance_tool_Init
,
2216 performance_tool_GetMsgDeliveryType
,
2217 performance_tool_GetMediaTypeArraySize
,
2218 performance_tool_GetMediaTypes
,
2219 performance_tool_ProcessPMsg
,
2220 performance_tool_Flush
,
2223 /* for ClassFactory */
2224 HRESULT
create_dmperformance(REFIID iid
, void **ret_iface
)
2226 struct performance
*obj
;
2229 TRACE("(%s, %p)\n", debugstr_guid(iid
), ret_iface
);
2232 if (!(obj
= calloc(1, sizeof(*obj
)))) return E_OUTOFMEMORY
;
2233 obj
->IDirectMusicPerformance8_iface
.lpVtbl
= &performance_vtbl
;
2234 obj
->IDirectMusicGraph_iface
.lpVtbl
= &performance_graph_vtbl
;
2235 obj
->IDirectMusicTool_iface
.lpVtbl
= &performance_tool_vtbl
;
2238 obj
->pDefaultPath
= NULL
;
2239 InitializeCriticalSection(&obj
->safe
);
2240 obj
->safe
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": performance->safe");
2241 wine_rb_init(&obj
->channel_blocks
, channel_block_compare
);
2243 list_init(&obj
->messages
);
2244 list_init(&obj
->notifications
);
2246 obj
->latency_offset
= 50;
2247 obj
->dwBumperLength
= 50; /* 50 ms default */
2248 obj
->dwPrepareTime
= 1000; /* 1000 ms default */
2250 hr
= IDirectMusicPerformance8_QueryInterface(&obj
->IDirectMusicPerformance8_iface
, iid
, ret_iface
);
2251 IDirectMusicPerformance_Release(&obj
->IDirectMusicPerformance8_iface
);
2255 static inline struct performance
*unsafe_impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8
*iface
)
2257 if (iface
->lpVtbl
!= &performance_vtbl
) return NULL
;
2258 return CONTAINING_RECORD(iface
, struct performance
, IDirectMusicPerformance8_iface
);
2261 HRESULT
performance_get_dsound(IDirectMusicPerformance8
*iface
, IDirectSound
**dsound
)
2263 struct performance
*This
= unsafe_impl_from_IDirectMusicPerformance8(iface
);
2264 if (!This
|| !(*dsound
= This
->dsound
)) return E_FAIL
;
2265 IDirectSound_AddRef(*dsound
);