-- v6: 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 | 67 ++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 15 deletions(-)
diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index d15c99f6fb6..78b9790dff3 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -20,6 +20,32 @@
WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
+static const WCHAR *system_default_gm_paths[] = +{ + L"/usr/share/sounds/sf2/default-GM.sf2", + L"/usr/share/soundfonts/default.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 +484,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 +880,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 +922,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();
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=138019
Your paranoid android.
=== w864 (32 bit report) ===
dmime: dmime.c:2458: Test failed: got dwType 0xb dmime.c:2458: Test failed: got guidNotificationType {00ad0598-00c0-00ad-3718-bc0560900508} dmime.c:2458: Test failed: got dwNotificationOption 0xad9d90 dmime.c:2458: Test failed: got dwField1 11337920 dmime.c:2458: Test failed: got dwField2 9 dmime.c:2526: Test failed: got guidNotificationType {81f75bc5-4e5d-11d2-bcc7-00a0c922e6eb} dmime.c:2526: Test failed: got dwNotificationOption 0x1 dmime.c:2535: Test failed: got guidNotificationType {d2ac2899-b39b-11d1-8704-00600893b1bd} dmime.c:2535: Test failed: got dwNotificationOption 0x2
Eh, sorry pushed the wrong branch...
I only added `/usr/share/soundfonts/default.sf2` to the fallback paths.
This merge request was approved by Michael Stefaniuc.