418 lines
14 KiB
C
418 lines
14 KiB
C
|
/*
|
||
|
* Copyright (C) 2003-2004 Rok Mandeljc
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2.1 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||
|
*/
|
||
|
|
||
|
#include "dmime_private.h"
|
||
|
|
||
|
WINE_DEFAULT_DEBUG_CHANNEL(dmime);
|
||
|
|
||
|
static DWORD next_track_id;
|
||
|
|
||
|
struct track_entry
|
||
|
{
|
||
|
struct list entry;
|
||
|
|
||
|
IDirectMusicTrack *track;
|
||
|
void *state_data;
|
||
|
DWORD track_id;
|
||
|
};
|
||
|
|
||
|
static void track_entry_destroy(struct track_entry *entry)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (FAILED(hr = IDirectMusicTrack_EndPlay(entry->track, entry->state_data)))
|
||
|
WARN("track %p EndPlay failed, hr %#lx\n", entry->track, hr);
|
||
|
|
||
|
IDirectMusicTrack_Release(entry->track);
|
||
|
free(entry);
|
||
|
}
|
||
|
|
||
|
struct segment_state
|
||
|
{
|
||
|
IDirectMusicSegmentState8 IDirectMusicSegmentState8_iface;
|
||
|
LONG ref;
|
||
|
|
||
|
IDirectMusicSegment *segment;
|
||
|
MUSIC_TIME start_time;
|
||
|
MUSIC_TIME start_point;
|
||
|
MUSIC_TIME end_point;
|
||
|
MUSIC_TIME played;
|
||
|
BOOL auto_download;
|
||
|
DWORD repeats;
|
||
|
|
||
|
struct list tracks;
|
||
|
};
|
||
|
|
||
|
static inline struct segment_state *impl_from_IDirectMusicSegmentState8(IDirectMusicSegmentState8 *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct segment_state, IDirectMusicSegmentState8_iface);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_QueryInterface(IDirectMusicSegmentState8 *iface, REFIID riid, void **ppobj)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
|
||
|
TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj);
|
||
|
|
||
|
if (!ppobj)
|
||
|
return E_POINTER;
|
||
|
|
||
|
*ppobj = NULL;
|
||
|
|
||
|
if (IsEqualIID(riid, &IID_IUnknown) ||
|
||
|
IsEqualIID(riid, &IID_IDirectMusicSegmentState) ||
|
||
|
IsEqualIID(riid, &IID_IDirectMusicSegmentState8))
|
||
|
{
|
||
|
IDirectMusicSegmentState8_AddRef(iface);
|
||
|
*ppobj = &This->IDirectMusicSegmentState8_iface;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ppobj);
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI segment_state_AddRef(IDirectMusicSegmentState8 *iface)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
ULONG ref = InterlockedIncrement(&This->ref);
|
||
|
|
||
|
TRACE("(%p): %ld\n", This, ref);
|
||
|
|
||
|
return ref;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
ULONG ref = InterlockedDecrement(&This->ref);
|
||
|
|
||
|
TRACE("(%p): %ld\n", This, ref);
|
||
|
|
||
|
if (!ref)
|
||
|
{
|
||
|
segment_state_end_play((IDirectMusicSegmentState *)iface, NULL);
|
||
|
if (This->segment) IDirectMusicSegment_Release(This->segment);
|
||
|
free(This);
|
||
|
}
|
||
|
|
||
|
return ref;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_GetRepeats(IDirectMusicSegmentState8 *iface, DWORD *repeats)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
FIXME("(%p, %p): semi-stub\n", This, repeats);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_GetSegment(IDirectMusicSegmentState8 *iface, IDirectMusicSegment **segment)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
|
||
|
TRACE("(%p, %p)\n", This, segment);
|
||
|
|
||
|
if (!(*segment = This->segment)) return DMUS_E_NOT_FOUND;
|
||
|
|
||
|
IDirectMusicSegment_AddRef(This->segment);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_GetStartTime(IDirectMusicSegmentState8 *iface, MUSIC_TIME *start_time)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
|
||
|
TRACE("(%p, %p)\n", This, start_time);
|
||
|
|
||
|
*start_time = This->start_time;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_GetSeek(IDirectMusicSegmentState8 *iface, MUSIC_TIME *seek_time)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
FIXME("(%p, %p): semi-stub\n", This, seek_time);
|
||
|
*seek_time = 0;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME *start_point)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
|
||
|
TRACE("(%p, %p)\n", This, start_point);
|
||
|
|
||
|
*start_point = This->start_point;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_SetTrackConfig(IDirectMusicSegmentState8 *iface,
|
||
|
REFGUID rguidTrackClassID, DWORD dwGroupBits, DWORD dwIndex, DWORD dwFlagsOn, DWORD dwFlagsOff)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
FIXME("(%p, %s, %ld, %ld, %ld, %ld): stub\n", This, debugstr_dmguid(rguidTrackClassID), dwGroupBits, dwIndex, dwFlagsOn, dwFlagsOff);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI segment_state_GetObjectInPath(IDirectMusicSegmentState8 *iface, DWORD dwPChannel,
|
||
|
DWORD dwStage, DWORD dwBuffer, REFGUID guidObject, DWORD dwIndex, REFGUID iidInterface, void **ppObject)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface);
|
||
|
FIXME("(%p, %ld, %ld, %ld, %s, %ld, %s, %p): stub\n", This, dwPChannel, dwStage, dwBuffer, debugstr_dmguid(guidObject), dwIndex, debugstr_dmguid(iidInterface), ppObject);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static const IDirectMusicSegmentState8Vtbl segment_state_vtbl =
|
||
|
{
|
||
|
segment_state_QueryInterface,
|
||
|
segment_state_AddRef,
|
||
|
segment_state_Release,
|
||
|
segment_state_GetRepeats,
|
||
|
segment_state_GetSegment,
|
||
|
segment_state_GetStartTime,
|
||
|
segment_state_GetSeek,
|
||
|
segment_state_GetStartPoint,
|
||
|
segment_state_SetTrackConfig,
|
||
|
segment_state_GetObjectInPath,
|
||
|
};
|
||
|
|
||
|
/* for ClassFactory */
|
||
|
HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface)
|
||
|
{
|
||
|
struct segment_state *obj;
|
||
|
HRESULT hr;
|
||
|
|
||
|
*ret_iface = NULL;
|
||
|
if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
|
||
|
obj->IDirectMusicSegmentState8_iface.lpVtbl = &segment_state_vtbl;
|
||
|
obj->ref = 1;
|
||
|
obj->start_time = -1;
|
||
|
list_init(&obj->tracks);
|
||
|
|
||
|
TRACE("Created IDirectMusicSegmentState %p\n", obj);
|
||
|
hr = IDirectMusicSegmentState8_QueryInterface(&obj->IDirectMusicSegmentState8_iface, riid, ret_iface);
|
||
|
IDirectMusicSegmentState8_Release(&obj->IDirectMusicSegmentState8_iface);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time,
|
||
|
IDirectMusicPerformance8 *performance, IDirectMusicSegmentState **ret_iface)
|
||
|
{
|
||
|
IDirectMusicSegmentState *iface;
|
||
|
struct segment_state *This;
|
||
|
IDirectMusicTrack *track;
|
||
|
HRESULT hr;
|
||
|
UINT i;
|
||
|
|
||
|
TRACE("(%p, %lu, %p)\n", segment, start_time, ret_iface);
|
||
|
|
||
|
if (FAILED(hr = create_dmsegmentstate(&IID_IDirectMusicSegmentState, (void **)&iface))) return hr;
|
||
|
This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface);
|
||
|
|
||
|
This->segment = segment;
|
||
|
IDirectMusicSegment_AddRef(This->segment);
|
||
|
|
||
|
if (SUCCEEDED(hr = IDirectMusicPerformance8_GetGlobalParam(performance, &GUID_PerfAutoDownload,
|
||
|
&This->auto_download, sizeof(This->auto_download))) && This->auto_download)
|
||
|
hr = IDirectMusicSegment_SetParam(segment, &GUID_DownloadToAudioPath, -1,
|
||
|
DMUS_SEG_ALLTRACKS, 0, performance);
|
||
|
|
||
|
This->start_time = start_time;
|
||
|
if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetStartPoint(segment, &This->start_point);
|
||
|
if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetLength(segment, &This->end_point);
|
||
|
if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetRepeats(segment, &This->repeats);
|
||
|
|
||
|
for (i = 0; SUCCEEDED(hr); i++)
|
||
|
{
|
||
|
DWORD track_id = ++next_track_id;
|
||
|
struct track_entry *entry;
|
||
|
|
||
|
if ((hr = IDirectMusicSegment_GetTrack(segment, &GUID_NULL, -1, i, &track)) != S_OK)
|
||
|
{
|
||
|
if (hr == DMUS_E_NOT_FOUND) hr = S_OK;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!(entry = malloc(sizeof(*entry))))
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
else if (SUCCEEDED(hr = IDirectMusicTrack_InitPlay(track, iface, (IDirectMusicPerformance *)performance,
|
||
|
&entry->state_data, track_id, 0)))
|
||
|
{
|
||
|
entry->track = track;
|
||
|
entry->track_id = track_id;
|
||
|
list_add_tail(&This->tracks, &entry->entry);
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
WARN("Failed to initialize track %p, hr %#lx\n", track, hr);
|
||
|
IDirectMusicTrack_Release(track);
|
||
|
free(entry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr)) *ret_iface = iface;
|
||
|
else IDirectMusicSegmentState_Release(iface);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT segment_state_play_until(struct segment_state *This, IDirectMusicPerformance8 *performance,
|
||
|
MUSIC_TIME end_time, DWORD track_flags)
|
||
|
{
|
||
|
IDirectMusicSegmentState *iface = (IDirectMusicSegmentState *)&This->IDirectMusicSegmentState8_iface;
|
||
|
struct track_entry *entry;
|
||
|
HRESULT hr = S_OK;
|
||
|
MUSIC_TIME played;
|
||
|
|
||
|
played = min(end_time - This->start_time, This->end_point - This->start_point);
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry)
|
||
|
{
|
||
|
if (FAILED(hr = IDirectMusicTrack_Play(entry->track, entry->state_data,
|
||
|
This->start_point + This->played, This->start_point + played,
|
||
|
This->start_time, track_flags, (IDirectMusicPerformance *)performance,
|
||
|
iface, entry->track_id)))
|
||
|
{
|
||
|
WARN("Failed to play track %p, hr %#lx\n", entry->track, hr);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
This->played = played;
|
||
|
if (This->start_point + This->played >= This->end_point) return S_FALSE;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT segment_state_play_chunk(struct segment_state *This, IDirectMusicPerformance8 *performance,
|
||
|
REFERENCE_TIME duration, DWORD track_flags)
|
||
|
{
|
||
|
IDirectMusicSegmentState *iface = (IDirectMusicSegmentState *)&This->IDirectMusicSegmentState8_iface;
|
||
|
MUSIC_TIME next_time;
|
||
|
REFERENCE_TIME time;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(performance,
|
||
|
This->start_time + This->played, &time)))
|
||
|
return hr;
|
||
|
if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(performance,
|
||
|
time + duration, &next_time)))
|
||
|
return hr;
|
||
|
|
||
|
while ((hr = segment_state_play_until(This, performance, next_time, track_flags)) == S_FALSE)
|
||
|
{
|
||
|
if (!This->repeats)
|
||
|
{
|
||
|
MUSIC_TIME end_time = This->start_time + This->played;
|
||
|
|
||
|
if (FAILED(hr = performance_send_segment_end(performance, end_time, iface, FALSE)))
|
||
|
{
|
||
|
ERR("Failed to send segment end, hr %#lx\n", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr = IDirectMusicSegment_GetLoopPoints(This->segment, &This->played,
|
||
|
&This->end_point)))
|
||
|
break;
|
||
|
This->start_time += This->end_point - This->start_point;
|
||
|
This->repeats--;
|
||
|
|
||
|
if (next_time <= This->start_time || This->end_point <= This->start_point) break;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface);
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("%p %p\n", iface, performance);
|
||
|
|
||
|
if (FAILED(hr = performance_send_segment_start(performance, This->start_time, iface)))
|
||
|
{
|
||
|
ERR("Failed to send segment start, hr %#lx\n", hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr = segment_state_play_chunk(This, performance, 10000000,
|
||
|
DMUS_TRACKF_START | DMUS_TRACKF_SEEK | DMUS_TRACKF_DIRTY)))
|
||
|
return hr;
|
||
|
|
||
|
if (hr == S_FALSE) return S_OK;
|
||
|
return performance_send_segment_tick(performance, This->start_time, iface);
|
||
|
}
|
||
|
|
||
|
HRESULT segment_state_tick(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface);
|
||
|
|
||
|
TRACE("%p %p\n", iface, performance);
|
||
|
|
||
|
return segment_state_play_chunk(This, performance, 10000000, 0);
|
||
|
}
|
||
|
|
||
|
HRESULT segment_state_stop(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface);
|
||
|
|
||
|
TRACE("%p %p\n", iface, performance);
|
||
|
|
||
|
This->played = This->end_point - This->start_point;
|
||
|
return performance_send_segment_end(performance, This->start_time + This->played, iface, TRUE);
|
||
|
}
|
||
|
|
||
|
HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface);
|
||
|
struct track_entry *entry, *next;
|
||
|
HRESULT hr;
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->tracks, struct track_entry, entry)
|
||
|
{
|
||
|
list_remove(&entry->entry);
|
||
|
track_entry_destroy(entry);
|
||
|
}
|
||
|
|
||
|
if (performance && This->auto_download && FAILED(hr = IDirectMusicSegment_SetParam(This->segment,
|
||
|
&GUID_UnloadFromAudioPath, -1, DMUS_SEG_ALLTRACKS, 0, performance)))
|
||
|
ERR("Failed to unload segment from performance, hr %#lx\n", hr);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
BOOL segment_state_has_segment(IDirectMusicSegmentState *iface, IDirectMusicSegment *segment)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface);
|
||
|
return !segment || This->segment == segment;
|
||
|
}
|
||
|
|
||
|
BOOL segment_state_has_track(IDirectMusicSegmentState *iface, DWORD track_id)
|
||
|
{
|
||
|
struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface);
|
||
|
struct track_entry *entry;
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry)
|
||
|
if (entry->track_id == track_id) return TRUE;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|