2 * Copyright 2023 RĂ©mi Bernon for CodeWeavers
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "dmusic_private.h"
20 #include "soundfont.h"
22 WINE_DEFAULT_DEBUG_CHANNEL(dmusic
);
30 C_ASSERT(sizeof(struct sample
) == offsetof(struct sample
, loops
[0]));
34 IUnknown IUnknown_iface
;
35 struct dmobject dmobj
;
38 struct sample
*sample
;
44 static inline struct wave
*impl_from_IUnknown(IUnknown
*iface
)
46 return CONTAINING_RECORD(iface
, struct wave
, IUnknown_iface
);
49 static HRESULT WINAPI
wave_QueryInterface(IUnknown
*iface
, REFIID riid
, void **ret_iface
)
51 struct wave
*This
= impl_from_IUnknown(iface
);
53 TRACE("(%p, %s, %p)\n", This
, debugstr_dmguid(riid
), ret_iface
);
55 if (IsEqualIID(riid
, &IID_IUnknown
))
57 *ret_iface
= &This
->IUnknown_iface
;
58 IUnknown_AddRef(&This
->IUnknown_iface
);
62 if (IsEqualIID(riid
, &IID_IDirectMusicObject
))
64 *ret_iface
= &This
->dmobj
.IDirectMusicObject_iface
;
65 IDirectMusicObject_AddRef(&This
->dmobj
.IDirectMusicObject_iface
);
69 if (IsEqualIID(riid
, &IID_IPersistStream
))
71 *ret_iface
= &This
->dmobj
.IPersistStream_iface
;
72 IDirectMusicObject_AddRef(&This
->dmobj
.IPersistStream_iface
);
76 WARN("(%p, %s, %p): not found\n", This
, debugstr_dmguid(riid
), ret_iface
);
81 static ULONG WINAPI
wave_AddRef(IUnknown
*iface
)
83 struct wave
*This
= impl_from_IUnknown(iface
);
84 LONG ref
= InterlockedIncrement(&This
->ref
);
85 TRACE("(%p) ref=%ld\n", This
, ref
);
89 static ULONG WINAPI
wave_Release(IUnknown
*iface
)
91 struct wave
*This
= impl_from_IUnknown(iface
);
92 LONG ref
= InterlockedDecrement(&This
->ref
);
94 TRACE("(%p) ref=%ld\n", This
, ref
);
107 static const IUnknownVtbl unknown_vtbl
=
114 static HRESULT
parse_wsmp_chunk(struct wave
*This
, IStream
*stream
, struct chunk_entry
*chunk
)
116 struct sample
*sample
;
121 if (chunk
->size
< sizeof(wsmpl
)) return E_INVALIDARG
;
122 if (FAILED(hr
= stream_read(stream
, &wsmpl
, sizeof(wsmpl
)))) return hr
;
123 if (chunk
->size
!= wsmpl
.cbSize
+ sizeof(WLOOP
) * wsmpl
.cSampleLoops
) return E_INVALIDARG
;
124 if (wsmpl
.cbSize
!= sizeof(wsmpl
)) return E_INVALIDARG
;
125 if (wsmpl
.cSampleLoops
> 1) FIXME("Not implemented: found more than one wave loop\n");
127 size
= offsetof(struct sample
, loops
[wsmpl
.cSampleLoops
]);
128 if (!(sample
= malloc(size
))) return E_OUTOFMEMORY
;
129 sample
->head
= wsmpl
;
131 size
= sizeof(WLOOP
) * wsmpl
.cSampleLoops
;
132 if (FAILED(hr
= stream_read(stream
, sample
->loops
, size
))) free(sample
);
133 else This
->sample
= sample
;
138 static HRESULT
parse_wave_chunk(struct wave
*This
, IStream
*stream
, struct chunk_entry
*parent
)
140 struct chunk_entry chunk
= {.parent
= parent
};
143 while ((hr
= stream_next_chunk(stream
, &chunk
)) == S_OK
)
145 switch (MAKE_IDTYPE(chunk
.id
, chunk
.type
))
147 case mmioFOURCC('f','m','t',' '):
148 if (!(This
->format
= malloc(chunk
.size
))) return E_OUTOFMEMORY
;
149 hr
= stream_chunk_get_data(stream
, &chunk
, This
->format
, chunk
.size
);
152 case mmioFOURCC('d','a','t','a'):
153 if (!(This
->data
= malloc(chunk
.size
))) return E_OUTOFMEMORY
;
154 hr
= stream_chunk_get_data(stream
, &chunk
, This
->data
, chunk
.size
);
155 if (SUCCEEDED(hr
)) This
->data_size
= chunk
.size
;
159 hr
= parse_wsmp_chunk(This
, stream
, &chunk
);
162 case mmioFOURCC('w','a','v','u'):
163 case mmioFOURCC('s','m','p','l'):
164 case MAKE_IDTYPE(FOURCC_LIST
, DMUS_FOURCC_INFO_LIST
):
168 FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk
.id
), debugstr_fourcc(chunk
.type
));
172 if (FAILED(hr
)) break;
175 if (This
->format
&& This
->data
) return S_OK
;
179 static inline struct wave
*impl_from_IDirectMusicObject(IDirectMusicObject
*iface
)
181 return CONTAINING_RECORD(iface
, struct wave
, dmobj
.IDirectMusicObject_iface
);
184 static HRESULT WINAPI
wave_object_ParseDescriptor(IDirectMusicObject
*iface
,
185 IStream
*stream
, DMUS_OBJECTDESC
*desc
)
187 struct chunk_entry chunk
= {0};
190 TRACE("(%p, %p, %p)\n", iface
, stream
, desc
);
192 if (!stream
|| !desc
) return E_POINTER
;
194 if ((hr
= stream_next_chunk(stream
, &chunk
)) == S_OK
)
196 switch (MAKE_IDTYPE(chunk
.id
, chunk
.type
))
198 case MAKE_IDTYPE(FOURCC_RIFF
, mmioFOURCC('W','A','V','E')):
199 hr
= dmobj_parsedescriptor(stream
, &chunk
, desc
,
200 DMUS_OBJ_NAME_INFO
| DMUS_OBJ_OBJECT
| DMUS_OBJ_VERSION
);
204 WARN("Invalid wave chunk %s %s\n", debugstr_fourcc(chunk
.id
), debugstr_fourcc(chunk
.type
));
205 hr
= DMUS_E_CHUNKNOTFOUND
;
210 if (FAILED(hr
)) return hr
;
212 TRACE("returning descriptor:\n");
213 dump_DMUS_OBJECTDESC(desc
);
217 static const IDirectMusicObjectVtbl wave_object_vtbl
=
219 dmobj_IDirectMusicObject_QueryInterface
,
220 dmobj_IDirectMusicObject_AddRef
,
221 dmobj_IDirectMusicObject_Release
,
222 dmobj_IDirectMusicObject_GetDescriptor
,
223 dmobj_IDirectMusicObject_SetDescriptor
,
224 wave_object_ParseDescriptor
,
227 static inline struct wave
*impl_from_IPersistStream(IPersistStream
*iface
)
229 return CONTAINING_RECORD(iface
, struct wave
, dmobj
.IPersistStream_iface
);
232 static HRESULT WINAPI
wave_persist_stream_Load(IPersistStream
*iface
, IStream
*stream
)
234 struct wave
*This
= impl_from_IPersistStream(iface
);
235 struct chunk_entry chunk
= {0};
238 TRACE("(%p, %p)\n", This
, stream
);
240 if (!stream
) return E_POINTER
;
242 if ((hr
= stream_next_chunk(stream
, &chunk
)) == S_OK
)
244 switch (MAKE_IDTYPE(chunk
.id
, chunk
.type
))
246 case MAKE_IDTYPE(FOURCC_RIFF
, mmioFOURCC('W','A','V','E')):
247 hr
= parse_wave_chunk(This
, stream
, &chunk
);
251 WARN("Invalid wave chunk %s %s\n", debugstr_fourcc(chunk
.id
), debugstr_fourcc(chunk
.type
));
252 hr
= DMUS_E_UNSUPPORTED_STREAM
;
257 stream_skip_chunk(stream
, &chunk
);
261 static const IPersistStreamVtbl wave_persist_stream_vtbl
=
263 dmobj_IPersistStream_QueryInterface
,
264 dmobj_IPersistStream_AddRef
,
265 dmobj_IPersistStream_Release
,
266 dmobj_IPersistStream_GetClassID
,
267 unimpl_IPersistStream_IsDirty
,
268 wave_persist_stream_Load
,
269 unimpl_IPersistStream_Save
,
270 unimpl_IPersistStream_GetSizeMax
,
273 HRESULT
wave_create(IDirectMusicObject
**ret_iface
)
277 if (!(obj
= calloc(1, sizeof(*obj
)))) return E_OUTOFMEMORY
;
278 obj
->IUnknown_iface
.lpVtbl
= &unknown_vtbl
;
280 dmobject_init(&obj
->dmobj
, &CLSID_DirectSoundWave
, &obj
->IUnknown_iface
);
281 obj
->dmobj
.IDirectMusicObject_iface
.lpVtbl
= &wave_object_vtbl
;
282 obj
->dmobj
.IPersistStream_iface
.lpVtbl
= &wave_persist_stream_vtbl
;
284 *ret_iface
= &obj
->dmobj
.IDirectMusicObject_iface
;
288 HRESULT
wave_create_from_chunk(IStream
*stream
, struct chunk_entry
*parent
, IDirectMusicObject
**ret_iface
)
291 IDirectMusicObject
*iface
;
294 TRACE("(%p, %p, %p)\n", stream
, parent
, ret_iface
);
296 if (FAILED(hr
= wave_create(&iface
))) return hr
;
297 This
= impl_from_IDirectMusicObject(iface
);
299 if (FAILED(hr
= parse_wave_chunk(This
, stream
, parent
)))
301 IDirectMusicObject_Release(iface
);
302 return DMUS_E_UNSUPPORTED_STREAM
;
305 if (TRACE_ON(dmusic
))
309 TRACE("*** Created DirectMusicWave %p\n", This
);
310 TRACE(" - format: %p\n", This
->format
);
313 TRACE(" - wFormatTag: %u\n", This
->format
->wFormatTag
);
314 TRACE(" - nChannels: %u\n", This
->format
->nChannels
);
315 TRACE(" - nSamplesPerSec: %lu\n", This
->format
->nSamplesPerSec
);
316 TRACE(" - nAvgBytesPerSec: %lu\n", This
->format
->nAvgBytesPerSec
);
317 TRACE(" - nBlockAlign: %u\n", This
->format
->nBlockAlign
);
318 TRACE(" - wBitsPerSample: %u\n", This
->format
->wBitsPerSample
);
319 TRACE(" - cbSize: %u\n", This
->format
->cbSize
);
323 TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n",
324 This
->sample
->head
.cbSize
, This
->sample
->head
.usUnityNote
,
325 This
->sample
->head
.sFineTune
, This
->sample
->head
.lAttenuation
,
326 This
->sample
->head
.fulOptions
, This
->sample
->head
.cSampleLoops
);
327 for (i
= 0; i
< This
->sample
->head
.cSampleLoops
; i
++)
328 TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i
,
329 This
->sample
->loops
[i
].cbSize
, This
->sample
->loops
[i
].ulType
,
330 This
->sample
->loops
[i
].ulStart
, This
->sample
->loops
[i
].ulLength
);
338 HRESULT
wave_create_from_soundfont(struct soundfont
*soundfont
, UINT index
, IDirectMusicObject
**ret_iface
)
340 struct sf_sample
*sf_sample
= soundfont
->shdr
+ index
;
341 struct sample
*sample
= NULL
;
342 WAVEFORMATEX
*format
= NULL
;
343 HRESULT hr
= E_OUTOFMEMORY
;
344 UINT data_size
, offset
;
347 IDirectMusicObject
*iface
;
349 TRACE("(%p, %u, %p)\n", soundfont
, index
, ret_iface
);
351 if (sf_sample
->sample_link
) FIXME("Stereo sample not supported\n");
353 if (!(format
= calloc(1, sizeof(*format
)))) goto failed
;
354 format
->wFormatTag
= WAVE_FORMAT_PCM
;
355 format
->nChannels
= 1;
356 format
->wBitsPerSample
= 16;
357 format
->nSamplesPerSec
= sf_sample
->sample_rate
;
358 format
->nBlockAlign
= format
->wBitsPerSample
* format
->nChannels
/ 8;
359 format
->nAvgBytesPerSec
= format
->nBlockAlign
* format
->nSamplesPerSec
;
361 if (!(sample
= calloc(1, offsetof(struct sample
, loops
[1])))) goto failed
;
362 sample
->head
.cbSize
= sizeof(sample
->head
);
363 sample
->head
.cSampleLoops
= 1;
364 sample
->loops
[0].ulStart
= sf_sample
->start_loop
- sf_sample
->start
;
365 sample
->loops
[0].ulLength
= sf_sample
->end_loop
- sf_sample
->start_loop
;
367 data_size
= sf_sample
->end
- sf_sample
->start
;
368 if (!(data
= malloc(data_size
* format
->nBlockAlign
))) goto failed
;
369 offset
= sf_sample
->start
* format
->nBlockAlign
/ format
->nChannels
;
370 memcpy(data
, soundfont
->sdta
+ offset
, data_size
);
372 if (FAILED(hr
= wave_create(&iface
))) goto failed
;
374 This
= impl_from_IDirectMusicObject(iface
);
375 This
->format
= format
;
376 This
->sample
= sample
;
377 This
->data_size
= data_size
;
380 if (TRACE_ON(dmusic
))
384 TRACE("*** Created DirectMusicWave %p\n", This
);
385 TRACE(" - format: %p\n", This
->format
);
388 TRACE(" - wFormatTag: %u\n", This
->format
->wFormatTag
);
389 TRACE(" - nChannels: %u\n", This
->format
->nChannels
);
390 TRACE(" - nSamplesPerSec: %lu\n", This
->format
->nSamplesPerSec
);
391 TRACE(" - nAvgBytesPerSec: %lu\n", This
->format
->nAvgBytesPerSec
);
392 TRACE(" - nBlockAlign: %u\n", This
->format
->nBlockAlign
);
393 TRACE(" - wBitsPerSample: %u\n", This
->format
->wBitsPerSample
);
394 TRACE(" - cbSize: %u\n", This
->format
->cbSize
);
397 TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n",
398 This
->sample
->head
.cbSize
, This
->sample
->head
.usUnityNote
,
399 This
->sample
->head
.sFineTune
, This
->sample
->head
.lAttenuation
,
400 This
->sample
->head
.fulOptions
, This
->sample
->head
.cSampleLoops
);
401 for (i
= 0; i
< This
->sample
->head
.cSampleLoops
; i
++)
402 TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i
,
403 This
->sample
->loops
[i
].cbSize
, This
->sample
->loops
[i
].ulType
,
404 This
->sample
->loops
[i
].ulStart
, This
->sample
->loops
[i
].ulLength
);
417 HRESULT
wave_download_to_port(IDirectMusicObject
*iface
, IDirectMusicPortDownload
*port
, DWORD
*id
)
419 struct download_buffer
421 DMUS_DOWNLOADINFO info
;
427 struct wave
*This
= impl_from_IDirectMusicObject(iface
);
428 DWORD size
= offsetof(struct download_buffer
, data
.byData
[This
->data_size
]);
429 IDirectMusicDownload
*download
;
432 if (FAILED(hr
= IDirectMusicPortDownload_AllocateBuffer(port
, size
, &download
))) return hr
;
434 if (SUCCEEDED(hr
= IDirectMusicDownload_GetBuffer(download
, (void **)&buffer
, &size
))
435 && SUCCEEDED(hr
= IDirectMusicPortDownload_GetDLId(port
, &buffer
->info
.dwDLId
, 1)))
437 buffer
->info
.dwDLType
= DMUS_DOWNLOADINFO_WAVE
;
438 buffer
->info
.dwNumOffsetTableEntries
= 2;
439 buffer
->info
.cbSize
= size
;
441 buffer
->offsets
[0] = offsetof(struct download_buffer
, wave
);
442 buffer
->offsets
[1] = offsetof(struct download_buffer
, data
);
444 buffer
->wave
.WaveformatEx
= *This
->format
;
445 buffer
->wave
.ulWaveDataIdx
= 1;
446 buffer
->wave
.ulCopyrightIdx
= 0;
447 buffer
->wave
.ulFirstExtCkIdx
= 0;
449 buffer
->data
.cbSize
= This
->data_size
;
450 memcpy(buffer
->data
.byData
, This
->data
, This
->data_size
);
452 if (SUCCEEDED(hr
= IDirectMusicPortDownload_Download(port
, download
))) *id
= buffer
->info
.dwDLId
;
453 else WARN("Failed to download wave to port, hr %#lx\n", hr
);
456 IDirectMusicDownload_Release(download
);
460 HRESULT
wave_download_to_dsound(IDirectMusicObject
*iface
, IDirectSound
*dsound
, IDirectSoundBuffer
**ret_iface
)
462 struct wave
*This
= impl_from_IDirectMusicObject(iface
);
465 .dwSize
= sizeof(desc
),
466 .dwBufferBytes
= This
->data_size
,
467 .lpwfxFormat
= This
->format
,
469 IDirectSoundBuffer
*buffer
;
474 TRACE("%p, %p, %p\n", This
, dsound
, ret_iface
);
476 if (FAILED(hr
= IDirectSound_CreateSoundBuffer(dsound
, &desc
, &buffer
, NULL
)))
478 WARN("Failed to create direct sound buffer, hr %#lx\n", hr
);
482 if (SUCCEEDED(hr
= IDirectSoundBuffer_Lock(buffer
, 0, This
->data_size
, &data
, &size
, NULL
, 0, 0)))
484 memcpy(data
, This
->data
, This
->data_size
);
485 hr
= IDirectSoundBuffer_Unlock(buffer
, data
, This
->data_size
, NULL
, 0);
490 WARN("Failed to download wave to dsound, hr %#lx\n", hr
);
491 IDirectSoundBuffer_Release(buffer
);
499 HRESULT
wave_get_duration(IDirectMusicObject
*iface
, REFERENCE_TIME
*duration
)
501 struct wave
*This
= impl_from_IDirectMusicObject(iface
);
502 *duration
= (REFERENCE_TIME
)This
->data_size
* 10000000 / This
->format
->nAvgBytesPerSec
;