-- v4: dmusic: Implement SoundFont2 instrument parsing. dmusic: Implement SoundFont2 wave sample parsing. dmusic: Implement SoundFont2 collection parsing. dmusic: Avoid crashing in traces if wave doesn't have a WSMPL. dmusic: Avoid leaking articulations when freeing regions. dmloader: Add fallbacks if the configured GMFilePath doesn't exist. dmloader: Remove invalid default DLS collection check. dmusic/tests: Test default gm.dls sound font instruments. dmloader: Use a simpler file stream implementation.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmloader/dmloader_private.h | 18 +- dlls/dmloader/loader.c | 105 ++++------ dlls/dmloader/loaderstream.c | 335 ++++++++++++++----------------- 3 files changed, 189 insertions(+), 269 deletions(-)
diff --git a/dlls/dmloader/dmloader_private.h b/dlls/dmloader/dmloader_private.h index 87fcf087bc0..c590040389b 100644 --- a/dlls/dmloader/dmloader_private.h +++ b/dlls/dmloader/dmloader_private.h @@ -59,25 +59,8 @@ typedef struct IDirectMusicLoaderGenericStream IDirectMusicLoaderGenericStream; */ extern HRESULT create_dmloader(REFIID riid, void **ret_iface); extern HRESULT create_dmcontainer(REFIID riid, void **ret_iface); -extern HRESULT DMUSIC_CreateDirectMusicLoaderFileStream(void **ppobj); extern HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream(void **ppobj);
-/***************************************************************************** - * IDirectMusicLoaderFileStream implementation structure - */ -struct IDirectMusicLoaderFileStream { - /* VTABLEs */ - const IStreamVtbl *StreamVtbl; - /* reference counter */ - LONG dwRef; - /* file */ - WCHAR wzFileName[MAX_PATH]; /* for clone */ - HANDLE hFile; -}; - -/* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderFileStream_Attach(LPSTREAM iface, LPCWSTR wzFile); - /***************************************************************************** * IDirectMusicLoaderResourceStream implementation structure */ @@ -98,6 +81,7 @@ extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach(LPSTREAM iface, LP LONGLONG llMemLength, LONGLONG llPos);
extern HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, IStream **ret_iface); +extern HRESULT file_stream_create(const WCHAR *path, IStream **ret_iface);
#include "debug.h"
diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index ef1a8c3dfe0..9c97f47e5f7 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -305,40 +305,29 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE TRACE(": no cache/alias entry found for requested object\n"); } - if (pDesc->dwValidData & DMUS_OBJ_URL) { - TRACE(": loading from URLs not supported yet\n"); - return DMUS_E_LOADER_FORMATNOTSUPPORTED; - } - else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { - /* load object from file */ - /* generate filename; if it's full path, don't add search - directory path, otherwise do */ - WCHAR wszFileName[MAX_PATH]; - - if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) { - lstrcpyW(wszFileName, pDesc->wszFileName); - } else { - WCHAR *p; - get_search_path(This, &pDesc->guidClass, wszFileName); - p = wszFileName + lstrlenW(wszFileName); - if (p > wszFileName && p[-1] != '\') *p++ = '\'; - lstrcpyW(p, pDesc->wszFileName); - } - TRACE(": loading from file (%s)\n", debugstr_w(wszFileName)); - /* create stream and associate it with file */ - result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream); - if (FAILED(result)) { - ERR(": could not create file stream\n"); - return result; - } - result = IDirectMusicLoaderFileStream_Attach(pStream, wszFileName); - if (FAILED(result)) - { - ERR(": could not attach stream to file\n"); - IStream_Release (pStream); - return result; - } - } + if (pDesc->dwValidData & DMUS_OBJ_URL) + { + TRACE(": loading from URLs not supported yet\n"); + return DMUS_E_LOADER_FORMATNOTSUPPORTED; + } + else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) + { + WCHAR file_name[MAX_PATH]; + + if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) + lstrcpyW(file_name, pDesc->wszFileName); + else + { + WCHAR *p; + get_search_path(This, &pDesc->guidClass, file_name); + p = file_name + lstrlenW(file_name); + if (p > file_name && p[-1] != '\') *p++ = '\'; + lstrcpyW(p, pDesc->wszFileName); + } + + TRACE(": loading from file (%s)\n", debugstr_w(file_name)); + if (FAILED(hr = file_stream_create(file_name, &pStream))) return hr; + } else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) { /* load object from resource */ TRACE(": loading from resource\n"); @@ -363,9 +352,8 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE } else { - /* nowhere to load from */ FIXME(": unknown/unsupported way of loading\n"); - return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */ + return DMUS_E_LOADER_NOFILENAME; }
if (FAILED(hr = loader_stream_create((IDirectMusicLoader *)iface, pStream, &loader_stream))) @@ -459,36 +447,23 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE if (TRACE_ON(dmloader)) dump_DMUS_OBJECTDESC(pDesc);
- /* create stream and load additional info from it */ - if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { - /* generate filename; if it's full path, don't add search - directory path, otherwise do */ - WCHAR wszFileName[MAX_PATH]; + if (pDesc->dwValidData & DMUS_OBJ_FILENAME) + { + WCHAR file_name[MAX_PATH];
- if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) { - lstrcpyW(wszFileName, pDesc->wszFileName); - } else { - WCHAR *p; - get_search_path(This, &pDesc->guidClass, wszFileName); - p = wszFileName + lstrlenW(wszFileName); - if (p > wszFileName && p[-1] != '\') *p++ = '\'; - lstrcpyW(p, pDesc->wszFileName); - } - /* create stream */ - hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream); - if (FAILED(hr)) { - ERR(": could not create file stream\n"); - return DMUS_E_LOADER_FAILEDOPEN; - } - /* attach stream */ - hr = IDirectMusicLoaderFileStream_Attach(pStream, wszFileName); - if (FAILED(hr)) - { - ERR(": could not attach stream to file %s, make sure it exists\n", debugstr_w(wszFileName)); - IStream_Release (pStream); - return DMUS_E_LOADER_FAILEDOPEN; - } - } + if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) + lstrcpyW(file_name, pDesc->wszFileName); + else + { + WCHAR *p; + get_search_path(This, &pDesc->guidClass, file_name); + p = file_name + lstrlenW(file_name); + if (p > file_name && p[-1] != '\') *p++ = '\'; + lstrcpyW(p, pDesc->wszFileName); + } + + if (FAILED(hr = file_stream_create(file_name, &pStream))) return hr; + } else if (pDesc->dwValidData & DMUS_OBJ_STREAM) { pStream = pDesc->pStream; diff --git a/dlls/dmloader/loaderstream.c b/dlls/dmloader/loaderstream.c index 5b7862c16f8..80e9f91e77a 100644 --- a/dlls/dmloader/loaderstream.c +++ b/dlls/dmloader/loaderstream.c @@ -1,8 +1,6 @@ -/* IDirectMusicLoaderFileStream - * IDirectMusicLoaderResourceStream - * IDirectMusicLoaderGenericStream - * +/* * Copyright (C) 2003-2004 Rok Mandeljc + * Copyright 2023 Rémi Bernon for CodeWeavers * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,31 +17,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
- -/* SIDE NOTES: - * After extensive testing and structure dumping I came to a conclusion that - * DirectMusic as in present state implements three types of streams: - * 1. IDirectMusicLoaderFileStream: stream that was most obvious, since - * it's used for loading from files; it is sort of wrapper around - * CreateFile, ReadFile, WriteFile and SetFilePointer and it supports - * both read and write - * 2. IDirectMusicLoaderResourceStream: a stream that had to exist, since - * according to MSDN, IDirectMusicLoader supports loading from resource - * as well; in this case, data is represented as a big chunk of bytes, - * from which we "read" (copy) data and keep the trace of our position; - * it supports read only - * 3. IDirectMusicLoaderGenericStream: this one was the most problematic, - * since I thought it was URL-related; besides, there's no obvious need - * for it, since input streams can simply be cloned, lest loading from - * stream is requested; but if one really thinks about it, input stream - * could be none of 1. or 2.; in this case, a wrapper that offers - * IDirectMusicGetLoader interface would be nice, and this is what this - * stream is; as such, all functions are supported, as long as underlying - * ("low-level") stream supports them - * - * - Rok Mandeljc; 24. April, 2004 -*/ - #include "dmloader_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmloader); @@ -284,216 +257,204 @@ HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, return S_OK; }
-static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_AddRef (LPSTREAM iface); -static ULONG WINAPI IDirectMusicLoaderResourceStream_IStream_AddRef (LPSTREAM iface); - +struct file_stream +{ + IStream IStream_iface; + LONG ref;
-/***************************************************************************** - * IDirectMusicLoaderFileStream implementation - */ -/* Custom : */ + WCHAR path[MAX_PATH]; + HANDLE file; +};
-static void IDirectMusicLoaderFileStream_Detach (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - TRACE("(%p)\n", This); - if (This->hFile != INVALID_HANDLE_VALUE) CloseHandle(This->hFile); - This->wzFileName[0] = '\0'; +static struct file_stream *file_stream_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct file_stream, IStream_iface); }
-HRESULT WINAPI IDirectMusicLoaderFileStream_Attach(LPSTREAM iface, LPCWSTR wzFile) +static HRESULT WINAPI file_stream_QueryInterface(IStream *iface, REFIID riid, void **ret_iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - TRACE("(%p, %s)\n", This, debugstr_w(wzFile)); - IDirectMusicLoaderFileStream_Detach (iface); - This->hFile = CreateFileW (wzFile, (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (This->hFile == INVALID_HANDLE_VALUE) { - WARN(": failed\n"); - return DMUS_E_LOADER_FAILEDOPEN; - } - /* create IDirectMusicGetLoader */ - lstrcpynW (This->wzFileName, wzFile, MAX_PATH); - TRACE(": succeeded\n"); - return S_OK; -} + struct file_stream *This = file_stream_from_IStream(iface);
+ TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface);
-/* IUnknown/IStream part: */ -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_QueryInterface (LPSTREAM iface, REFIID riid, void** ppobj) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - - TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj); - if (IsEqualIID (riid, &IID_IUnknown) || - IsEqualIID (riid, &IID_IStream)) { - *ppobj = &This->StreamVtbl; - IDirectMusicLoaderFileStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); - return S_OK; - } + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IStream)) + { + IStream_AddRef(&This->IStream_iface); + *ret_iface = &This->IStream_iface; + return S_OK; + }
- WARN(": not found\n"); - return E_NOINTERFACE; + WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); + *ret_iface = NULL; + return E_NOINTERFACE; }
-static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_AddRef (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - TRACE("(%p): AddRef from %ld\n", This, This->dwRef); - return InterlockedIncrement (&This->dwRef); +static ULONG WINAPI file_stream_AddRef(IStream *iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p): new ref = %lu\n", This, ref); + return ref; }
-static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_Release (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - - DWORD dwRef = InterlockedDecrement (&This->dwRef); - TRACE("(%p): ReleaseRef to %ld\n", This, dwRef); - if (dwRef == 0) { - if (This->hFile) - IDirectMusicLoaderFileStream_Detach (iface); - free(This); - } - - return dwRef; -} +static ULONG WINAPI file_stream_Release(IStream *iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + ULONG ref = InterlockedDecrement(&This->ref);
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Read (LPSTREAM iface, void* pv, ULONG cb, ULONG* pcbRead) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - ULONG cbRead; - - TRACE_(dmfileraw)("(%p, %p, %#lx, %p)\n", This, pv, cb, pcbRead); - if (This->hFile == INVALID_HANDLE_VALUE) return E_FAIL; - if (pcbRead == NULL) pcbRead = &cbRead; - if (!ReadFile (This->hFile, pv, cb, pcbRead, NULL) || *pcbRead != cb) return E_FAIL; - - TRACE_(dmfileraw)(": data (size = %#lx): %s\n", *pcbRead, debugstr_an(pv, *pcbRead)); - return S_OK; + TRACE("(%p): new ref = %lu\n", This, ref); + + if (!ref) + { + CloseHandle(This->file); + free(This); + } + + return ref; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Seek (LPSTREAM iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - LARGE_INTEGER liNewPos; - - TRACE_(dmfileraw)("(%p, %s, %s, %p)\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), resolve_STREAM_SEEK(dwOrigin), plibNewPosition); +static HRESULT WINAPI file_stream_Read(IStream *iface, void *data, ULONG size, ULONG *ret_size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + DWORD dummy;
- if (This->hFile == INVALID_HANDLE_VALUE) return E_FAIL; + TRACE("(%p, %p, %#lx, %p)\n", This, data, size, ret_size);
- liNewPos.HighPart = dlibMove.HighPart; - liNewPos.LowPart = SetFilePointer (This->hFile, dlibMove.LowPart, &liNewPos.HighPart, dwOrigin); + if (!ret_size) ret_size = &dummy; + if (!ReadFile(This->file, data, size, ret_size, NULL)) return HRESULT_FROM_WIN32(GetLastError()); + return *ret_size == size ? S_OK : S_FALSE; +}
- if (liNewPos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) return E_FAIL; - if (plibNewPosition) plibNewPosition->QuadPart = liNewPos.QuadPart; - - return S_OK; +static HRESULT WINAPI file_stream_Write(IStream *iface, const void *data, ULONG size, ULONG *ret_size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Clone (LPSTREAM iface, IStream** ppstm) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - LPSTREAM pOther = NULL; - HRESULT result; +static HRESULT WINAPI file_stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD method, ULARGE_INTEGER *ret_offset) +{ + struct file_stream *This = file_stream_from_IStream(iface); + DWORD position;
- TRACE("(%p, %p)\n", iface, ppstm); - result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pOther); - if (FAILED(result)) return result; - if (This->hFile != INVALID_HANDLE_VALUE) { - ULARGE_INTEGER ullCurrentPosition; - result = IDirectMusicLoaderFileStream_Attach(pOther, This->wzFileName); - if (SUCCEEDED(result)) - { - LARGE_INTEGER liZero; - liZero.QuadPart = 0; - result = IDirectMusicLoaderFileStream_IStream_Seek (iface, liZero, STREAM_SEEK_CUR, &ullCurrentPosition); /* get current position in current stream */ - } - if (SUCCEEDED(result)) { - LARGE_INTEGER liNewPosition; - liNewPosition.QuadPart = ullCurrentPosition.QuadPart; - result = IDirectMusicLoaderFileStream_IStream_Seek (pOther, liNewPosition, STREAM_SEEK_SET, &ullCurrentPosition); - } - if (FAILED(result)) { - TRACE(": failed\n"); - IDirectMusicLoaderFileStream_IStream_Release (pOther); - return result; - } - } - TRACE(": succeeded\n"); - *ppstm = pOther; - return S_OK; -} + TRACE("(%p, %I64d, %#lx, %p)\n", This, offset.QuadPart, method, ret_offset);
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Write (LPSTREAM iface, const void* pv, ULONG cb, ULONG* pcbWritten) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - ULONG cbWrite; - - TRACE_(dmfileraw)("(%p, %p, %#lx, %p)\n", This, pv, cb, pcbWritten); - if (This->hFile == INVALID_HANDLE_VALUE) return E_FAIL; - if (pcbWritten == NULL) pcbWritten = &cbWrite; - if (!WriteFile (This->hFile, pv, cb, pcbWritten, NULL) || *pcbWritten != cb) return E_FAIL; - - TRACE_(dmfileraw)(": data (size = %#lx): %s\n", *pcbWritten, debugstr_an(pv, *pcbWritten)); + position = SetFilePointer(This->file, offset.u.LowPart, NULL, method); + if (position == INVALID_SET_FILE_POINTER) return HRESULT_FROM_WIN32(GetLastError()); + if (ret_offset) ret_offset->QuadPart = position; return S_OK; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_SetSize (LPSTREAM iface, ULARGE_INTEGER libNewSize) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_SetSize(IStream *iface, ULARGE_INTEGER size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_CopyTo (LPSTREAM iface, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, + ULARGE_INTEGER *read_size, ULARGE_INTEGER *write_size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Commit (LPSTREAM iface, DWORD grfCommitFlags) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_Commit(IStream *iface, DWORD flags) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Revert (LPSTREAM iface) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_Revert(IStream *iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_LockRegion (LPSTREAM iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_UnlockRegion (LPSTREAM iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, + ULARGE_INTEGER size, DWORD type) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; }
-static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Stat (LPSTREAM iface, STATSTG* pstatstg, DWORD grfStatFlag) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_Stat(IStream *iface, STATSTG *stat, DWORD flags) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; }
-static const IStreamVtbl DirectMusicLoaderFileStream_Stream_Vtbl = { - IDirectMusicLoaderFileStream_IStream_QueryInterface, - IDirectMusicLoaderFileStream_IStream_AddRef, - IDirectMusicLoaderFileStream_IStream_Release, - IDirectMusicLoaderFileStream_IStream_Read, - IDirectMusicLoaderFileStream_IStream_Write, - IDirectMusicLoaderFileStream_IStream_Seek, - IDirectMusicLoaderFileStream_IStream_SetSize, - IDirectMusicLoaderFileStream_IStream_CopyTo, - IDirectMusicLoaderFileStream_IStream_Commit, - IDirectMusicLoaderFileStream_IStream_Revert, - IDirectMusicLoaderFileStream_IStream_LockRegion, - IDirectMusicLoaderFileStream_IStream_UnlockRegion, - IDirectMusicLoaderFileStream_IStream_Stat, - IDirectMusicLoaderFileStream_IStream_Clone -}; +static HRESULT WINAPI file_stream_Clone(IStream *iface, IStream **ret_iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + HRESULT hr;
-HRESULT DMUSIC_CreateDirectMusicLoaderFileStream (void** ppobj) { - IDirectMusicLoaderFileStream *obj; + TRACE("(%p, %p)\n", This, ret_iface);
- TRACE("(%p)\n", ppobj); + if (SUCCEEDED(hr = file_stream_create(This->path, ret_iface))) + { + LARGE_INTEGER position = {0}; + position.LowPart = SetFilePointer(This->file, 0, NULL, SEEK_CUR); + hr = IStream_Seek(*ret_iface, position, SEEK_SET, NULL); + }
- *ppobj = NULL; - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->StreamVtbl = &DirectMusicLoaderFileStream_Stream_Vtbl; - obj->dwRef = 0; /* will be inited with QueryInterface */ + return hr; +}
- return IDirectMusicLoaderFileStream_IStream_QueryInterface ((LPSTREAM)&obj->StreamVtbl, &IID_IStream, ppobj); +static const IStreamVtbl file_stream_vtbl = +{ + file_stream_QueryInterface, + file_stream_AddRef, + file_stream_Release, + file_stream_Read, + file_stream_Write, + file_stream_Seek, + file_stream_SetSize, + file_stream_CopyTo, + file_stream_Commit, + file_stream_Revert, + file_stream_LockRegion, + file_stream_UnlockRegion, + file_stream_Stat, + file_stream_Clone, +}; + +HRESULT file_stream_create(const WCHAR *path, IStream **ret_iface) +{ + struct file_stream *stream; + + *ret_iface = NULL; + if (!(stream = calloc(1, sizeof(*stream)))) return E_OUTOFMEMORY; + stream->IStream_iface.lpVtbl = &file_stream_vtbl; + stream->ref = 1; + + wcscpy(stream->path, path); + stream->file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (stream->file == INVALID_HANDLE_VALUE) + { + free(stream); + return DMUS_E_LOADER_FAILEDOPEN; + } + + *ret_iface = &stream->IStream_iface; + return S_OK; }
+static ULONG WINAPI IDirectMusicLoaderResourceStream_IStream_AddRef (LPSTREAM iface);
/***************************************************************************** * IDirectMusicLoaderResourceStream implementation
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmusic/tests/dmusic.c | 300 +++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+)
diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 703251bb9da..0e0ff7e609a 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1338,6 +1338,305 @@ static void test_download_instrument(void) IDirectMusic_Release(dmusic); }
+struct result +{ + DWORD patch; + WCHAR name[DMUS_MAX_NAME]; +}; + +static int __cdecl result_cmp(const void *a, const void *b) +{ + const struct result *ra = a, *rb = b; + if (ra->patch != rb->patch) return ra->patch < rb->patch ? -1 : 1; + return wcscmp(ra->name, rb->name); +} + +static void test_default_gm_collection(void) +{ + DMUS_OBJECTDESC desc = + { + .dwSize = sizeof(DMUS_OBJECTDESC), + .dwValidData = DMUS_OBJ_OBJECT | DMUS_OBJ_CLASS, + .guidClass = CLSID_DirectMusicCollection, + .guidObject = GUID_DefaultGMCollection, + }; + struct result expected[] = + { + { 0, L"Piano 1 "}, + { 0x1, L"Piano 2 "}, + { 0x2, L"Piano 3 "}, + { 0x3, L"Honky-tonk "}, + { 0x4, L"E.Piano 1 "}, + { 0x5, L"E.Piano 2 "}, + { 0x6, L"Harpsichord "}, + { 0x7, L"Clav. "}, + { 0x8, L"Celesta "}, + { 0x9, L"Glockenspiel"}, + { 0xa, L"Music Box "}, + { 0xb, L"Vibraphone "}, + { 0xc, L"Marimba "}, + { 0xd, L"Xylophone "}, + { 0xe, L"Tubular-bell"}, + { 0xf, L"Santur "}, + { 0x10, L"Organ 1 "}, + { 0x11, L"Organ 2 "}, + { 0x12, L"Organ 3 "}, + { 0x13, L"Church Org.1"}, + { 0x14, L"Reed Organ "}, + { 0x15, L"Accordion Fr"}, + { 0x16, L"Harmonica "}, + { 0x17, L"Bandoneon "}, + { 0x18, L"Nylon-str.Gt"}, + { 0x19, L"Steel-str.Gt"}, + { 0x1a, L"Jazz Gt. "}, + { 0x1b, L"Clean Gt. "}, + { 0x1c, L"Muted Gt. "}, + { 0x1d, L"Overdrive Gt"}, + { 0x1e, L"DistortionGt"}, + { 0x1f, L"Gt.Harmonics"}, + { 0x20, L"Acoustic Bs."}, + { 0x21, L"Fingered Bs."}, + { 0x22, L"Picked Bs. "}, + { 0x23, L"Fretless Bs."}, + { 0x24, L"Slap Bass 1 "}, + { 0x25, L"Slap Bass 2 "}, + { 0x26, L"Synth Bass 1"}, + { 0x27, L"Synth Bass 2"}, + { 0x28, L"Violin "}, + { 0x29, L"Viola "}, + { 0x2a, L"Cello "}, + { 0x2b, L"Contrabass "}, + { 0x2c, L"Tremolo Str "}, + { 0x2d, L"PizzicatoStr"}, + { 0x2e, L"Harp "}, + { 0x2f, L"Timpani "}, + { 0x30, L"Strings "}, + { 0x31, L"Slow Strings"}, + { 0x32, L"Syn.Strings1"}, + { 0x33, L"Syn.Strings2"}, + { 0x34, L"Choir Aahs "}, + { 0x35, L"Voice Oohs "}, + { 0x36, L"SynVox "}, + { 0x37, L"OrchestraHit"}, + { 0x38, L"Trumpet "}, + { 0x39, L"Trombone "}, + { 0x3a, L"Tuba "}, + { 0x3b, L"MutedTrumpet"}, + { 0x3c, L"French Horns"}, + { 0x3d, L"Brass 1 "}, + { 0x3e, L"Synth Brass1"}, + { 0x3f, L"Synth Brass2"}, + { 0x40, L"Soprano Sax "}, + { 0x41, L"Alto Sax "}, + { 0x42, L"Tenor Sax "}, + { 0x43, L"Baritone Sax"}, + { 0x44, L"Oboe "}, + { 0x45, L"English Horn"}, + { 0x46, L"Bassoon "}, + { 0x47, L"Clarinet "}, + { 0x48, L"Piccolo "}, + { 0x49, L"Flute "}, + { 0x4a, L"Recorder "}, + { 0x4b, L"Pan Flute "}, + { 0x4c, L"Bottle Blow "}, + { 0x4d, L"Shakuhachi "}, + { 0x4e, L"Whistle "}, + { 0x4f, L"Ocarina "}, + { 0x50, L"Square Wave "}, + { 0x51, L"Saw Wave "}, + { 0x52, L"Syn.Calliope"}, + { 0x53, L"Chiffer Lead"}, + { 0x54, L"Charang "}, + { 0x55, L"Solo Vox "}, + { 0x56, L"5th Saw Wave"}, + { 0x57, L"Bass & Lead "}, + { 0x58, L"Fantasia "}, + { 0x59, L"Warm Pad "}, + { 0x5a, L"Polysynth "}, + { 0x5b, L"Space Voice "}, + { 0x5c, L"Bowed Glass "}, + { 0x5d, L"Metal Pad "}, + { 0x5e, L"Halo Pad "}, + { 0x5f, L"Sweep Pad "}, + { 0x60, L"Ice Rain "}, + { 0x61, L"Soundtrack "}, + { 0x62, L"Crystal "}, + { 0x63, L"Atmosphere "}, + { 0x64, L"Brightness "}, + { 0x65, L"Goblin "}, + { 0x66, L"Echo Drops "}, + { 0x67, L"Star Theme "}, + { 0x68, L"Sitar "}, + { 0x69, L"Banjo "}, + { 0x6a, L"Shamisen "}, + { 0x6b, L"Koto "}, + { 0x6c, L"Kalimba "}, + { 0x6d, L"Bagpipe "}, + { 0x6e, L"Fiddle "}, + { 0x6f, L"Shanai "}, + { 0x70, L"Tinkle Bell "}, + { 0x71, L"Agogo "}, + { 0x72, L"Steel Drums "}, + { 0x73, L"Woodblock "}, + { 0x74, L"Taiko "}, + { 0x75, L"Melo. Tom 1 "}, + { 0x76, L"Synth Drum "}, + { 0x77, L"Reverse Cym."}, + { 0x78, L"Gt.FretNoise"}, + { 0x79, L"Breath Noise"}, + { 0x7a, L"Seashore "}, + { 0x7b, L"Bird "}, + { 0x7c, L"Telephone 1 "}, + { 0x7d, L"Helicopter "}, + { 0x7e, L"Applause "}, + { 0x7f, L"Gun Shot "}, + { 0x10026, L"SynthBass101"}, + { 0x10039, L"Trombone 2 "}, + { 0x1003c, L"Fr.Horn 2 "}, + { 0x10050, L"Square "}, + { 0x10051, L"Saw "}, + { 0x10062, L"Syn Mallet "}, + { 0x10066, L"Echo Bell "}, + { 0x10068, L"Sitar 2 "}, + { 0x10078, L"Gt.Cut Noise"}, + { 0x10079, L"Fl.Key Click"}, + { 0x1007a, L"Rain "}, + { 0x1007b, L"Dog "}, + { 0x1007c, L"Telephone 2 "}, + { 0x1007d, L"Car-Engine "}, + { 0x1007e, L"Laughing "}, + { 0x1007f, L"Machine Gun "}, + { 0x20066, L"Echo Pan "}, + { 0x20078, L"String Slap "}, + { 0x2007a, L"Thunder "}, + { 0x2007b, L"Horse-Gallop"}, + { 0x2007c, L"DoorCreaking"}, + { 0x2007d, L"Car-Stop "}, + { 0x2007e, L"Screaming "}, + { 0x2007f, L"Lasergun "}, + { 0x3007a, L"Wind "}, + { 0x3007b, L"Bird 2 "}, + { 0x3007c, L"Door "}, + { 0x3007d, L"Car-Pass "}, + { 0x3007e, L"Punch "}, + { 0x3007f, L"Explosion "}, + { 0x4007a, L"Stream "}, + { 0x4007c, L"Scratch "}, + { 0x4007d, L"Car-Crash "}, + { 0x4007e, L"Heart Beat "}, + { 0x5007a, L"Bubble "}, + { 0x5007c, L"Wind Chimes "}, + { 0x5007d, L"Siren "}, + { 0x5007e, L"Footsteps "}, + { 0x6007d, L"Train "}, + { 0x7007d, L"Jetplane "}, + { 0x80000, L"Piano 1 "}, + { 0x80001, L"Piano 2 "}, + { 0x80002, L"Piano 3 "}, + { 0x80003, L"Honky-tonk "}, + { 0x80004, L"Detuned EP 1"}, + { 0x80005, L"Detuned EP 2"}, + { 0x80006, L"Coupled Hps."}, + { 0x8000b, L"Vibraphone "}, + { 0x8000c, L"Marimba "}, + { 0x8000e, L"Church Bell "}, + { 0x80010, L"Detuned Or.1"}, + { 0x80011, L"Detuned Or.2"}, + { 0x80013, L"Church Org.2"}, + { 0x80015, L"Accordion It"}, + { 0x80018, L"Ukulele "}, + { 0x80019, L"12-str.Gt "}, + { 0x8001a, L"Hawaiian Gt."}, + { 0x8001b, L"Chorus Gt. "}, + { 0x8001c, L"Funk Gt. "}, + { 0x8001e, L"Feedback Gt."}, + { 0x8001f, L"Gt. Feedback"}, + { 0x80026, L"Synth Bass 3"}, + { 0x80027, L"Synth Bass 4"}, + { 0x80028, L"Slow Violin "}, + { 0x80030, L"Orchestra "}, + { 0x80032, L"Syn.Strings3"}, + { 0x8003d, L"Brass 2 "}, + { 0x8003e, L"Synth Brass3"}, + { 0x8003f, L"Synth Brass4"}, + { 0x80050, L"Sine Wave "}, + { 0x80051, L"Doctor Solo "}, + { 0x8006b, L"Taisho Koto "}, + { 0x80073, L"Castanets "}, + { 0x80074, L"Concert BD "}, + { 0x80075, L"Melo. Tom 2 "}, + { 0x80076, L"808 Tom "}, + { 0x8007d, L"Starship "}, + { 0x9000e, L"Carillon "}, + { 0x90076, L"Elec Perc. "}, + { 0x9007d, L"Burst Noise "}, + { 0x100000, L"Piano 1d "}, + { 0x100004, L"E.Piano 1v "}, + { 0x100005, L"E.Piano 2v "}, + { 0x100006, L"Harpsichord "}, + { 0x100010, L"60's Organ 1"}, + { 0x100013, L"Church Org.3"}, + { 0x100018, L"Nylon Gt.o "}, + { 0x100019, L"Mandolin "}, + { 0x10001c, L"Funk Gt.2 "}, + { 0x100027, L"Rubber Bass "}, + { 0x10003e, L"AnalogBrass1"}, + { 0x10003f, L"AnalogBrass2"}, + { 0x180004, L"60's E.Piano"}, + { 0x180006, L"Harpsi.o "}, + { 0x200010, L"Organ 4 "}, + { 0x200011, L"Organ 5 "}, + { 0x200018, L"Nylon Gt.2 "}, + { 0x200034, L"Choir Aahs 2"}, + {0x80000000, L"Standard "}, + {0x80000008, L"Room "}, + {0x80000010, L"Power "}, + {0x80000018, L"Electronic "}, + {0x80000019, L"TR-808 "}, + {0x80000020, L"Jazz "}, + {0x80000028, L"Brush "}, + {0x80000030, L"Orchestra "}, + {0x80000038, L"SFX "}, + }, results[ARRAY_SIZE(expected) + 1]; + IDirectMusicCollection *collection; + IDirectMusicLoader *loader; + HRESULT hr; + DWORD i; + + hr = CoCreateInstance(&CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicLoader, (void**)&loader); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicCollection, (void **)&collection); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + for (i = 0; hr == S_OK && i < ARRAY_SIZE(results); i++) + { + results[i].patch = 0xdeadbeef; + wcscpy(results[i].name, L"DeadBeef"); + hr = IDirectMusicCollection_EnumInstrument(collection, i, &results[i].patch, + results[i].name, ARRAY_SIZE(results[i].name)); + } + if (hr == S_FALSE) i--; + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + todo_wine ok(i > 0, "got %lu\n", i); + todo_wine ok(i == ARRAY_SIZE(expected), "got %lu\n", i); + + qsort(results, i, sizeof(*results), result_cmp); + + while (i--) + { + winetest_push_context("%lu", i); + trace("got %#lx %s\n", results[i].patch, debugstr_w(results[i].name)); + ok(results[i].patch == expected[i].patch, "got %#lx\n", results[i].patch); + /* system soundfont names are not very predictable, let's not check them */ + winetest_pop_context(); + } + + if (hr == S_FALSE) IDirectMusicCollection_Release(collection); + IDirectMusicLoader_Release(loader); +} + START_TEST(dmusic) { CoInitializeEx(NULL, COINIT_MULTITHREADED); @@ -1360,6 +1659,7 @@ START_TEST(dmusic) test_synthport(); test_port_download(); test_download_instrument(); + test_default_gm_collection();
CoUninitialize(); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmloader/loader.c | 54 +++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 35 deletions(-)
diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 9c97f47e5f7..d15c99f6fb6 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -40,7 +40,6 @@ struct cache_entry { struct list entry; DMUS_OBJECTDESC Desc; IDirectMusicObject *pObject; - BOOL bInvalidDefaultDLS; /* workaround for enabling caching of "faulty" default dls collection */ };
struct loader @@ -276,12 +275,6 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE TRACE(": looking if we have object in the cache or if it can be found via alias\n"); pExistingEntry = find_cache_object(This, pDesc); if (pExistingEntry) { - - if (pExistingEntry->bInvalidDefaultDLS) { - TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n"); - return DMUS_E_LOADER_NOFILENAME; - } - if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) { TRACE(": already loaded\n"); return IDirectMusicObject_QueryInterface(pExistingEntry->pObject, riid, ppv); @@ -404,23 +397,26 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE IStream_Release (pStream); IPersistStream_Release (pPersistStream); - /* add object to cache/overwrite existing info (if cache is enabled) */ - bCache = is_cache_enabled(This, &pDesc->guidClass); - if (bCache) { - if (!pObjectEntry) { + /* add object to cache/overwrite existing info (if cache is enabled) */ + bCache = is_cache_enabled(This, &pDesc->guidClass); + if (!bCache) TRACE(": caching disabled\n"); + else + { + if (!pObjectEntry) + { pObjectEntry = calloc(1, sizeof(*pObjectEntry)); - DM_STRUCT_INIT(&pObjectEntry->Desc); - DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); - pObjectEntry->pObject = pObject; - pObjectEntry->bInvalidDefaultDLS = FALSE; - list_add_head(&This->cache, &pObjectEntry->entry); - } else { - DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); - pObjectEntry->pObject = pObject; - pObjectEntry->bInvalidDefaultDLS = FALSE; - } - TRACE(": filled in cache entry\n"); - } else TRACE(": caching disabled\n"); + DM_STRUCT_INIT(&pObjectEntry->Desc); + DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); + pObjectEntry->pObject = pObject; + list_add_head(&This->cache, &pObjectEntry->entry); + } + else + { + DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); + pObjectEntry->pObject = pObject; + } + TRACE(": filled in cache entry\n"); + }
result = IDirectMusicObject_QueryInterface (pObject, riid, ppv); if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */ @@ -873,8 +869,6 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) { struct loader *obj; DMUS_OBJECTDESC Desc; - struct cache_entry *dls; - struct list *pEntry; HRESULT hr;
TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj); @@ -895,16 +889,6 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) DMUSIC_GetDefaultGMPath(Desc.wszFileName); IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc);
- /* and now the workaroundTM for "invalid" default DLS; basically, - my tests showed that if GUID chunk is present in default DLS - collection, loader treats it as "invalid" and returns - DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check - if out input guidObject was overwritten */ - pEntry = list_head(&obj->cache); - dls = LIST_ENTRY(pEntry, struct cache_entry, entry); - if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) - dls->bInvalidDefaultDLS = TRUE; - hr = IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); IDirectMusicLoader_Release(&obj->IDirectMusicLoader8_iface); return hr;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmloader/loader.c | 66 ++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 15 deletions(-)
diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index d15c99f6fb6..11fb5292163 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -20,6 +20,31 @@
WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
+static const WCHAR *system_default_gm_paths[] = +{ + L"/usr/share/sounds/sf2/default-GM.sf2", +}; + +static HRESULT get_system_default_gm_path(WCHAR *path, UINT max_len) +{ + UINT i; + + for (i = 0; i < ARRAY_SIZE(system_default_gm_paths); i++) + { + swprintf(path, max_len, L"\??\unix%s", system_default_gm_paths[i]); + if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) break; + } + + if (i < ARRAY_SIZE(system_default_gm_paths)) + { + WARN("Using system %s for the default collection\n", debugstr_w(path)); + return S_OK; + } + + ERR("Unable to find system path, default collection will not be available\n"); + return DMUS_E_LOADER_FAILEDOPEN; +} + static const GUID *classes[] = { &GUID_DirectMusicAllTypes, /* Keep as first */ &CLSID_DirectMusicAudioPathConfig, @@ -458,6 +483,13 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE lstrcpyW(p, pDesc->wszFileName); }
+ if (!wcsicmp(file_name, L"C:\windows\system32\drivers\gm.dls") + && GetFileAttributesW(file_name) == INVALID_FILE_ATTRIBUTES) + { + hr = get_system_default_gm_path(file_name, ARRAY_SIZE(file_name)); + if (FAILED(hr)) return hr; + } + if (FAILED(hr = file_stream_create(file_name, &pStream))) return hr; } else if (pDesc->dwValidData & DMUS_OBJ_STREAM) @@ -847,21 +879,24 @@ static const IDirectMusicLoader8Vtbl loader_vtbl = loader_LoadObjectFromFile, };
-/* help function for DMUSIC_SetDefaultDLS */ -static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) { - HKEY hkDM; - DWORD returnType, sizeOfReturnBuffer = MAX_PATH; - char szPath[MAX_PATH]; +static HRESULT get_default_gm_path(WCHAR *path, DWORD max_len) +{ + DWORD ret; + HKEY hkey; + + if (!(ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\Microsoft\DirectMusic" , 0, KEY_READ, &hkey))) + { + DWORD type, size = max_len * sizeof(WCHAR); + ret = RegQueryValueExW(hkey, L"GMFilePath", NULL, &type, (BYTE *)path, &size); + RegCloseKey(hkey);
- if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\Microsoft\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || - (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) { - WARN(": registry entry missing\n" ); - return E_FAIL; - } - /* FIXME: Check return types to ensure we're interpreting data right */ - MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH); + if (!ret && GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) return S_OK; + }
- return S_OK; + if (!ret) WARN("Failed to find %s, using system fallbacks\n", debugstr_w(path)); + else WARN("Failed to open GMFilePath registry key, using system fallbacks\n"); + + return get_system_default_gm_path(path, max_len); }
/* for ClassFactory */ @@ -886,8 +921,9 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT; Desc.guidClass = CLSID_DirectMusicCollection; Desc.guidObject = GUID_DefaultGMCollection; - DMUSIC_GetDefaultGMPath(Desc.wszFileName); - IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); + if (SUCCEEDED(hr = get_default_gm_path(Desc.wszFileName, ARRAY_SIZE(Desc.wszFileName)))) + hr = IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); + if (FAILED(hr)) WARN("Failed to load the default collection, hr %#lx\n", hr);
hr = IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); IDirectMusicLoader_Release(&obj->IDirectMusicLoader8_iface);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmusic/instrument.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 8ce3c0bc979..54ce7508ee4 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -44,6 +44,19 @@ struct region BOOL loop_present; };
+static void region_destroy(struct region *region) +{ + struct articulation *articulation, *next; + + LIST_FOR_EACH_ENTRY_SAFE(articulation, next, ®ion->articulations, struct articulation, entry) + { + list_remove(&articulation->entry); + free(articulation); + } + + free(region); +} + struct instrument { IDirectMusicInstrument IDirectMusicInstrument_iface; @@ -122,15 +135,8 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface)
LIST_FOR_EACH_ENTRY_SAFE(region, next_region, &This->regions, struct region, entry) { - LIST_FOR_EACH_ENTRY_SAFE(articulation, next_articulation, ®ion->articulations, - struct articulation, entry) - { - list_remove(&articulation->entry); - free(articulation); - } - list_remove(®ion->entry); - free(region); + region_destroy(region); }
collection_internal_release(This->collection); @@ -322,7 +328,7 @@ static HRESULT parse_rgn_chunk(struct instrument *This, IStream *stream, struct if (FAILED(hr)) break; }
- if (FAILED(hr)) free(region); + if (FAILED(hr)) region_destroy(region); else list_add_tail(&This->regions, ®ion->entry);
return hr;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmusic/wave.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 08146db0d1c..6637612596a 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -199,14 +199,17 @@ HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnk TRACE(" - wBitsPerSample: %u\n", This->format->wBitsPerSample); TRACE(" - cbSize: %u\n", This->format->cbSize); } - TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", - This->sample->head.cbSize, This->sample->head.usUnityNote, - This->sample->head.sFineTune, This->sample->head.lAttenuation, - This->sample->head.fulOptions, This->sample->head.cSampleLoops); - for (i = 0; i < This->sample->head.cSampleLoops; i++) - TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, - This->sample->loops[i].cbSize, This->sample->loops[i].ulType, - This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + if (This->sample) + { + TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + This->sample->head.cbSize, This->sample->head.usUnityNote, + This->sample->head.sFineTune, This->sample->head.lAttenuation, + This->sample->head.fulOptions, This->sample->head.cSampleLoops); + for (i = 0; i < This->sample->head.cSampleLoops; i++) + TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + This->sample->loops[i].cbSize, This->sample->loops[i].ulType, + This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + } }
*ret_iface = iface;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmime/tests/dmime.c | 2 +- dlls/dmusic/collection.c | 249 +++++++++++++++++++++++++++- dlls/dmusic/soundfont.h | 323 +++++++++++++++++++++++++++++++++++++ dlls/dmusic/tests/dmusic.c | 6 +- 4 files changed, 574 insertions(+), 6 deletions(-) create mode 100644 dlls/dmusic/soundfont.h
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 1266c5d053b..00190d17fe2 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3072,7 +3072,7 @@ static void test_band_track_play(void) &IID_IDirectMusicLoader8, (void **)&loader); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicLoader_SetObject(loader, &desc); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr);
hr = test_loader_stream_create(stream, loader, &loader_stream); ok(hr == S_OK, "got %#lx\n", hr); diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 1074c4e2868..feda1cbf8a9 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -19,6 +19,7 @@ */
#include "dmusic_private.h" +#include "soundfont.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmusic);
@@ -353,6 +354,246 @@ static HRESULT parse_dls_chunk(struct collection *This, IStream *stream, struct 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); + } + } + + 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) { @@ -366,7 +607,7 @@ static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *ifac
if ((hr = stream_get_chunk(stream, &riff)) != S_OK) return hr; - if (riff.id != FOURCC_RIFF || riff.type != FOURCC_DLS) { + 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; @@ -410,6 +651,10 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str 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; @@ -439,7 +684,7 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str }
TRACE(" - cues:\n"); - for (i = 0; i < This->pool->table.cCues; i++) + 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"); diff --git a/dlls/dmusic/soundfont.h b/dlls/dmusic/soundfont.h new file mode 100644 index 00000000000..ac71ba7909a --- /dev/null +++ b/dlls/dmusic/soundfont.h @@ -0,0 +1,323 @@ +/* + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * 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 "stdarg.h" +#include "stddef.h" + +#include "windef.h" +#include "winbase.h" + +#include "wine/debug.h" + +#include <pshpack1.h> + +/* SoundFont 2.04 data structures, from http://www.synthfont.com/sfspec24.pdf */ + +struct sf_range +{ + BYTE low; + BYTE high; +}; + +union sf_amount +{ + struct sf_range range; + WORD value; +}; + +C_ASSERT(sizeof(union sf_amount) == 2); + +enum +{ + SF_SAMPLE_MONO = 1, + SF_SAMPLE_RIGHT = 2, + SF_SAMPLE_LEFT = 4, + SF_SAMPLE_LINKED = 8, + SF_SAMPLE_ROM_MONO = 0x8001, + SF_SAMPLE_ROM_RIGHT = 0x8002, + SF_SAMPLE_ROM_LEFT = 0x8004, + SF_SAMPLE_ROM_LINKED = 0x8008, +}; +typedef WORD sf_sample_type; + +enum +{ + SF_GEN_START_ADDRS_OFFSET = 0, + SF_GEN_END_ADDRS_OFFSET = 1, + SF_GEN_STARTLOOP_ADDRS_OFFSET = 2, + SF_GEN_ENDLOOP_ADDRS_OFFSET = 3, + SF_GEN_START_ADDRS_COARSE_OFFSET = 4, + SF_GEN_MOD_LFO_TO_PITCH = 5, + SF_GEN_VIB_LFO_TO_PITCH = 6, + SF_GEN_MOD_ENV_TO_PITCH = 7, + SF_GEN_INITIAL_FILTER_FC = 8, + SF_GEN_INITIAL_FILTER_Q = 9, + SF_GEN_MOD_LFO_TO_FILTER_FC = 10, + SF_GEN_MOD_ENV_TO_FILTER_FC = 11, + SF_GEN_END_ADDRS_COARSE_OFFSET = 12, + SF_GEN_MOD_LFO_TO_VOLUME = 13, + + SF_GEN_CHORUS_EFFECTS_SEND = 15, + SF_GEN_REVERB_EFFECTS_SEND = 16, + SF_GEN_PAN = 17, + + SF_GEN_DELAY_MOD_LFO = 21, + SF_GEN_FREQ_MOD_LFO = 22, + SF_GEN_DELAY_VIB_LFO = 23, + SF_GEN_FREQ_VIB_LFO = 24, + SF_GEN_DELAY_MOD_ENV = 25, + SF_GEN_ATTACK_MOD_ENV = 26, + SF_GEN_HOLD_MOD_ENV = 27, + SF_GEN_DECAY_MOD_ENV = 28, + SF_GEN_SUSTAIN_MOD_ENV = 29, + SF_GEN_RELEASE_MOD_ENV = 30, + SF_GEN_KEYNUM_TO_MOD_ENV_HOLD = 31, + SF_GEN_KEYNUM_TO_MOD_ENV_DECAY = 32, + SF_GEN_DELAY_VOL_ENV = 33, + SF_GEN_ATTACK_VOL_ENV = 34, + SF_GEN_HOLD_VOL_ENV = 35, + SF_GEN_DECAY_VOL_ENV = 36, + SF_GEN_SUSTAIN_VOL_ENV = 37, + SF_GEN_RELEASE_VOL_ENV = 38, + SF_GEN_KEYNUM_TO_VOL_ENV_HOLD = 39, + SF_GEN_KEYNUM_TO_VOL_ENV_DECAY = 40, + SF_GEN_INSTRUMENT = 41, + + SF_GEN_KEY_RANGE = 43, + SF_GEN_VEL_RANGE = 44, + SF_GEN_STARTLOOP_ADDRS_COARSE_OFFSET = 45, + SF_GEN_KEYNUM = 46, + SF_GEN_VELOCITY = 47, + SF_GEN_INITIAL_ATTENUATION = 48, + + SF_GEN_ENDLOOP_ADDRS_COARSE_OFFSET = 50, + SF_GEN_COARSE_TUNE = 51, + SF_GEN_FINE_TUNE = 52, + SF_GEN_SAMPLE_ID = 53, + SF_GEN_SAMPLE_MODES = 54, + + SF_GEN_SCALE_TUNING = 56, + SF_GEN_EXCLUSIVE_CLASS = 57, + SF_GEN_OVERRIDING_ROOT_KEY = 58, + + SF_GEN_END_OPER = 60, +}; +typedef WORD sf_generator; + +static inline const char *debugstr_sf_generator(sf_generator oper) +{ + switch (oper) + { + case SF_GEN_START_ADDRS_OFFSET: return "start_addrs_offset"; + case SF_GEN_END_ADDRS_OFFSET: return "end_addrs_offset"; + case SF_GEN_STARTLOOP_ADDRS_OFFSET: return "startloop_addrs_offset"; + case SF_GEN_ENDLOOP_ADDRS_OFFSET: return "endloop_addrs_offset"; + case SF_GEN_START_ADDRS_COARSE_OFFSET: return "start_addrs_coarse_offset"; + case SF_GEN_MOD_LFO_TO_PITCH: return "mod_lfo_to_pitch"; + case SF_GEN_VIB_LFO_TO_PITCH: return "vib_lfo_to_pitch"; + case SF_GEN_MOD_ENV_TO_PITCH: return "mod_env_to_pitch"; + case SF_GEN_INITIAL_FILTER_FC: return "initial_filter_fc"; + case SF_GEN_INITIAL_FILTER_Q: return "initial_filter_q"; + case SF_GEN_MOD_LFO_TO_FILTER_FC: return "mod_lfo_to_filter_fc"; + case SF_GEN_MOD_ENV_TO_FILTER_FC: return "mod_env_to_filter_fc"; + case SF_GEN_END_ADDRS_COARSE_OFFSET: return "end_addrs_coarse_offset"; + case SF_GEN_MOD_LFO_TO_VOLUME: return "mod_lfo_to_volume"; + case SF_GEN_CHORUS_EFFECTS_SEND: return "chorus_effects_send"; + case SF_GEN_REVERB_EFFECTS_SEND: return "reverb_effects_send"; + case SF_GEN_PAN: return "pan"; + case SF_GEN_DELAY_MOD_LFO: return "delay_mod_lfo"; + case SF_GEN_FREQ_MOD_LFO: return "freq_mod_lfo"; + case SF_GEN_DELAY_VIB_LFO: return "delay_vib_lfo"; + case SF_GEN_FREQ_VIB_LFO: return "freq_vib_lfo"; + case SF_GEN_DELAY_MOD_ENV: return "delay_mod_env"; + case SF_GEN_ATTACK_MOD_ENV: return "attack_mod_env"; + case SF_GEN_HOLD_MOD_ENV: return "hold_mod_env"; + case SF_GEN_DECAY_MOD_ENV: return "decay_mod_env"; + case SF_GEN_SUSTAIN_MOD_ENV: return "sustain_mod_env"; + case SF_GEN_RELEASE_MOD_ENV: return "release_mod_env"; + case SF_GEN_KEYNUM_TO_MOD_ENV_HOLD: return "keynum_to_mod_env_hold"; + case SF_GEN_KEYNUM_TO_MOD_ENV_DECAY: return "keynum_to_mod_env_decay"; + case SF_GEN_DELAY_VOL_ENV: return "delay_vol_env"; + case SF_GEN_ATTACK_VOL_ENV: return "attack_vol_env"; + case SF_GEN_HOLD_VOL_ENV: return "hold_vol_env"; + case SF_GEN_DECAY_VOL_ENV: return "decay_vol_env"; + case SF_GEN_SUSTAIN_VOL_ENV: return "sustain_vol_env"; + case SF_GEN_RELEASE_VOL_ENV: return "release_vol_env"; + case SF_GEN_KEYNUM_TO_VOL_ENV_HOLD: return "keynum_to_vol_env_hold"; + case SF_GEN_KEYNUM_TO_VOL_ENV_DECAY: return "keynum_to_vol_env_decay"; + case SF_GEN_INSTRUMENT: return "instrument"; + case SF_GEN_KEY_RANGE: return "key_range"; + case SF_GEN_VEL_RANGE: return "vel_range"; + case SF_GEN_STARTLOOP_ADDRS_COARSE_OFFSET: return "startloop_addrs_coarse_offset"; + case SF_GEN_KEYNUM: return "keynum"; + case SF_GEN_VELOCITY: return "velocity"; + case SF_GEN_INITIAL_ATTENUATION: return "initial_attenuation"; + case SF_GEN_ENDLOOP_ADDRS_COARSE_OFFSET: return "endloop_addrs_coarse_offset"; + case SF_GEN_COARSE_TUNE: return "coarse_tune"; + case SF_GEN_FINE_TUNE: return "fine_tune"; + case SF_GEN_SAMPLE_ID: return "sample_id"; + case SF_GEN_SAMPLE_MODES: return "sample_modes"; + case SF_GEN_SCALE_TUNING: return "scale_tuning"; + case SF_GEN_EXCLUSIVE_CLASS: return "exclusive_class"; + case SF_GEN_OVERRIDING_ROOT_KEY: return "overriding_root_key"; + case SF_GEN_END_OPER: return "end_oper"; + } + + return wine_dbg_sprintf("%u", oper); +} + +enum +{ + /* sf_modulator is a set of flags ored together */ + + SF_MOD_CTRL_GEN_NONE = 0, + SF_MOD_CTRL_GEN_VELOCITY = 0x2, + SF_MOD_CTRL_GEN_KEY = 0x3, + SF_MOD_CTRL_GEN_POLY_PRESSURE = 0xa, + SF_MOD_CTRL_GEN_CHAN_PRESSURE = 0xd, + SF_MOD_CTRL_GEN_PITCH_WHEEL = 0xe, + SF_MOD_CTRL_GEN_PITCH_WHEEL_SENSITIVITY = 0x10, + SF_MOD_CTRL_GEN_LINK = 0x7f, + + SF_MOD_CTRL_GEN = 0 << 7, + SF_MOD_CTRL_MIDI = 1 << 7, /* with LSB: MIDI CC */ + + SF_MOD_DIR_INCREASING = 0 << 8, + SF_MOD_DIR_DECREASING = 1 << 8, + + SF_MOD_POL_UNIPOLAR = 0 << 9, + SF_MOD_POL_BIPOLAR = 1 << 9, + + SF_MOD_SRC_LINEAR = 0 << 10, + SF_MOD_SRC_CONCAVE = 1 << 10, + SF_MOD_SRC_CONVEX = 2 << 10, + SF_MOD_SRC_SWITCH = 3 << 10, +}; +typedef WORD sf_modulator; + +enum +{ + SF_TRAN_LINEAR = 0, + SF_TRAN_ABSOLUTE = 2, +}; +typedef WORD sf_transform; + +struct sf_preset /* <phdr-rec> */ +{ + char name[20]; + WORD preset; + WORD bank; + WORD bag_ndx; + DWORD library; + DWORD genre; + DWORD morphology; +}; + +C_ASSERT(sizeof(struct sf_preset) == 38); + +struct sf_bag /* <pbag-rec> / <ibag-rec> */ +{ + WORD gen_ndx; + WORD mod_ndx; +}; + +C_ASSERT(sizeof(struct sf_bag) == 4); + +struct sf_mod /* <pmod-rec> / <imod-rec> */ +{ + sf_modulator src_mod; + sf_generator dest_gen; + SHORT amount; + sf_modulator amount_src_mod; + sf_transform transform; +}; + +C_ASSERT(sizeof(struct sf_mod) == 10); + +static inline const char *debugstr_sf_mod(struct sf_mod *mod) +{ + const char *dest_name = debugstr_sf_generator(mod->dest_gen); + return wine_dbg_sprintf("%#x x %#x -> %s: %d (%#x)", mod->src_mod, mod->amount_src_mod, dest_name, mod->amount, mod->transform); +} + +struct sf_gen /* <pgen-rec> / <igen-rec> */ +{ + sf_generator oper; + union sf_amount amount; +}; + +C_ASSERT(sizeof(struct sf_gen) == 4); + +static inline const char *debugstr_sf_gen(struct sf_gen *gen) +{ + const char *name = debugstr_sf_generator(gen->oper); + + switch (gen->oper) + { + case SF_GEN_KEY_RANGE: + case SF_GEN_VEL_RANGE: + return wine_dbg_sprintf("%s: %u-%u", name, gen->amount.range.low, gen->amount.range.high); + default: + return wine_dbg_sprintf("%s: %u", name, gen->amount.value); + } +} + +struct sf_instrument /* <inst-rec> */ +{ + char name[20]; + WORD bag_ndx; +}; + +C_ASSERT(sizeof(struct sf_instrument) == 22); + +struct sf_sample /* <shdr-rec> */ +{ + char name[20]; + DWORD start; + DWORD end; + DWORD start_loop; + DWORD end_loop; + DWORD sample_rate; + BYTE original_key; + char correction; + WORD sample_link; + sf_sample_type sample_type; +}; + +C_ASSERT(sizeof(struct sf_sample) == 46); + +#include <poppack.h> + +struct soundfont +{ + UINT preset_count; + struct sf_preset *phdr; + struct sf_bag *pbag; + struct sf_mod *pmod; + struct sf_gen *pgen; + + UINT instrument_count; + struct sf_instrument *inst; + struct sf_bag *ibag; + struct sf_mod *imod; + struct sf_gen *igen; + + UINT sample_count; + struct sf_sample *shdr; + BYTE *sdta; +}; diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 0e0ff7e609a..cf0faaa5b87 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1608,7 +1608,7 @@ static void test_default_gm_collection(void) ok(hr == S_OK, "got %#lx\n", hr);
hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicCollection, (void **)&collection); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr);
for (i = 0; hr == S_OK && i < ARRAY_SIZE(results); i++) { @@ -1618,7 +1618,7 @@ static void test_default_gm_collection(void) results[i].name, ARRAY_SIZE(results[i].name)); } if (hr == S_FALSE) i--; - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); todo_wine ok(i > 0, "got %lu\n", i); todo_wine ok(i == ARRAY_SIZE(expected), "got %lu\n", i);
@@ -1633,7 +1633,7 @@ static void test_default_gm_collection(void) winetest_pop_context(); }
- if (hr == S_FALSE) IDirectMusicCollection_Release(collection); + IDirectMusicCollection_Release(collection); IDirectMusicLoader_Release(loader); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmusic/collection.c | 23 +++++++++++ dlls/dmusic/dmusic_private.h | 2 + dlls/dmusic/wave.c | 80 ++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+)
diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index feda1cbf8a9..a7d9d666e6c 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -580,6 +580,29 @@ static HRESULT parse_sfbk_chunk(struct collection *This, IStream *stream, struct } }
+ 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); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 0b000fb6f1c..232bbcd0c94 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -91,12 +91,14 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj,
extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface);
+struct soundfont; extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicPortDownload *port, IDirectMusicDownloadedInstrument **downloaded); extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port);
+extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **out); extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id);
diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 6637612596a..40a8c9e129f 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -17,6 +17,7 @@ */
#include "dmusic_private.h" +#include "soundfont.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmusic);
@@ -216,6 +217,85 @@ HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnk return S_OK; }
+HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **ret_iface) +{ + struct sf_sample *sf_sample = soundfont->shdr + index; + struct sample *sample = NULL; + WAVEFORMATEX *format = NULL; + HRESULT hr = E_OUTOFMEMORY; + UINT data_size, offset; + struct wave *This; + void *data = NULL; + IUnknown *iface; + + TRACE("(%p, %u, %p)\n", soundfont, index, ret_iface); + + if (sf_sample->sample_link) FIXME("Stereo sample not supported\n"); + + if (!(format = calloc(1, sizeof(*format)))) goto failed; + format->wFormatTag = WAVE_FORMAT_PCM; + format->nChannels = 1; + format->wBitsPerSample = 16; + format->nSamplesPerSec = sf_sample->sample_rate; + format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8; + format->nAvgBytesPerSec = format->nBlockAlign * format->nSamplesPerSec; + + if (!(sample = calloc(1, offsetof(struct sample, loops[1])))) goto failed; + sample->head.cbSize = sizeof(sample->head); + sample->head.cSampleLoops = 1; + sample->loops[0].ulStart = sf_sample->start_loop - sf_sample->start; + sample->loops[0].ulLength = sf_sample->end_loop - sf_sample->start_loop; + + data_size = sf_sample->end - sf_sample->start; + if (!(data = malloc(data_size * format->nBlockAlign))) goto failed; + offset = sf_sample->start * format->nBlockAlign / format->nChannels; + memcpy(data, soundfont->sdta + offset, data_size); + + if (FAILED(hr = wave_create(&iface))) goto failed; + + This = impl_from_IUnknown(iface); + This->format = format; + This->sample = sample; + This->data_size = data_size; + This->data = data; + + if (TRACE_ON(dmusic)) + { + UINT i; + + TRACE("*** Created DirectMusicWave %p\n", This); + TRACE(" - format: %p\n", This->format); + if (This->format) + { + TRACE(" - wFormatTag: %u\n", This->format->wFormatTag); + TRACE(" - nChannels: %u\n", This->format->nChannels); + TRACE(" - nSamplesPerSec: %lu\n", This->format->nSamplesPerSec); + TRACE(" - nAvgBytesPerSec: %lu\n", This->format->nAvgBytesPerSec); + TRACE(" - nBlockAlign: %u\n", This->format->nBlockAlign); + TRACE(" - wBitsPerSample: %u\n", This->format->wBitsPerSample); + TRACE(" - cbSize: %u\n", This->format->cbSize); + } + + TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + This->sample->head.cbSize, This->sample->head.usUnityNote, + This->sample->head.sFineTune, This->sample->head.lAttenuation, + This->sample->head.fulOptions, This->sample->head.cSampleLoops); + for (i = 0; i < This->sample->head.cSampleLoops; i++) + TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + This->sample->loops[i].cbSize, This->sample->loops[i].ulType, + This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + } + + *ret_iface = iface; + return S_OK; + +failed: + free(data); + free(sample); + free(format); + return hr; +} + HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id) { struct download_buffer
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmusic/collection.c | 11 ++ dlls/dmusic/dmusic_private.h | 2 + dlls/dmusic/instrument.c | 239 +++++++++++++++++++++++++++++++++++ dlls/dmusic/tests/dmusic.c | 3 +- 4 files changed, 254 insertions(+), 1 deletion(-)
diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index a7d9d666e6c..bf21d3148e3 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -580,6 +580,17 @@ static HRESULT parse_sfbk_chunk(struct collection *This, IStream *stream, struct } }
+ 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]); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 232bbcd0c94..2317ffa57c6 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -92,6 +92,8 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface);
struct soundfont; +extern HRESULT instrument_create_from_soundfont(struct soundfont *soundfont, UINT index, + struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicPortDownload *port, diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 54ce7508ee4..7a0f8a2f1eb 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -19,11 +19,21 @@ */
#include "dmusic_private.h" +#include "soundfont.h" +#include "dls2.h"
WINE_DEFAULT_DEBUG_CHANNEL(dmusic);
static const GUID IID_IDirectMusicInstrumentPRIVATE = { 0xbcb20080, 0xa40c, 0x11d1, { 0x86, 0xbc, 0x00, 0xc0, 0x4f, 0xbf, 0x8f, 0xef } };
+#define CONN_SRC_CC2 0x0082 +#define CONN_SRC_RPN0 0x0100 + +#define CONN_TRN_BIPOLAR (1<<4) +#define CONN_TRN_INVERT (1<<5) + +#define CONN_TRANSFORM(src, ctrl, dst) (((src) & 0x3f) << 10) | (((ctrl) & 0x3f) << 4) | ((dst) & 0xf) + struct articulation { struct list entry; @@ -458,6 +468,235 @@ HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent return S_OK; }
+struct sf_generators +{ + union sf_amount amount[SF_GEN_END_OPER]; +}; + +static const struct sf_generators SF_DEFAULT_GENERATORS = +{ + .amount = + { + [SF_GEN_INITIAL_FILTER_FC] = {.value = 13500}, + [SF_GEN_DELAY_MOD_LFO] = {.value = -12000}, + [SF_GEN_DELAY_VIB_LFO] = {.value = -12000}, + [SF_GEN_DELAY_MOD_ENV] = {.value = -12000}, + [SF_GEN_ATTACK_MOD_ENV] = {.value = -12000}, + [SF_GEN_HOLD_MOD_ENV] = {.value = -12000}, + [SF_GEN_DECAY_MOD_ENV] = {.value = -12000}, + [SF_GEN_RELEASE_MOD_ENV] = {.value = -12000}, + [SF_GEN_DELAY_VOL_ENV] = {.value = -12000}, + [SF_GEN_ATTACK_VOL_ENV] = {.value = -12000}, + [SF_GEN_HOLD_VOL_ENV] = {.value = -12000}, + [SF_GEN_DECAY_VOL_ENV] = {.value = -12000}, + [SF_GEN_RELEASE_VOL_ENV] = {.value = -12000}, + [SF_GEN_KEY_RANGE] = {.range = {.low = 0, .high = 127}}, + [SF_GEN_VEL_RANGE] = {.range = {.low = 0, .high = 127}}, + [SF_GEN_KEYNUM] = {.value = -1}, + [SF_GEN_VELOCITY] = {.value = -1}, + [SF_GEN_SCALE_TUNING] = {.value = 100}, + [SF_GEN_OVERRIDING_ROOT_KEY] = {.value = -1}, + } +}; + +static BOOL parse_soundfont_generators(struct soundfont *soundfont, UINT index, + struct sf_generators *preset_generators, struct sf_generators *generators) +{ + struct sf_bag *bag = (preset_generators ? soundfont->ibag : soundfont->pbag) + index; + struct sf_gen *gen, *gens = preset_generators ? soundfont->igen : soundfont->pgen; + + for (gen = gens + bag->gen_ndx; gen < gens + (bag + 1)->gen_ndx; gen++) + { + switch (gen->oper) + { + case SF_GEN_START_ADDRS_OFFSET: + case SF_GEN_END_ADDRS_OFFSET: + case SF_GEN_STARTLOOP_ADDRS_OFFSET: + case SF_GEN_ENDLOOP_ADDRS_OFFSET: + case SF_GEN_START_ADDRS_COARSE_OFFSET: + case SF_GEN_END_ADDRS_COARSE_OFFSET: + case SF_GEN_STARTLOOP_ADDRS_COARSE_OFFSET: + case SF_GEN_KEYNUM: + case SF_GEN_VELOCITY: + case SF_GEN_ENDLOOP_ADDRS_COARSE_OFFSET: + case SF_GEN_SAMPLE_MODES: + case SF_GEN_EXCLUSIVE_CLASS: + case SF_GEN_OVERRIDING_ROOT_KEY: + if (preset_generators) generators->amount[gen->oper] = gen->amount; + else WARN("Ignoring invalid preset generator %s\n", debugstr_sf_gen(gen)); + break; + + case SF_GEN_INSTRUMENT: + if (!preset_generators) generators->amount[gen->oper] = gen->amount; + else WARN("Ignoring invalid instrument generator %s\n", debugstr_sf_gen(gen)); + /* should always be the last generator */ + return FALSE; + + case SF_GEN_SAMPLE_ID: + if (preset_generators) generators->amount[gen->oper] = gen->amount; + else WARN("Ignoring invalid preset generator %s\n", debugstr_sf_gen(gen)); + /* should always be the last generator */ + return FALSE; + + default: + generators->amount[gen->oper] = gen->amount; + if (preset_generators) generators->amount[gen->oper].value += preset_generators->amount[gen->oper].value; + break; + } + } + + return TRUE; +} + +static HRESULT instrument_add_soundfont_region(struct instrument *This, struct soundfont *soundfont, + struct sf_generators *generators) +{ + UINT start_loop, end_loop, unity_note, sample_index = generators->amount[SF_GEN_SAMPLE_ID].value; + struct sf_sample *sample = soundfont->shdr + sample_index; + struct region *region; + + if (!(region = calloc(1, sizeof(*region)))) return E_OUTOFMEMORY; + list_init(®ion->articulations); + + region->header.RangeKey.usLow = generators->amount[SF_GEN_KEY_RANGE].range.low; + region->header.RangeKey.usHigh = generators->amount[SF_GEN_KEY_RANGE].range.high; + region->header.RangeVelocity.usLow = generators->amount[SF_GEN_VEL_RANGE].range.low; + region->header.RangeVelocity.usHigh = generators->amount[SF_GEN_VEL_RANGE].range.high; + + region->wave_link.ulTableIndex = sample_index; + + unity_note = generators->amount[SF_GEN_OVERRIDING_ROOT_KEY].value; + if (unity_note == -1) unity_note = sample->original_key; + region->wave_sample.usUnityNote = unity_note; + region->wave_sample.sFineTune = generators->amount[SF_GEN_FINE_TUNE].value; + region->wave_sample.lAttenuation = sample->correction; + + start_loop = generators->amount[SF_GEN_STARTLOOP_ADDRS_OFFSET].value; + end_loop = generators->amount[SF_GEN_ENDLOOP_ADDRS_OFFSET].value; + if (start_loop || end_loop) + { + region->loop_present = TRUE; + region->wave_sample.cSampleLoops = 1; + region->wave_loop.ulStart = start_loop; + region->wave_loop.ulLength = end_loop - start_loop; + } + + list_add_tail(&This->regions, ®ion->entry); + This->header.cRegions++; + return S_OK; +} + +static HRESULT instrument_add_soundfont_instrument(struct instrument *This, struct soundfont *soundfont, + UINT index, struct sf_generators *preset_generators) +{ + struct sf_generators global_generators = SF_DEFAULT_GENERATORS; + struct sf_instrument *instrument = soundfont->inst + index; + UINT i = instrument->bag_ndx; + HRESULT hr = S_OK; + + for (i = instrument->bag_ndx; SUCCEEDED(hr) && i < (instrument + 1)->bag_ndx; i++) + { + struct sf_generators generators = global_generators; + + if (parse_soundfont_generators(soundfont, i, preset_generators, &generators)) + { + if (i > instrument->bag_ndx) + WARN("Ignoring instrument zone without a sample id\n"); + else + global_generators = generators; + continue; + } + + hr = instrument_add_soundfont_region(This, soundfont, &generators); + } + + return hr; +} + +HRESULT instrument_create_from_soundfont(struct soundfont *soundfont, UINT index, + struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface) +{ + struct sf_preset *preset = soundfont->phdr + index; + struct sf_generators global_generators = {0}; + IDirectMusicInstrument *iface; + struct instrument *This; + HRESULT hr; + UINT i; + + TRACE("(%p, %u, %p, %p, %p)\n", soundfont, index, collection, desc, ret_iface); + + if (FAILED(hr = instrument_create(collection, &iface))) return hr; + This = impl_from_IDirectMusicInstrument(iface); + + This->header.Locale.ulBank = (preset->bank & 0x7f) | ((preset->bank << 1) & 0x7f00); + This->header.Locale.ulInstrument = preset->preset; + MultiByteToWideChar(CP_ACP, 0, preset->name, strlen(preset->name) + 1, + desc->wszName, sizeof(desc->wszName)); + + for (i = preset->bag_ndx; SUCCEEDED(hr) && i < (preset + 1)->bag_ndx; i++) + { + struct sf_generators generators = global_generators; + UINT instrument; + + if (parse_soundfont_generators(soundfont, i, NULL, &generators)) + { + if (i > preset->bag_ndx) + WARN("Ignoring preset zone without an instrument\n"); + else + global_generators = generators; + continue; + } + + instrument = generators.amount[SF_GEN_INSTRUMENT].value; + hr = instrument_add_soundfont_instrument(This, soundfont, instrument, &generators); + } + + if (FAILED(hr)) + { + IDirectMusicInstrument_Release(iface); + return hr; + } + + if (TRACE_ON(dmusic)) + { + struct region *region; + UINT i; + + TRACE("Created DirectMusicInstrument %p\n", This); + TRACE(" - header:\n"); + TRACE(" - regions: %ld\n", This->header.cRegions); + TRACE(" - locale: {bank: %#lx, instrument: %#lx} (patch %#lx)\n", + This->header.Locale.ulBank, This->header.Locale.ulInstrument, + MIDILOCALE2Patch(&This->header.Locale)); + if (desc->dwValidData & DMUS_OBJ_OBJECT) TRACE(" - guid: %s\n", debugstr_dmguid(&desc->guidObject)); + if (desc->dwValidData & DMUS_OBJ_NAME) TRACE(" - name: %s\n", debugstr_w(desc->wszName)); + + TRACE(" - regions:\n"); + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) + { + TRACE(" - region:\n"); + TRACE(" - header: {key: %u - %u, vel: %u - %u, options: %#x, group: %#x}\n", + region->header.RangeKey.usLow, region->header.RangeKey.usHigh, + region->header.RangeVelocity.usLow, region->header.RangeVelocity.usHigh, + region->header.fusOptions, region->header.usKeyGroup); + TRACE(" - wave_link: {options: %#x, group: %u, channel: %lu, index: %lu}\n", + region->wave_link.fusOptions, region->wave_link.usPhaseGroup, + region->wave_link.ulChannel, region->wave_link.ulTableIndex); + TRACE(" - wave_sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + region->wave_sample.cbSize, region->wave_sample.usUnityNote, + region->wave_sample.sFineTune, region->wave_sample.lAttenuation, + region->wave_sample.fulOptions, region->wave_sample.cSampleLoops); + for (i = 0; i < region->wave_sample.cSampleLoops; i++) + TRACE(" - wave_loop[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + region->wave_loop.cbSize, region->wave_loop.ulType, + region->wave_loop.ulStart, region->wave_loop.ulLength); + } + } + + *ret_iface = iface; + return S_OK; +} + static void write_articulation_download(struct list *articulations, ULONG *offsets, BYTE **ptr, UINT index, DWORD *first, UINT *end) { diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index cf0faaa5b87..51a6b0d8774 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1619,7 +1619,7 @@ static void test_default_gm_collection(void) } if (hr == S_FALSE) i--; ok(hr == S_FALSE, "got %#lx\n", hr); - todo_wine ok(i > 0, "got %lu\n", i); + ok(i > 0, "got %lu\n", i); todo_wine ok(i == ARRAY_SIZE(expected), "got %lu\n", i);
qsort(results, i, sizeof(*results), result_cmp); @@ -1628,6 +1628,7 @@ static void test_default_gm_collection(void) { winetest_push_context("%lu", i); trace("got %#lx %s\n", results[i].patch, debugstr_w(results[i].name)); + todo_wine_if(expected[i].patch >= 128) ok(results[i].patch == expected[i].patch, "got %#lx\n", results[i].patch); /* system soundfont names are not very predictable, let's not check them */ winetest_pop_context();
Actually I was wrong, the CI does have a default-GM.sf2, but `SetObject` will force override the collection path like I initially thought (and on the contrary to my later analysis). I restored some fallback logic for this case to handle a missing `c:/windows/system32/drivers/gm.dls`.
I didn't have time to review the commits yet, just reacting to your comments.
I wanted to use the native provided soundfonts too but only when using the native FluidSynth.\ With PE builds I wonder if we should just provide our own gm.dls, either: - Build it from soundfonts at Wine build time, or - Download it like winegecko / winemono and automatically downloading it when needed.