765 lines
25 KiB
C
765 lines
25 KiB
C
|
/*
|
||
|
* IDirectMusicCollection Implementation
|
||
|
*
|
||
|
* 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 "dmusic_private.h"
|
||
|
#include "soundfont.h"
|
||
|
|
||
|
WINE_DEFAULT_DEBUG_CHANNEL(dmusic);
|
||
|
|
||
|
struct instrument_entry
|
||
|
{
|
||
|
struct list entry;
|
||
|
DWORD patch;
|
||
|
DMUS_OBJECTDESC desc;
|
||
|
IDirectMusicInstrument *instrument;
|
||
|
};
|
||
|
|
||
|
struct pool
|
||
|
{
|
||
|
POOLTABLE table;
|
||
|
POOLCUE cues[];
|
||
|
};
|
||
|
|
||
|
C_ASSERT(sizeof(struct pool) == offsetof(struct pool, cues[0]));
|
||
|
|
||
|
struct wave_entry
|
||
|
{
|
||
|
struct list entry;
|
||
|
IDirectMusicObject *wave;
|
||
|
DWORD offset;
|
||
|
};
|
||
|
|
||
|
struct collection
|
||
|
{
|
||
|
IDirectMusicCollection IDirectMusicCollection_iface;
|
||
|
struct dmobject dmobj;
|
||
|
LONG internal_ref;
|
||
|
LONG ref;
|
||
|
|
||
|
DLSHEADER header;
|
||
|
struct pool *pool;
|
||
|
struct list instruments;
|
||
|
struct list waves;
|
||
|
};
|
||
|
|
||
|
void collection_internal_addref(struct collection *collection)
|
||
|
{
|
||
|
ULONG ref = InterlockedIncrement( &collection->internal_ref );
|
||
|
TRACE( "collection %p, internal ref %lu.\n", collection, ref );
|
||
|
}
|
||
|
|
||
|
void collection_internal_release(struct collection *collection)
|
||
|
{
|
||
|
ULONG ref = InterlockedDecrement( &collection->internal_ref );
|
||
|
TRACE( "collection %p, internal ref %lu.\n", collection, ref );
|
||
|
|
||
|
if (!ref)
|
||
|
free(collection);
|
||
|
}
|
||
|
|
||
|
HRESULT collection_get_wave(struct collection *collection, DWORD index, IDirectMusicObject **out)
|
||
|
{
|
||
|
struct wave_entry *wave_entry;
|
||
|
DWORD offset;
|
||
|
|
||
|
if (index >= collection->pool->table.cCues) return E_INVALIDARG;
|
||
|
offset = collection->pool->cues[index].ulOffset;
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(wave_entry, &collection->waves, struct wave_entry, entry)
|
||
|
{
|
||
|
if (offset == wave_entry->offset)
|
||
|
{
|
||
|
*out = wave_entry->wave;
|
||
|
IUnknown_AddRef(wave_entry->wave);
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
static inline struct collection *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct collection, IDirectMusicCollection_iface);
|
||
|
}
|
||
|
|
||
|
static inline struct collection *impl_from_IPersistStream(IPersistStream *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct collection, dmobj.IPersistStream_iface);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI collection_QueryInterface(IDirectMusicCollection *iface,
|
||
|
REFIID riid, void **ret_iface)
|
||
|
{
|
||
|
struct collection *This = impl_from_IDirectMusicCollection(iface);
|
||
|
|
||
|
TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface);
|
||
|
|
||
|
*ret_iface = NULL;
|
||
|
|
||
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDirectMusicCollection))
|
||
|
*ret_iface = iface;
|
||
|
else if (IsEqualIID(riid, &IID_IDirectMusicObject))
|
||
|
*ret_iface = &This->dmobj.IDirectMusicObject_iface;
|
||
|
else if (IsEqualIID(riid, &IID_IPersistStream))
|
||
|
*ret_iface = &This->dmobj.IPersistStream_iface;
|
||
|
else
|
||
|
{
|
||
|
WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface);
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
IUnknown_AddRef((IUnknown*)*ret_iface);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI collection_AddRef(IDirectMusicCollection *iface)
|
||
|
{
|
||
|
struct collection *This = impl_from_IDirectMusicCollection(iface);
|
||
|
ULONG ref = InterlockedIncrement(&This->ref);
|
||
|
|
||
|
TRACE("(%p): new ref = %lu\n", iface, ref);
|
||
|
|
||
|
return ref;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI collection_Release(IDirectMusicCollection *iface)
|
||
|
{
|
||
|
struct collection *This = impl_from_IDirectMusicCollection(iface);
|
||
|
ULONG ref = InterlockedDecrement(&This->ref);
|
||
|
|
||
|
TRACE("(%p): new ref = %lu\n", iface, ref);
|
||
|
|
||
|
if (!ref)
|
||
|
{
|
||
|
struct instrument_entry *instrument_entry;
|
||
|
struct wave_entry *wave_entry;
|
||
|
void *next;
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY_SAFE(instrument_entry, next, &This->instruments, struct instrument_entry, entry)
|
||
|
{
|
||
|
list_remove(&instrument_entry->entry);
|
||
|
IDirectMusicInstrument_Release(instrument_entry->instrument);
|
||
|
free(instrument_entry);
|
||
|
}
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY_SAFE(wave_entry, next, &This->waves, struct wave_entry, entry)
|
||
|
{
|
||
|
list_remove(&wave_entry->entry);
|
||
|
IDirectMusicInstrument_Release(wave_entry->wave);
|
||
|
free(wave_entry);
|
||
|
}
|
||
|
|
||
|
collection_internal_release(This);
|
||
|
}
|
||
|
|
||
|
return ref;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface,
|
||
|
DWORD patch, IDirectMusicInstrument **instrument)
|
||
|
{
|
||
|
struct collection *This = impl_from_IDirectMusicCollection(iface);
|
||
|
struct instrument_entry *entry;
|
||
|
|
||
|
TRACE("(%p, %lu, %p)\n", iface, patch, instrument);
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry)
|
||
|
{
|
||
|
if (patch == entry->patch)
|
||
|
{
|
||
|
*instrument = entry->instrument;
|
||
|
IDirectMusicInstrument_AddRef(entry->instrument);
|
||
|
TRACE(": returning instrument %p\n", entry->instrument);
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE(": instrument not found\n");
|
||
|
return DMUS_E_INVALIDPATCH;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface,
|
||
|
DWORD index, DWORD *patch, LPWSTR name, DWORD name_length)
|
||
|
{
|
||
|
struct collection *This = impl_from_IDirectMusicCollection(iface);
|
||
|
struct instrument_entry *entry;
|
||
|
|
||
|
TRACE("(%p, %ld, %p, %p, %ld)\n", iface, index, patch, name, name_length);
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry)
|
||
|
{
|
||
|
if (index--) continue;
|
||
|
*patch = entry->patch;
|
||
|
if (name) lstrcpynW(name, entry->desc.wszName, name_length);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
static const IDirectMusicCollectionVtbl collection_vtbl =
|
||
|
{
|
||
|
collection_QueryInterface,
|
||
|
collection_AddRef,
|
||
|
collection_Release,
|
||
|
collection_GetInstrument,
|
||
|
collection_EnumInstrument,
|
||
|
};
|
||
|
|
||
|
static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct chunk_entry *parent)
|
||
|
{
|
||
|
struct chunk_entry chunk = {.parent = parent};
|
||
|
struct instrument_entry *entry;
|
||
|
HRESULT hr;
|
||
|
|
||
|
while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
|
||
|
{
|
||
|
switch (MAKE_IDTYPE(chunk.id, chunk.type))
|
||
|
{
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS):
|
||
|
if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY;
|
||
|
hr = instrument_create_from_chunk(stream, &chunk, This, &entry->desc, &entry->instrument);
|
||
|
if (SUCCEEDED(hr)) hr = IDirectMusicInstrument_GetPatch(entry->instrument, &entry->patch);
|
||
|
if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry);
|
||
|
else free(entry);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) break;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT parse_wvpl_list(struct collection *This, IStream *stream, struct chunk_entry *parent)
|
||
|
{
|
||
|
struct chunk_entry chunk = {.parent = parent};
|
||
|
struct wave_entry *entry;
|
||
|
HRESULT hr;
|
||
|
|
||
|
while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
|
||
|
{
|
||
|
switch (MAKE_IDTYPE(chunk.id, chunk.type))
|
||
|
{
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, FOURCC_wave):
|
||
|
if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY;
|
||
|
if (FAILED(hr = wave_create_from_chunk(stream, &chunk, &entry->wave))) free(entry);
|
||
|
else
|
||
|
{
|
||
|
entry->offset = chunk.offset.QuadPart - parent->offset.QuadPart - 12;
|
||
|
list_add_tail(&This->waves, &entry->entry);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
FIXME("Skipping unknown chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) break;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT parse_ptbl_chunk(struct collection *This, IStream *stream, struct chunk_entry *chunk)
|
||
|
{
|
||
|
struct pool *pool;
|
||
|
POOLTABLE table;
|
||
|
HRESULT hr;
|
||
|
UINT size;
|
||
|
|
||
|
if (chunk->size < sizeof(table)) return E_INVALIDARG;
|
||
|
if (FAILED(hr = stream_read(stream, &table, sizeof(table)))) return hr;
|
||
|
if (chunk->size != table.cbSize + sizeof(POOLCUE) * table.cCues) return E_INVALIDARG;
|
||
|
if (table.cbSize != sizeof(table)) return E_INVALIDARG;
|
||
|
|
||
|
size = offsetof(struct pool, cues[table.cCues]);
|
||
|
if (!(pool = malloc(size))) return E_OUTOFMEMORY;
|
||
|
pool->table = table;
|
||
|
|
||
|
size = sizeof(POOLCUE) * table.cCues;
|
||
|
if (FAILED(hr = stream_read(stream, pool->cues, size))) free(pool);
|
||
|
else This->pool = pool;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT parse_dls_chunk(struct collection *This, IStream *stream, struct chunk_entry *parent)
|
||
|
{
|
||
|
struct chunk_entry chunk = {.parent = parent};
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (FAILED(hr = dmobj_parsedescriptor(stream, parent, &This->dmobj.desc,
|
||
|
DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION|DMUS_OBJ_OBJECT|DMUS_OBJ_GUID_DLID))
|
||
|
|| FAILED(hr = stream_reset_chunk_data(stream, parent)))
|
||
|
return hr;
|
||
|
|
||
|
while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
|
||
|
{
|
||
|
switch (MAKE_IDTYPE(chunk.id, chunk.type))
|
||
|
{
|
||
|
case FOURCC_DLID:
|
||
|
case FOURCC_VERS:
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INFO_LIST):
|
||
|
/* already parsed by dmobj_parsedescriptor */
|
||
|
break;
|
||
|
|
||
|
case FOURCC_COLH:
|
||
|
hr = stream_chunk_get_data(stream, &chunk, &This->header, sizeof(This->header));
|
||
|
break;
|
||
|
|
||
|
case FOURCC_PTBL:
|
||
|
hr = parse_ptbl_chunk(This, stream, &chunk);
|
||
|
break;
|
||
|
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LINS):
|
||
|
hr = parse_lins_list(This, stream, &chunk);
|
||
|
break;
|
||
|
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, FOURCC_WVPL):
|
||
|
hr = parse_wvpl_list(This, stream, &chunk);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) break;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT parse_sdta_list(struct collection *This, IStream *stream, struct chunk_entry *parent,
|
||
|
struct soundfont *soundfont)
|
||
|
{
|
||
|
struct chunk_entry chunk = {.parent = parent};
|
||
|
HRESULT hr;
|
||
|
|
||
|
while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
|
||
|
{
|
||
|
switch (MAKE_IDTYPE(chunk.id, chunk.type))
|
||
|
{
|
||
|
case mmioFOURCC('s','m','p','l'):
|
||
|
if (soundfont->sdta) return E_INVALIDARG;
|
||
|
if (!(soundfont->sdta = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->sdta, chunk.size);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
FIXME("Skipping unknown chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) break;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT parse_pdta_list(struct collection *This, IStream *stream, struct chunk_entry *parent,
|
||
|
struct soundfont *soundfont)
|
||
|
{
|
||
|
struct chunk_entry chunk = {.parent = parent};
|
||
|
HRESULT hr;
|
||
|
|
||
|
while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
|
||
|
{
|
||
|
switch (MAKE_IDTYPE(chunk.id, chunk.type))
|
||
|
{
|
||
|
case mmioFOURCC('p','h','d','r'):
|
||
|
if (soundfont->phdr) return E_INVALIDARG;
|
||
|
if (!(soundfont->phdr = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->phdr, chunk.size);
|
||
|
soundfont->preset_count = chunk.size / sizeof(*soundfont->phdr) - 1;
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('p','b','a','g'):
|
||
|
if (soundfont->pbag) return E_INVALIDARG;
|
||
|
if (!(soundfont->pbag = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->pbag, chunk.size);
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('p','m','o','d'):
|
||
|
if (soundfont->pmod) return E_INVALIDARG;
|
||
|
if (!(soundfont->pmod = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->pmod, chunk.size);
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('p','g','e','n'):
|
||
|
if (soundfont->pgen) return E_INVALIDARG;
|
||
|
if (!(soundfont->pgen = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->pgen, chunk.size);
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('i','n','s','t'):
|
||
|
if (soundfont->inst) return E_INVALIDARG;
|
||
|
if (!(soundfont->inst = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->inst, chunk.size);
|
||
|
soundfont->instrument_count = chunk.size / sizeof(*soundfont->inst) - 1;
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('i','b','a','g'):
|
||
|
if (soundfont->ibag) return E_INVALIDARG;
|
||
|
if (!(soundfont->ibag = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->ibag, chunk.size);
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('i','m','o','d'):
|
||
|
if (soundfont->imod) return E_INVALIDARG;
|
||
|
if (!(soundfont->imod = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->imod, chunk.size);
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('i','g','e','n'):
|
||
|
if (soundfont->igen) return E_INVALIDARG;
|
||
|
if (!(soundfont->igen = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->igen, chunk.size);
|
||
|
break;
|
||
|
|
||
|
case mmioFOURCC('s','h','d','r'):
|
||
|
if (soundfont->shdr) return E_INVALIDARG;
|
||
|
if (!(soundfont->shdr = malloc(chunk.size))) return E_OUTOFMEMORY;
|
||
|
hr = stream_chunk_get_data(stream, &chunk, soundfont->shdr, chunk.size);
|
||
|
soundfont->sample_count = chunk.size / sizeof(*soundfont->shdr) - 1;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
FIXME("Skipping unknown chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) break;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT parse_sfbk_chunk(struct collection *This, IStream *stream, struct chunk_entry *parent)
|
||
|
{
|
||
|
struct chunk_entry chunk = {.parent = parent};
|
||
|
struct soundfont soundfont = {0};
|
||
|
UINT i, j, k;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (FAILED(hr = dmobj_parsedescriptor(stream, parent, &This->dmobj.desc,
|
||
|
DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION|DMUS_OBJ_OBJECT|DMUS_OBJ_GUID_DLID))
|
||
|
|| FAILED(hr = stream_reset_chunk_data(stream, parent)))
|
||
|
return hr;
|
||
|
|
||
|
while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
|
||
|
{
|
||
|
switch (MAKE_IDTYPE(chunk.id, chunk.type))
|
||
|
{
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INFO_LIST):
|
||
|
/* already parsed by dmobj_parsedescriptor */
|
||
|
break;
|
||
|
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, mmioFOURCC('s','d','t','a')):
|
||
|
hr = parse_sdta_list(This, stream, &chunk, &soundfont);
|
||
|
break;
|
||
|
|
||
|
case MAKE_IDTYPE(FOURCC_LIST, mmioFOURCC('p','d','t','a')):
|
||
|
hr = parse_pdta_list(This, stream, &chunk, &soundfont);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) break;
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
TRACE("presets:\n");
|
||
|
for (i = 0; i < soundfont.preset_count; i++)
|
||
|
{
|
||
|
struct sf_preset *preset = soundfont.phdr + i;
|
||
|
|
||
|
TRACE("preset[%u]:\n", i);
|
||
|
TRACE(" - name: %s\n", debugstr_a(preset->name));
|
||
|
TRACE(" - preset: %u\n", preset->preset);
|
||
|
TRACE(" - bank: %u\n", preset->bank);
|
||
|
TRACE(" - preset_bag_ndx: %u\n", preset->bag_ndx);
|
||
|
TRACE(" - library: %lu\n", preset->library);
|
||
|
TRACE(" - genre: %lu\n", preset->genre);
|
||
|
TRACE(" - morphology: %#lx\n", preset->morphology);
|
||
|
|
||
|
for (j = preset->bag_ndx; j < (preset + 1)->bag_ndx; j++)
|
||
|
{
|
||
|
struct sf_bag *bag = soundfont.pbag + j;
|
||
|
TRACE(" - bag[%u]:\n", j);
|
||
|
TRACE(" - gen_ndx: %u\n", bag->gen_ndx);
|
||
|
TRACE(" - mod_ndx: %u\n", bag->mod_ndx);
|
||
|
|
||
|
for (k = bag->gen_ndx; k < (bag + 1)->gen_ndx; k++)
|
||
|
{
|
||
|
struct sf_gen *gen = soundfont.pgen + k;
|
||
|
TRACE(" - gen[%u]: %s\n", k, debugstr_sf_gen(gen));
|
||
|
}
|
||
|
|
||
|
for (k = bag->mod_ndx; k < (bag + 1)->mod_ndx; k++)
|
||
|
{
|
||
|
struct sf_mod *mod = soundfont.pmod + k;
|
||
|
TRACE(" - mod[%u]: %s\n", k, debugstr_sf_mod(mod));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE("instruments:\n");
|
||
|
for (i = 0; i < soundfont.instrument_count; i++)
|
||
|
{
|
||
|
struct sf_instrument *instrument = soundfont.inst + i;
|
||
|
TRACE("instrument[%u]:\n", i);
|
||
|
TRACE(" - name: %s\n", debugstr_a(instrument->name));
|
||
|
TRACE(" - bag_ndx: %u\n", instrument->bag_ndx);
|
||
|
|
||
|
for (j = instrument->bag_ndx; j < (instrument + 1)->bag_ndx; j++)
|
||
|
{
|
||
|
struct sf_bag *bag = soundfont.ibag + j;
|
||
|
TRACE(" - bag[%u]:\n", j);
|
||
|
TRACE(" - wGenNdx: %u\n", bag->gen_ndx);
|
||
|
TRACE(" - wModNdx: %u\n", bag->mod_ndx);
|
||
|
|
||
|
for (k = bag->gen_ndx; k < (bag + 1)->gen_ndx; k++)
|
||
|
{
|
||
|
struct sf_gen *gen = soundfont.igen + k;
|
||
|
TRACE(" - gen[%u]: %s\n", k, debugstr_sf_gen(gen));
|
||
|
}
|
||
|
|
||
|
for (k = bag->mod_ndx; k < (bag + 1)->mod_ndx; k++)
|
||
|
{
|
||
|
struct sf_mod *mod = soundfont.imod + k;
|
||
|
TRACE(" - mod[%u]: %s\n", k, debugstr_sf_mod(mod));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE("samples:\n");
|
||
|
for (i = 0; i < soundfont.sample_count; i++)
|
||
|
{
|
||
|
struct sf_sample *sample = soundfont.shdr + i;
|
||
|
|
||
|
TRACE("sample[%u]:\n", i);
|
||
|
TRACE(" - name: %s\n", debugstr_a(sample->name));
|
||
|
TRACE(" - start: %lu\n", sample->start);
|
||
|
TRACE(" - end: %lu\n", sample->end);
|
||
|
TRACE(" - start_loop: %lu\n", sample->start_loop);
|
||
|
TRACE(" - end_loop: %lu\n", sample->end_loop);
|
||
|
TRACE(" - sample_rate: %lu\n", sample->sample_rate);
|
||
|
TRACE(" - original_key: %u\n", sample->original_key);
|
||
|
TRACE(" - correction: %d\n", sample->correction);
|
||
|
TRACE(" - sample_link: %#x\n", sample->sample_link);
|
||
|
TRACE(" - sample_type: %#x\n", sample->sample_type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; SUCCEEDED(hr) && i < soundfont.preset_count; i++)
|
||
|
{
|
||
|
struct instrument_entry *entry;
|
||
|
|
||
|
if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY;
|
||
|
hr = instrument_create_from_soundfont(&soundfont, i, This, &entry->desc, &entry->instrument);
|
||
|
if (SUCCEEDED(hr)) hr = IDirectMusicInstrument_GetPatch(entry->instrument, &entry->patch);
|
||
|
if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry);
|
||
|
else free(entry);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
UINT size = offsetof(struct pool, cues[soundfont.sample_count]);
|
||
|
if (!(This->pool = calloc(1, size))) return E_OUTOFMEMORY;
|
||
|
This->pool->table.cbSize = sizeof(This->pool->table);
|
||
|
}
|
||
|
|
||
|
for (i = 0; SUCCEEDED(hr) && i < soundfont.sample_count; i++)
|
||
|
{
|
||
|
struct wave_entry *entry;
|
||
|
|
||
|
if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY;
|
||
|
hr = wave_create_from_soundfont(&soundfont, i, &entry->wave);
|
||
|
if (FAILED(hr)) free(entry);
|
||
|
else
|
||
|
{
|
||
|
entry->offset = i;
|
||
|
This->pool->table.cCues++;
|
||
|
This->pool->cues[i].ulOffset = i;
|
||
|
list_add_tail(&This->waves, &entry->entry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(soundfont.phdr);
|
||
|
free(soundfont.pbag);
|
||
|
free(soundfont.pmod);
|
||
|
free(soundfont.pgen);
|
||
|
free(soundfont.inst);
|
||
|
free(soundfont.ibag);
|
||
|
free(soundfont.imod);
|
||
|
free(soundfont.igen);
|
||
|
free(soundfont.shdr);
|
||
|
free(soundfont.sdta);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *iface,
|
||
|
IStream *stream, DMUS_OBJECTDESC *desc)
|
||
|
{
|
||
|
struct chunk_entry riff = {0};
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("(%p, %p, %p)\n", iface, stream, desc);
|
||
|
|
||
|
if (!stream || !desc)
|
||
|
return E_POINTER;
|
||
|
|
||
|
if ((hr = stream_get_chunk(stream, &riff)) != S_OK)
|
||
|
return hr;
|
||
|
if (riff.id != FOURCC_RIFF || (riff.type != FOURCC_DLS && riff.type != mmioFOURCC('s','f','b','k'))) {
|
||
|
TRACE("loading failed: unexpected %s\n", debugstr_chunk(&riff));
|
||
|
stream_skip_chunk(stream, &riff);
|
||
|
return DMUS_E_NOTADLSCOL;
|
||
|
}
|
||
|
|
||
|
hr = dmobj_parsedescriptor(stream, &riff, desc, DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION);
|
||
|
if (FAILED(hr))
|
||
|
return hr;
|
||
|
|
||
|
desc->guidClass = CLSID_DirectMusicCollection;
|
||
|
desc->dwValidData |= DMUS_OBJ_CLASS;
|
||
|
|
||
|
TRACE("returning descriptor:\n");
|
||
|
dump_DMUS_OBJECTDESC(desc);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static const IDirectMusicObjectVtbl collection_object_vtbl =
|
||
|
{
|
||
|
dmobj_IDirectMusicObject_QueryInterface,
|
||
|
dmobj_IDirectMusicObject_AddRef,
|
||
|
dmobj_IDirectMusicObject_Release,
|
||
|
dmobj_IDirectMusicObject_GetDescriptor,
|
||
|
dmobj_IDirectMusicObject_SetDescriptor,
|
||
|
collection_object_ParseDescriptor,
|
||
|
};
|
||
|
|
||
|
static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *stream)
|
||
|
{
|
||
|
struct collection *This = impl_from_IPersistStream(iface);
|
||
|
struct chunk_entry chunk = {0};
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("(%p, %p)\n", This, stream);
|
||
|
|
||
|
if ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
|
||
|
{
|
||
|
switch (MAKE_IDTYPE(chunk.id, chunk.type))
|
||
|
{
|
||
|
case MAKE_IDTYPE(FOURCC_RIFF, FOURCC_DLS):
|
||
|
hr = parse_dls_chunk(This, stream, &chunk);
|
||
|
break;
|
||
|
|
||
|
case MAKE_IDTYPE(FOURCC_RIFF, mmioFOURCC('s','f','b','k')):
|
||
|
hr = parse_sfbk_chunk(This, stream, &chunk);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
WARN("Invalid collection chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type));
|
||
|
hr = DMUS_E_UNSUPPORTED_STREAM;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr)) return hr;
|
||
|
|
||
|
if (TRACE_ON(dmusic))
|
||
|
{
|
||
|
struct instrument_entry *entry;
|
||
|
struct wave_entry *wave_entry;
|
||
|
int i = 0;
|
||
|
|
||
|
TRACE("*** IDirectMusicCollection (%p) ***\n", &This->IDirectMusicCollection_iface);
|
||
|
dump_DMUS_OBJECTDESC(&This->dmobj.desc);
|
||
|
|
||
|
TRACE(" - Collection header:\n");
|
||
|
TRACE(" - cInstruments: %ld\n", This->header.cInstruments);
|
||
|
TRACE(" - Instruments:\n");
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry)
|
||
|
{
|
||
|
TRACE(" - Instrument[%i]: %p\n", i, entry->instrument);
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
TRACE(" - cues:\n");
|
||
|
for (i = 0; This->pool && i < This->pool->table.cCues; i++)
|
||
|
TRACE(" - index: %u, offset: %lu\n", i, This->pool->cues[i].ulOffset);
|
||
|
|
||
|
TRACE(" - waves:\n");
|
||
|
LIST_FOR_EACH_ENTRY(wave_entry, &This->waves, struct wave_entry, entry)
|
||
|
TRACE(" - offset: %lu, wave %p\n", wave_entry->offset, wave_entry->wave);
|
||
|
}
|
||
|
|
||
|
stream_skip_chunk(stream, &chunk);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static const IPersistStreamVtbl collection_stream_vtbl =
|
||
|
{
|
||
|
dmobj_IPersistStream_QueryInterface,
|
||
|
dmobj_IPersistStream_AddRef,
|
||
|
dmobj_IPersistStream_Release,
|
||
|
unimpl_IPersistStream_GetClassID,
|
||
|
unimpl_IPersistStream_IsDirty,
|
||
|
collection_stream_Load,
|
||
|
unimpl_IPersistStream_Save,
|
||
|
unimpl_IPersistStream_GetSizeMax,
|
||
|
};
|
||
|
|
||
|
HRESULT collection_create(IUnknown **ret_iface)
|
||
|
{
|
||
|
struct collection *collection;
|
||
|
|
||
|
*ret_iface = NULL;
|
||
|
if (!(collection = calloc(1, sizeof(*collection)))) return E_OUTOFMEMORY;
|
||
|
collection->IDirectMusicCollection_iface.lpVtbl = &collection_vtbl;
|
||
|
collection->internal_ref = 1;
|
||
|
collection->ref = 1;
|
||
|
dmobject_init(&collection->dmobj, &CLSID_DirectMusicCollection,
|
||
|
(IUnknown *)&collection->IDirectMusicCollection_iface);
|
||
|
collection->dmobj.IDirectMusicObject_iface.lpVtbl = &collection_object_vtbl;
|
||
|
collection->dmobj.IPersistStream_iface.lpVtbl = &collection_stream_vtbl;
|
||
|
list_init(&collection->instruments);
|
||
|
list_init(&collection->waves);
|
||
|
|
||
|
TRACE("Created DirectMusicCollection %p\n", collection);
|
||
|
*ret_iface = (IUnknown *)&collection->IDirectMusicCollection_iface;
|
||
|
return S_OK;
|
||
|
}
|