2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD 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.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file dmusic.cpp Playing music via DirectMusic. */
11 #include "../stdafx.h"
12 #ifdef WIN32_LEAN_AND_MEAN
13 # undef WIN32_LEAN_AND_MEAN // Don't exclude rarely-used stuff from Windows headers
16 #include "../os/windows/win32.h"
17 #include "../core/mem_func.hpp"
18 #include "../thread.h"
19 #include "../fileio_func.h"
20 #include "../base_media_base.h"
22 #include "midifile.hpp"
30 #include "../safeguards.h"
33 # pragma comment(lib, "ole32.lib")
34 #endif /* defined(_MSC_VER) */
36 static const int MS_TO_REFTIME
= 1000 * 10; ///< DirectMusic time base is 100 ns.
37 static const int MIDITIME_TO_REFTIME
= 10; ///< Time base of the midi file reader is 1 us.
40 #define FOURCC_INFO mmioFOURCC('I', 'N', 'F', 'O')
41 #define FOURCC_fmt mmioFOURCC('f', 'm', 't', ' ')
42 #define FOURCC_data mmioFOURCC('d', 'a', 't', 'a')
46 /** An instrument region maps a note range to wave data. */
52 std::vector
<WLOOP
> wave_loops
;
53 std::vector
<CONNECTION
> articulators
;
56 /** Instrument definition read from a DLS file. */
57 struct DLSInstrument
{
60 std::vector
<CONNECTION
> articulators
;
61 std::vector
<DLSRegion
> regions
;
64 /** Wave data definition from a DLS file. */
69 std::vector
<BYTE
> data
;
72 std::vector
<WLOOP
> wave_loops
;
75 std::vector
<DLSInstrument
> instruments
;
76 std::vector
<POOLCUE
> pool_cues
;
77 std::vector
<DLSWave
> waves
;
79 /** Try loading a DLS file into memory. */
80 bool LoadFile(const std::string
&file
);
83 /** Load an articulation structure from a DLS file. */
84 bool ReadDLSArticulation(FileHandle
&f
, DWORD list_length
, std::vector
<CONNECTION
> &out
);
85 /** Load a list of regions from a DLS file. */
86 bool ReadDLSRegionList(FileHandle
&f
, DWORD list_length
, DLSInstrument
&instrument
);
87 /** Load a single region from a DLS file. */
88 bool ReadDLSRegion(FileHandle
&f
, DWORD list_length
, std::vector
<DLSRegion
> &out
);
89 /** Load a list of instruments from a DLS file. */
90 bool ReadDLSInstrumentList(FileHandle
&f
, DWORD list_length
);
91 /** Load a single instrument from a DLS file. */
92 bool ReadDLSInstrument(FileHandle
&f
, DWORD list_length
);
93 /** Load a list of waves from a DLS file. */
94 bool ReadDLSWaveList(FileHandle
&f
, DWORD list_length
);
95 /** Load a single wave from a DLS file. */
96 bool ReadDLSWave(FileHandle
&f
, DWORD list_length
, long offset
);
99 /** A RIFF chunk header. */
100 PACK_N(struct ChunkHeader
{
101 FOURCC type
; ///< Chunk type.
102 DWORD length
; ///< Length of the chunk, not including the chunk header itself.
105 /** Buffer format for a DLS wave download. */
106 PACK_N(struct WAVE_DOWNLOAD
{
107 DMUS_DOWNLOADINFO dlInfo
;
108 ULONG ulOffsetTable
[2];
110 DMUS_WAVEDATA dmWaveData
;
113 struct PlaybackSegment
{
120 bool shutdown
; ///< flag to indicate playback thread shutdown
121 bool playing
; ///< flag indicating that playback is active
122 bool do_start
; ///< flag for starting playback of next_file at next opportunity
123 bool do_stop
; ///< flag for stopping playback at next opportunity
125 int preload_time
; ///< preload time for music blocks.
126 uint8_t new_volume
; ///< volume setting to change to
128 MidiFile next_file
; ///< upcoming file to play
129 PlaybackSegment next_segment
; ///< segment info for upcoming file
132 /** Handle to our worker thread. */
133 static std::thread _dmusic_thread
;
134 /** Event to signal the thread that it should look at a state change. */
135 static HANDLE _thread_event
= nullptr;
136 /** Lock access to playback data that is not thread-safe. */
137 static std::mutex _thread_mutex
;
139 /** The direct music object manages buffers and ports. */
140 static IDirectMusic
*_music
= nullptr;
141 /** The port object lets us send MIDI data to the synthesizer. */
142 static IDirectMusicPort
*_port
= nullptr;
143 /** The buffer object collects the data to sent. */
144 static IDirectMusicBuffer
*_buffer
= nullptr;
145 /** List of downloaded DLS instruments. */
146 static std::vector
<IDirectMusicDownload
*> _dls_downloads
;
149 static FMusicDriver_DMusic iFMusicDriver_DMusic
;
152 bool DLSFile::ReadDLSArticulation(FileHandle
&f
, DWORD list_length
, std::vector
<CONNECTION
> &out
)
154 while (list_length
> 0) {
156 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
157 list_length
-= chunk
.length
+ sizeof(chunk
);
159 if (chunk
.type
== FOURCC_ART1
) {
160 CONNECTIONLIST conns
;
161 if (fread(&conns
, sizeof(conns
), 1, f
) != 1) return false;
162 fseek(f
, conns
.cbSize
- sizeof(conns
), SEEK_CUR
);
164 /* Read all defined articulations. */
165 for (ULONG i
= 0; i
< conns
.cConnections
; i
++) {
167 if (fread(&con
, sizeof(con
), 1, f
) != 1) return false;
171 fseek(f
, chunk
.length
, SEEK_CUR
);
178 bool DLSFile::ReadDLSRegion(FileHandle
&f
, DWORD list_length
, std::vector
<DLSRegion
> &out
)
180 DLSRegion
®ion
= out
.emplace_back();
182 /* Set default values. */
183 region
.wave_sample
.cbSize
= 0;
185 while (list_length
> 0) {
187 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
188 list_length
-= chunk
.length
+ sizeof(chunk
);
190 if (chunk
.type
== FOURCC_LIST
) {
191 /* Unwrap list header. */
192 if (fread(&chunk
.type
, sizeof(chunk
.type
), 1, f
) != 1) return false;
193 chunk
.length
-= sizeof(chunk
.type
);
196 switch (chunk
.type
) {
198 if (fread(®ion
.hdr
, sizeof(region
.hdr
), 1, f
) != 1) return false;
202 if (fread(®ion
.wave_sample
, sizeof(region
.wave_sample
), 1, f
) != 1) return false;
203 fseek(f
, region
.wave_sample
.cbSize
- sizeof(region
.wave_sample
), SEEK_CUR
);
205 /* Read all defined sample loops. */
206 for (ULONG i
= 0; i
< region
.wave_sample
.cSampleLoops
; i
++) {
208 if (fread(&loop
, sizeof(loop
), 1, f
) != 1) return false;
209 region
.wave_loops
.push_back(loop
);
214 if (fread(®ion
.wave
, sizeof(region
.wave
), 1, f
) != 1) return false;
217 case FOURCC_LART
: // List chunk
218 if (!this->ReadDLSArticulation(f
, chunk
.length
, region
.articulators
)) return false;
222 /* We don't care about info stuff. */
223 fseek(f
, chunk
.length
, SEEK_CUR
);
227 Debug(driver
, 7, "DLS: Ignoring unknown chunk {}{}{}{}", (char)(chunk
.type
& 0xFF), (char)((chunk
.type
>> 8) & 0xFF), (char)((chunk
.type
>> 16) & 0xFF), (char)((chunk
.type
>> 24) & 0xFF));
228 fseek(f
, chunk
.length
, SEEK_CUR
);
236 bool DLSFile::ReadDLSRegionList(FileHandle
&f
, DWORD list_length
, DLSInstrument
&instrument
)
238 while (list_length
> 0) {
240 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
241 list_length
-= chunk
.length
+ sizeof(chunk
);
243 if (chunk
.type
== FOURCC_LIST
) {
245 if (fread(&list_type
, sizeof(list_type
), 1, f
) != 1) return false;
247 if (list_type
== FOURCC_RGN
) {
248 this->ReadDLSRegion(f
, chunk
.length
- sizeof(list_type
), instrument
.regions
);
250 Debug(driver
, 7, "DLS: Ignoring unknown list chunk of type {}{}{}{}", (char)(list_type
& 0xFF), (char)((list_type
>> 8) & 0xFF), (char)((list_type
>> 16) & 0xFF), (char)((list_type
>> 24) & 0xFF));
251 fseek(f
, chunk
.length
- sizeof(list_type
), SEEK_CUR
);
254 Debug(driver
, 7, "DLS: Ignoring chunk {}{}{}{}", (char)(chunk
.type
& 0xFF), (char)((chunk
.type
>> 8) & 0xFF), (char)((chunk
.type
>> 16) & 0xFF), (char)((chunk
.type
>> 24) & 0xFF));
255 fseek(f
, chunk
.length
, SEEK_CUR
);
262 bool DLSFile::ReadDLSInstrument(FileHandle
&f
, DWORD list_length
)
264 DLSInstrument
&instrument
= this->instruments
.emplace_back();
266 while (list_length
> 0) {
268 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
269 list_length
-= chunk
.length
+ sizeof(chunk
);
271 if (chunk
.type
== FOURCC_LIST
) {
272 /* Unwrap list header. */
273 if (fread(&chunk
.type
, sizeof(chunk
.type
), 1, f
) != 1) return false;
274 chunk
.length
-= sizeof(chunk
.type
);
277 switch (chunk
.type
) {
279 if (fread(&instrument
.hdr
, sizeof(instrument
.hdr
), 1, f
) != 1) return false;
282 case FOURCC_LART
: // List chunk
283 if (!this->ReadDLSArticulation(f
, chunk
.length
, instrument
.articulators
)) return false;
286 case FOURCC_LRGN
: // List chunk
287 if (!this->ReadDLSRegionList(f
, chunk
.length
, instrument
)) return false;
291 /* We don't care about info stuff. */
292 fseek(f
, chunk
.length
, SEEK_CUR
);
296 Debug(driver
, 7, "DLS: Ignoring unknown chunk {}{}{}{}", (char)(chunk
.type
& 0xFF), (char)((chunk
.type
>> 8) & 0xFF), (char)((chunk
.type
>> 16) & 0xFF), (char)((chunk
.type
>> 24) & 0xFF));
297 fseek(f
, chunk
.length
, SEEK_CUR
);
305 bool DLSFile::ReadDLSInstrumentList(FileHandle
&f
, DWORD list_length
)
307 while (list_length
> 0) {
309 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
310 list_length
-= chunk
.length
+ sizeof(chunk
);
312 if (chunk
.type
== FOURCC_LIST
) {
314 if (fread(&list_type
, sizeof(list_type
), 1, f
) != 1) return false;
316 if (list_type
== FOURCC_INS
) {
317 Debug(driver
, 6, "DLS: Reading instrument {}", (int)instruments
.size());
319 if (!this->ReadDLSInstrument(f
, chunk
.length
- sizeof(list_type
))) return false;
321 Debug(driver
, 7, "DLS: Ignoring unknown list chunk of type {}{}{}{}", (char)(list_type
& 0xFF), (char)((list_type
>> 8) & 0xFF), (char)((list_type
>> 16) & 0xFF), (char)((list_type
>> 24) & 0xFF));
322 fseek(f
, chunk
.length
- sizeof(list_type
), SEEK_CUR
);
325 Debug(driver
, 7, "DLS: Ignoring chunk {}{}{}{}", (char)(chunk
.type
& 0xFF), (char)((chunk
.type
>> 8) & 0xFF), (char)((chunk
.type
>> 16) & 0xFF), (char)((chunk
.type
>> 24) & 0xFF));
326 fseek(f
, chunk
.length
, SEEK_CUR
);
333 bool DLSFile::ReadDLSWave(FileHandle
&f
, DWORD list_length
, long offset
)
335 DLSWave
&wave
= this->waves
.emplace_back();
337 /* Set default values. */
338 MemSetT(&wave
.wave_sample
, 0);
339 wave
.wave_sample
.cbSize
= sizeof(WSMPL
);
340 wave
.wave_sample
.usUnityNote
= 60;
341 wave
.file_offset
= offset
; // Store file offset so we can resolve the wave pool table later on.
343 while (list_length
> 0) {
345 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
346 list_length
-= chunk
.length
+ sizeof(chunk
);
348 if (chunk
.type
== FOURCC_LIST
) {
349 /* Unwrap list header. */
350 if (fread(&chunk
.type
, sizeof(chunk
.type
), 1, f
) != 1) return false;
351 chunk
.length
-= sizeof(chunk
.type
);
354 switch (chunk
.type
) {
356 if (fread(&wave
.fmt
, sizeof(wave
.fmt
), 1, f
) != 1) return false;
357 if (chunk
.length
> sizeof(wave
.fmt
)) fseek(f
, chunk
.length
- sizeof(wave
.fmt
), SEEK_CUR
);
361 if (fread(&wave
.wave_sample
, sizeof(wave
.wave_sample
), 1, f
) != 1) return false;
362 fseek(f
, wave
.wave_sample
.cbSize
- sizeof(wave
.wave_sample
), SEEK_CUR
);
364 /* Read all defined sample loops. */
365 for (ULONG i
= 0; i
< wave
.wave_sample
.cSampleLoops
; i
++) {
367 if (fread(&loop
, sizeof(loop
), 1, f
) != 1) return false;
368 wave
.wave_loops
.push_back(loop
);
373 wave
.data
.resize(chunk
.length
);
374 if (fread(&wave
.data
[0], sizeof(BYTE
), wave
.data
.size(), f
) != wave
.data
.size()) return false;
378 /* We don't care about info stuff. */
379 fseek(f
, chunk
.length
, SEEK_CUR
);
383 Debug(driver
, 7, "DLS: Ignoring unknown chunk {}{}{}{}", (char)(chunk
.type
& 0xFF), (char)((chunk
.type
>> 8) & 0xFF), (char)((chunk
.type
>> 16) & 0xFF), (char)((chunk
.type
>> 24) & 0xFF));
384 fseek(f
, chunk
.length
, SEEK_CUR
);
392 bool DLSFile::ReadDLSWaveList(FileHandle
&f
, DWORD list_length
)
394 long base_offset
= ftell(f
);
396 while (list_length
> 0) {
397 long chunk_offset
= ftell(f
);
400 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
401 list_length
-= chunk
.length
+ sizeof(chunk
);
403 if (chunk
.type
== FOURCC_LIST
) {
405 if (fread(&list_type
, sizeof(list_type
), 1, f
) != 1) return false;
407 if (list_type
== FOURCC_wave
) {
408 Debug(driver
, 6, "DLS: Reading wave {}", waves
.size());
410 if (!this->ReadDLSWave(f
, chunk
.length
- sizeof(list_type
), chunk_offset
- base_offset
)) return false;
412 Debug(driver
, 7, "DLS: Ignoring unknown list chunk of type {}{}{}{}", (char)(list_type
& 0xFF), (char)((list_type
>> 8) & 0xFF), (char)((list_type
>> 16) & 0xFF), (char)((list_type
>> 24) & 0xFF));
413 fseek(f
, chunk
.length
- sizeof(list_type
), SEEK_CUR
);
416 Debug(driver
, 7, "DLS: Ignoring chunk {}{}{}{}", (char)(chunk
.type
& 0xFF), (char)((chunk
.type
>> 8) & 0xFF), (char)((chunk
.type
>> 16) & 0xFF), (char)((chunk
.type
>> 24) & 0xFF));
417 fseek(f
, chunk
.length
, SEEK_CUR
);
424 bool DLSFile::LoadFile(const std::string
&file
)
426 Debug(driver
, 2, "DMusic: Try to load DLS file {}", file
);
428 auto of
= FileHandle::Open(file
, "rb");
429 if (!of
.has_value()) return false;
432 /* Check DLS file header. */
435 if (fread(&hdr
, sizeof(hdr
), 1, f
) != 1) return false;
436 if (fread(&dls_type
, sizeof(dls_type
), 1, f
) != 1) return false;
437 if (hdr
.type
!= FOURCC_RIFF
|| dls_type
!= FOURCC_DLS
) return false;
439 hdr
.length
-= sizeof(FOURCC
);
441 Debug(driver
, 2, "DMusic: Parsing DLS file");
446 /* Iterate over all chunks in the file. */
447 while (hdr
.length
> 0) {
449 if (fread(&chunk
, sizeof(chunk
), 1, f
) != 1) return false;
450 hdr
.length
-= chunk
.length
+ sizeof(chunk
);
452 if (chunk
.type
== FOURCC_LIST
) {
453 /* Unwrap list header. */
454 if (fread(&chunk
.type
, sizeof(chunk
.type
), 1, f
) != 1) return false;
455 chunk
.length
-= sizeof(chunk
.type
);
458 switch (chunk
.type
) {
460 if (fread(&header
, sizeof(header
), 1, f
) != 1) return false;
463 case FOURCC_LINS
: // List chunk
464 if (!this->ReadDLSInstrumentList(f
, chunk
.length
)) return false;
467 case FOURCC_WVPL
: // List chunk
468 if (!this->ReadDLSWaveList(f
, chunk
.length
)) return false;
473 if (fread(&ptbl
, sizeof(ptbl
), 1, f
) != 1) return false;
474 fseek(f
, ptbl
.cbSize
- sizeof(ptbl
), SEEK_CUR
);
476 /* Read all defined cues. */
477 for (ULONG i
= 0; i
< ptbl
.cCues
; i
++) {
479 if (fread(&cue
, sizeof(cue
), 1, f
) != 1) return false;
480 this->pool_cues
.push_back(cue
);
485 /* We don't care about info stuff. */
486 fseek(f
, chunk
.length
, SEEK_CUR
);
490 Debug(driver
, 7, "DLS: Ignoring unknown chunk {}{}{}{}", (char)(chunk
.type
& 0xFF), (char)((chunk
.type
>> 8) & 0xFF), (char)((chunk
.type
>> 16) & 0xFF), (char)((chunk
.type
>> 24) & 0xFF));
491 fseek(f
, chunk
.length
, SEEK_CUR
);
496 /* Have we read as many instruments as indicated? */
497 if (header
.cInstruments
!= this->instruments
.size()) return false;
499 /* Resolve wave pool table. */
500 for (std::vector
<POOLCUE
>::iterator cue
= this->pool_cues
.begin(); cue
!= this->pool_cues
.end(); cue
++) {
501 std::vector
<DLSWave
>::iterator w
= std::ranges::find(this->waves
, cue
->ulOffset
, &DLSWave::file_offset
);
502 if (w
!= this->waves
.end()) {
503 cue
->ulOffset
= (ULONG
)(w
- this->waves
.begin());
513 static uint8_t ScaleVolume(uint8_t original
, uint8_t scale
)
515 return original
* scale
/ 127;
518 static void TransmitChannelMsg(IDirectMusicBuffer
*buffer
, REFERENCE_TIME rt
, uint8_t status
, uint8_t p1
, uint8_t p2
= 0)
520 if (buffer
->PackStructured(rt
, 0, status
| (p1
<< 8) | (p2
<< 16)) == E_OUTOFMEMORY
) {
521 /* Buffer is full, clear it and try again. */
522 _port
->PlayBuffer(buffer
);
525 buffer
->PackStructured(rt
, 0, status
| (p1
<< 8) | (p2
<< 16));
529 static void TransmitSysex(IDirectMusicBuffer
*buffer
, REFERENCE_TIME rt
, const uint8_t *&msg_start
, size_t &remaining
)
531 /* Find end of message. */
532 const uint8_t *msg_end
= msg_start
;
533 while (*msg_end
!= MIDIST_ENDSYSEX
) msg_end
++;
534 msg_end
++; // Also include SysEx end byte.
536 if (buffer
->PackUnstructured(rt
, 0, msg_end
- msg_start
, const_cast<LPBYTE
>(msg_start
)) == E_OUTOFMEMORY
) {
537 /* Buffer is full, clear it and try again. */
538 _port
->PlayBuffer(buffer
);
541 buffer
->PackUnstructured(rt
, 0, msg_end
- msg_start
, const_cast<LPBYTE
>(msg_start
));
544 /* Update position in buffer. */
545 remaining
-= msg_end
- msg_start
;
549 static void TransmitStandardSysex(IDirectMusicBuffer
*buffer
, REFERENCE_TIME rt
, MidiSysexMessage msg
)
552 const uint8_t *data
= MidiGetStandardSysexMessage(msg
, length
);
553 TransmitSysex(buffer
, rt
, data
, length
);
556 /** Transmit 'Note off' messages to all MIDI channels. */
557 static void TransmitNotesOff(IDirectMusicBuffer
*buffer
, REFERENCE_TIME block_time
, REFERENCE_TIME cur_time
)
559 for (int ch
= 0; ch
< 16; ch
++) {
560 TransmitChannelMsg(buffer
, block_time
+ 10, MIDIST_CONTROLLER
| ch
, MIDICT_MODE_ALLNOTESOFF
, 0);
561 TransmitChannelMsg(buffer
, block_time
+ 10, MIDIST_CONTROLLER
| ch
, MIDICT_SUSTAINSW
, 0);
562 TransmitChannelMsg(buffer
, block_time
+ 10, MIDIST_CONTROLLER
| ch
, MIDICT_MODE_RESETALLCTRL
, 0);
565 /* Performing a GM reset stops all sound and resets all parameters. */
566 TransmitStandardSysex(buffer
, block_time
+ 20, MidiSysexMessage::ResetGM
);
567 TransmitStandardSysex(buffer
, block_time
+ 30, MidiSysexMessage::RolandSetReverb
);
569 /* Explicitly flush buffer to make sure the messages are processed,
570 * as we want sound to stop immediately. */
571 _port
->PlayBuffer(buffer
);
574 /* Wait until message time has passed. */
575 Sleep(Clamp((block_time
- cur_time
) / MS_TO_REFTIME
, 5, 1000));
578 static void MidiThreadProc()
580 Debug(driver
, 2, "DMusic: Entering playback thread");
582 REFERENCE_TIME last_volume_time
= 0; // timestamp of the last volume change
583 REFERENCE_TIME block_time
= 0; // timestamp of the last block sent to the port
584 REFERENCE_TIME playback_start_time
; // timestamp current file began playback
585 MidiFile current_file
; // file currently being played from
586 PlaybackSegment current_segment
; // segment info for current playback
587 size_t current_block
= 0; // next block index to send
588 uint8_t current_volume
= 0; // current effective volume setting
589 uint8_t channel_volumes
[16]; // last seen volume controller values in raw data
591 /* Get pointer to the reference clock of our output port. */
592 IReferenceClock
*clock
;
593 _port
->GetLatencyClock(&clock
);
595 REFERENCE_TIME cur_time
;
596 clock
->GetTime(&cur_time
);
598 _port
->PlayBuffer(_buffer
);
601 DWORD next_timeout
= 1000;
603 /* Wait for a signal from the GUI thread or until the time for the next event has come. */
604 DWORD wfso
= WaitForSingleObject(_thread_event
, next_timeout
);
606 if (_playback
.shutdown
) {
607 _playback
.playing
= false;
611 if (_playback
.do_stop
) {
612 Debug(driver
, 2, "DMusic thread: Stopping playback");
614 /* Turn all notes off and wait a bit to allow the messages to be handled. */
615 clock
->GetTime(&cur_time
);
616 TransmitNotesOff(_buffer
, block_time
, cur_time
);
618 _playback
.playing
= false;
619 _playback
.do_stop
= false;
625 if (wfso
== WAIT_OBJECT_0
) {
626 if (_playback
.do_start
) {
627 Debug(driver
, 2, "DMusic thread: Starting playback");
629 /* New scope to limit the time the mutex is locked. */
630 std::lock_guard
<std::mutex
> lock(_thread_mutex
);
632 current_file
.MoveFrom(_playback
.next_file
);
633 std::swap(_playback
.next_segment
, current_segment
);
634 current_segment
.start_block
= 0;
636 _playback
.playing
= true;
637 _playback
.do_start
= false;
640 /* Reset playback device between songs. */
641 clock
->GetTime(&cur_time
);
642 TransmitNotesOff(_buffer
, block_time
, cur_time
);
644 MemSetT
<uint8_t>(channel_volumes
, 127, lengthof(channel_volumes
));
645 /* Invalidate current volume. */
646 current_volume
= UINT8_MAX
;
647 last_volume_time
= 0;
649 /* Take the current time plus the preload time as the music start time. */
650 clock
->GetTime(&playback_start_time
);
651 playback_start_time
+= _playback
.preload_time
* MS_TO_REFTIME
;
655 if (_playback
.playing
) {
656 /* skip beginning of file? */
657 if (current_segment
.start
> 0 && current_block
== 0 && current_segment
.start_block
== 0) {
658 /* find first block after start time and pretend playback started earlier
659 * this is to allow all blocks prior to the actual start to still affect playback,
660 * as they may contain important controller and program changes */
661 size_t preload_bytes
= 0;
662 for (size_t bl
= 0; bl
< current_file
.blocks
.size(); bl
++) {
663 MidiFile::DataBlock
&block
= current_file
.blocks
[bl
];
664 preload_bytes
+= block
.data
.size();
665 if (block
.ticktime
>= current_segment
.start
) {
666 if (current_segment
.loop
) {
667 Debug(driver
, 2, "DMusic: timer: loop from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl
, block
.ticktime
, ((int)block
.realtime
) / 1000.0, preload_bytes
);
668 current_segment
.start_block
= bl
;
671 /* Skip the transmission delay compensation performed in the Win32 MIDI driver.
672 * The DMusic driver will most likely be used with the MS softsynth, which is not subject to transmission delays.
674 Debug(driver
, 2, "DMusic: timer: start from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl
, block
.ticktime
, ((int)block
.realtime
) / 1000.0, preload_bytes
);
675 playback_start_time
-= block
.realtime
* MIDITIME_TO_REFTIME
;
682 /* Get current playback timestamp. */
683 REFERENCE_TIME current_time
;
684 clock
->GetTime(¤t_time
);
686 /* Check for volume change. */
687 if (current_volume
!= _playback
.new_volume
) {
688 if (current_time
- last_volume_time
> 10 * MS_TO_REFTIME
) {
689 Debug(driver
, 2, "DMusic thread: volume change");
690 current_volume
= _playback
.new_volume
;
691 last_volume_time
= current_time
;
692 for (int ch
= 0; ch
< 16; ch
++) {
693 int vol
= ScaleVolume(channel_volumes
[ch
], current_volume
);
694 TransmitChannelMsg(_buffer
, block_time
+ 1, MIDIST_CONTROLLER
| ch
, MIDICT_CHANVOLUME
, vol
);
696 _port
->PlayBuffer(_buffer
);
701 while (current_block
< current_file
.blocks
.size()) {
702 MidiFile::DataBlock
&block
= current_file
.blocks
[current_block
];
704 /* check that block isn't at end-of-song override */
705 if (current_segment
.end
> 0 && block
.ticktime
>= current_segment
.end
) {
706 if (current_segment
.loop
) {
707 Debug(driver
, 2, "DMusic thread: Looping song");
708 current_block
= current_segment
.start_block
;
709 playback_start_time
= current_time
- current_file
.blocks
[current_block
].realtime
* MIDITIME_TO_REFTIME
;
711 _playback
.do_stop
= true;
716 /* check that block is not in the future */
717 REFERENCE_TIME playback_time
= current_time
- playback_start_time
;
718 if (block
.realtime
* MIDITIME_TO_REFTIME
> playback_time
+ 3 *_playback
.preload_time
* MS_TO_REFTIME
) {
719 /* Stop the thread loop until we are at the preload time of the next block. */
720 next_timeout
= Clamp(((int64_t)block
.realtime
* MIDITIME_TO_REFTIME
- playback_time
) / MS_TO_REFTIME
- _playback
.preload_time
, 0, 1000);
721 Debug(driver
, 9, "DMusic thread: Next event in {} ms (music {}, ref {})", next_timeout
, block
.realtime
* MIDITIME_TO_REFTIME
, playback_time
);
725 /* Timestamp of the current block. */
726 block_time
= playback_start_time
+ block
.realtime
* MIDITIME_TO_REFTIME
;
727 Debug(driver
, 9, "DMusic thread: Streaming block {} (cur={}, block={})", current_block
, (long long)(current_time
/ MS_TO_REFTIME
), (long long)(block_time
/ MS_TO_REFTIME
));
729 const uint8_t *data
= block
.data
.data();
730 size_t remaining
= block
.data
.size();
731 uint8_t last_status
= 0;
732 while (remaining
> 0) {
733 /* MidiFile ought to have converted everything out of running status,
734 * but handle it anyway just to be safe */
735 uint8_t status
= data
[0];
737 last_status
= status
;
741 status
= last_status
;
743 switch (status
& 0xF0) {
745 case MIDIST_CHANPRESS
:
746 /* 2 byte channel messages */
747 TransmitChannelMsg(_buffer
, block_time
, status
, data
[0]);
753 case MIDIST_POLYPRESS
:
754 case MIDIST_PITCHBEND
:
755 /* 3 byte channel messages */
756 TransmitChannelMsg(_buffer
, block_time
, status
, data
[0], data
[1]);
760 case MIDIST_CONTROLLER
:
761 /* controller change */
762 if (data
[0] == MIDICT_CHANVOLUME
) {
763 /* volume controller, adjust for user volume */
764 channel_volumes
[status
& 0x0F] = data
[1];
765 int vol
= ScaleVolume(data
[1], current_volume
);
766 TransmitChannelMsg(_buffer
, block_time
, status
, data
[0], vol
);
768 /* handle other controllers normally */
769 TransmitChannelMsg(_buffer
, block_time
, status
, data
[0], data
[1]);
775 /* system messages */
777 case MIDIST_SYSEX
: /* system exclusive */
778 TransmitSysex(_buffer
, block_time
, data
, remaining
);
780 case MIDIST_TC_QFRAME
: /* time code quarter frame */
781 case MIDIST_SONGSEL
: /* song select */
785 case MIDIST_SONGPOSPTR
: /* song position pointer */
789 default: /* remaining have no data bytes */
799 /* Anything in the playback buffer? Send it down the port. */
800 DWORD used_buffer
= 0;
801 _buffer
->GetUsedBytes(&used_buffer
);
802 if (used_buffer
> 0) {
803 _port
->PlayBuffer(_buffer
);
808 if (current_block
== current_file
.blocks
.size()) {
809 if (current_segment
.loop
) {
810 current_block
= current_segment
.start_block
;
811 playback_start_time
= block_time
- current_file
.blocks
[current_block
].realtime
* MIDITIME_TO_REFTIME
;
813 _playback
.do_stop
= true;
820 Debug(driver
, 2, "DMusic: Exiting playback thread");
822 /* Turn all notes off and wait a bit to allow the messages to be handled by real hardware. */
823 clock
->GetTime(&cur_time
);
824 TransmitNotesOff(_buffer
, block_time
, cur_time
);
825 Sleep(_playback
.preload_time
* 4);
830 static void * DownloadArticulationData(int base_offset
, void *data
, const std::vector
<CONNECTION
> &artic
)
832 DMUS_ARTICULATION2
*art
= (DMUS_ARTICULATION2
*)data
;
833 art
->ulArtIdx
= base_offset
+ 1;
834 art
->ulFirstExtCkIdx
= 0;
835 art
->ulNextArtIdx
= 0;
837 CONNECTIONLIST
*con_list
= (CONNECTIONLIST
*)(art
+ 1);
838 con_list
->cbSize
= sizeof(CONNECTIONLIST
);
839 con_list
->cConnections
= (ULONG
)artic
.size();
840 MemCpyT((CONNECTION
*)(con_list
+ 1), &artic
.front(), artic
.size());
842 return (CONNECTION
*)(con_list
+ 1) + artic
.size();
845 static const char *LoadDefaultDLSFile(const char *user_dls
)
849 caps
.dwSize
= sizeof(DMUS_PORTCAPS
);
850 _port
->GetCaps(&caps
);
852 /* Nothing to unless it is a synth with instrument download that doesn't come with GM voices by default. */
853 if ((caps
.dwFlags
& (DMUS_PC_DLS
| DMUS_PC_DLS2
)) != 0 && (caps
.dwFlags
& DMUS_PC_GMINHARDWARE
) == 0) {
856 if (user_dls
== nullptr) {
857 /* Try loading the default GM DLS file stored in the registry. */
859 if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\DirectMusic", 0, KEY_READ
, &hkDM
))) {
860 wchar_t dls_path
[MAX_PATH
];
861 DWORD buf_size
= sizeof(dls_path
); // Buffer size as to be given in bytes!
862 if (SUCCEEDED(RegQueryValueEx(hkDM
, L
"GMFilePath", nullptr, nullptr, (LPBYTE
)dls_path
, &buf_size
))) {
863 wchar_t expand_path
[MAX_PATH
* 2];
864 ExpandEnvironmentStrings(dls_path
, expand_path
, static_cast<DWORD
>(std::size(expand_path
)));
865 if (!dls_file
.LoadFile(FS2OTTD(expand_path
))) Debug(driver
, 1, "Failed to load default GM DLS file from registry");
870 /* If we couldn't load the file from the registry, try again at the default install path of the GM DLS file. */
871 if (dls_file
.instruments
.empty()) {
872 static const wchar_t *DLS_GM_FILE
= L
"%windir%\\System32\\drivers\\gm.dls";
873 wchar_t path
[MAX_PATH
];
874 ExpandEnvironmentStrings(DLS_GM_FILE
, path
, static_cast<DWORD
>(std::size(path
)));
876 if (!dls_file
.LoadFile(FS2OTTD(path
))) return "Can't load GM DLS collection";
879 if (!dls_file
.LoadFile(user_dls
)) return "Can't load GM DLS collection";
882 /* Get download port and allocate download IDs. */
883 IDirectMusicPortDownload
*download_port
= nullptr;
884 if (FAILED(_port
->QueryInterface(IID_IDirectMusicPortDownload
, (LPVOID
*)&download_port
))) return "Can't get download port";
886 DWORD dlid_wave
= 0, dlid_inst
= 0;
887 if (FAILED(download_port
->GetDLId(&dlid_wave
, (DWORD
)dls_file
.waves
.size())) || FAILED(download_port
->GetDLId(&dlid_inst
, (DWORD
)dls_file
.instruments
.size()))) {
888 download_port
->Release();
889 return "Can't get enough DLS ids";
893 download_port
->GetAppend(&dwAppend
);
895 /* Download wave data. */
896 for (DWORD i
= 0; i
< dls_file
.waves
.size(); i
++) {
897 IDirectMusicDownload
*dl_wave
= nullptr;
898 if (FAILED(download_port
->AllocateBuffer((DWORD
)(sizeof(WAVE_DOWNLOAD
) + dwAppend
* dls_file
.waves
[i
].fmt
.wf
.nBlockAlign
+ dls_file
.waves
[i
].data
.size()), &dl_wave
))) {
899 download_port
->Release();
900 return "Can't allocate wave download buffer";
905 if (FAILED(dl_wave
->GetBuffer((LPVOID
*)&wave
, &wave_size
))) {
907 download_port
->Release();
908 return "Can't get wave download buffer";
911 /* Fill download data. */
913 wave
->dlInfo
.dwDLType
= DMUS_DOWNLOADINFO_WAVE
;
914 wave
->dlInfo
.cbSize
= wave_size
;
915 wave
->dlInfo
.dwDLId
= dlid_wave
+ i
;
916 wave
->dlInfo
.dwNumOffsetTableEntries
= 2;
917 wave
->ulOffsetTable
[0] = offsetof(WAVE_DOWNLOAD
, dmWave
);
918 wave
->ulOffsetTable
[1] = offsetof(WAVE_DOWNLOAD
, dmWaveData
);
919 wave
->dmWave
.ulWaveDataIdx
= 1;
920 MemCpyT((PCMWAVEFORMAT
*)&wave
->dmWave
.WaveformatEx
, &dls_file
.waves
[i
].fmt
, 1);
921 wave
->dmWaveData
.cbSize
= (DWORD
)dls_file
.waves
[i
].data
.size();
922 MemCpyT(wave
->dmWaveData
.byData
, &dls_file
.waves
[i
].data
[0], dls_file
.waves
[i
].data
.size());
924 _dls_downloads
.push_back(dl_wave
);
925 if (FAILED(download_port
->Download(dl_wave
))) {
926 download_port
->Release();
927 return "Downloading DLS wave failed";
931 /* Download instrument data. */
932 for (DWORD i
= 0; i
< dls_file
.instruments
.size(); i
++) {
933 DWORD offsets
= 1 + (DWORD
)dls_file
.instruments
[i
].regions
.size();
935 /* Calculate download size for the instrument. */
936 size_t i_size
= sizeof(DMUS_DOWNLOADINFO
) + sizeof(DMUS_INSTRUMENT
);
937 if (!dls_file
.instruments
[i
].articulators
.empty()) {
938 /* Articulations are stored as two chunks, one containing meta data and one with the actual articulation data. */
940 i_size
+= sizeof(DMUS_ARTICULATION2
) + sizeof(CONNECTIONLIST
) + sizeof(CONNECTION
) * dls_file
.instruments
[i
].articulators
.size();
943 for (std::vector
<DLSFile::DLSRegion
>::iterator rgn
= dls_file
.instruments
[i
].regions
.begin(); rgn
!= dls_file
.instruments
[i
].regions
.end(); rgn
++) {
944 if (!rgn
->articulators
.empty()) {
946 i_size
+= sizeof(DMUS_ARTICULATION2
) + sizeof(CONNECTIONLIST
) + sizeof(CONNECTION
) * rgn
->articulators
.size();
949 /* Region size depends on the number of wave loops. The size of the
950 * declared structure already accounts for one loop. */
951 if (rgn
->wave_sample
.cbSize
!= 0) {
952 i_size
+= sizeof(DMUS_REGION
) - sizeof(DMUS_REGION::WLOOP
) + sizeof(WLOOP
) * rgn
->wave_loops
.size();
954 i_size
+= sizeof(DMUS_REGION
) - sizeof(DMUS_REGION::WLOOP
) + sizeof(WLOOP
) * dls_file
.waves
[dls_file
.pool_cues
[rgn
->wave
.ulTableIndex
].ulOffset
].wave_loops
.size();
958 i_size
+= offsets
* sizeof(ULONG
);
960 /* Allocate download buffer. */
961 IDirectMusicDownload
*dl_inst
= nullptr;
962 if (FAILED(download_port
->AllocateBuffer((DWORD
)i_size
, &dl_inst
))) {
963 download_port
->Release();
964 return "Can't allocate instrument download buffer";
969 if (FAILED(dl_inst
->GetBuffer((LPVOID
*)&instrument
, &inst_size
))) {
971 download_port
->Release();
972 return "Can't get instrument download buffer";
974 char *inst_base
= (char *)instrument
;
976 /* Fill download header. */
977 DMUS_DOWNLOADINFO
*d_info
= (DMUS_DOWNLOADINFO
*)instrument
;
978 d_info
->dwDLType
= DMUS_DOWNLOADINFO_INSTRUMENT2
;
979 d_info
->cbSize
= inst_size
;
980 d_info
->dwDLId
= dlid_inst
+ i
;
981 d_info
->dwNumOffsetTableEntries
= offsets
;
982 instrument
= d_info
+ 1;
984 /* Download offset table; contains the offsets of all chunks relative to the buffer start. */
985 ULONG
*offset_table
= (ULONG
*)instrument
;
986 instrument
= offset_table
+ offsets
;
989 /* Instrument header. */
990 DMUS_INSTRUMENT
*inst_data
= (DMUS_INSTRUMENT
*)instrument
;
991 MemSetT(inst_data
, 0);
992 offset_table
[last_offset
++] = (char *)inst_data
- inst_base
;
993 inst_data
->ulPatch
= (dls_file
.instruments
[i
].hdr
.Locale
.ulBank
& F_INSTRUMENT_DRUMS
) | ((dls_file
.instruments
[i
].hdr
.Locale
.ulBank
& 0x7F7F) << 8) | (dls_file
.instruments
[i
].hdr
.Locale
.ulInstrument
& 0x7F);
994 instrument
= inst_data
+ 1;
996 /* Write global articulations. */
997 if (!dls_file
.instruments
[i
].articulators
.empty()) {
998 inst_data
->ulGlobalArtIdx
= last_offset
;
999 offset_table
[last_offset
++] = (char *)instrument
- inst_base
;
1000 offset_table
[last_offset
++] = (char *)instrument
+ sizeof(DMUS_ARTICULATION2
) - inst_base
;
1002 instrument
= DownloadArticulationData(inst_data
->ulGlobalArtIdx
, instrument
, dls_file
.instruments
[i
].articulators
);
1003 assert((char *)instrument
- inst_base
<= (ptrdiff_t)inst_size
);
1006 /* Write out regions. */
1007 inst_data
->ulFirstRegionIdx
= last_offset
;
1008 for (uint j
= 0; j
< dls_file
.instruments
[i
].regions
.size(); j
++) {
1009 DLSFile::DLSRegion
&rgn
= dls_file
.instruments
[i
].regions
[j
];
1011 DMUS_REGION
*inst_region
= (DMUS_REGION
*)instrument
;
1012 offset_table
[last_offset
++] = (char *)inst_region
- inst_base
;
1013 inst_region
->RangeKey
= rgn
.hdr
.RangeKey
;
1014 inst_region
->RangeVelocity
= rgn
.hdr
.RangeVelocity
;
1015 inst_region
->fusOptions
= rgn
.hdr
.fusOptions
;
1016 inst_region
->usKeyGroup
= rgn
.hdr
.usKeyGroup
;
1017 inst_region
->ulFirstExtCkIdx
= 0;
1019 ULONG wave_id
= dls_file
.pool_cues
[rgn
.wave
.ulTableIndex
].ulOffset
;
1020 inst_region
->WaveLink
= rgn
.wave
;
1021 inst_region
->WaveLink
.ulTableIndex
= wave_id
+ dlid_wave
;
1023 /* The wave sample data will be taken from the region, if defined, otherwise from the wave itself. */
1024 if (rgn
.wave_sample
.cbSize
!= 0) {
1025 inst_region
->WSMP
= rgn
.wave_sample
;
1026 if (!rgn
.wave_loops
.empty()) MemCpyT(inst_region
->WLOOP
, &rgn
.wave_loops
.front(), rgn
.wave_loops
.size());
1028 instrument
= (char *)(inst_region
+ 1) - sizeof(DMUS_REGION::WLOOP
) + sizeof(WLOOP
) * rgn
.wave_loops
.size();
1030 inst_region
->WSMP
= rgn
.wave_sample
;
1031 if (!dls_file
.waves
[wave_id
].wave_loops
.empty()) MemCpyT(inst_region
->WLOOP
, &dls_file
.waves
[wave_id
].wave_loops
.front(), dls_file
.waves
[wave_id
].wave_loops
.size());
1033 instrument
= (char *)(inst_region
+ 1) - sizeof(DMUS_REGION::WLOOP
) + sizeof(WLOOP
) * dls_file
.waves
[wave_id
].wave_loops
.size();
1036 /* Write local articulator data. */
1037 if (!rgn
.articulators
.empty()) {
1038 inst_region
->ulRegionArtIdx
= last_offset
;
1039 offset_table
[last_offset
++] = (char *)instrument
- inst_base
;
1040 offset_table
[last_offset
++] = (char *)instrument
+ sizeof(DMUS_ARTICULATION2
) - inst_base
;
1042 instrument
= DownloadArticulationData(inst_region
->ulRegionArtIdx
, instrument
, rgn
.articulators
);
1044 inst_region
->ulRegionArtIdx
= 0;
1046 assert((char *)instrument
- inst_base
<= (ptrdiff_t)inst_size
);
1048 /* Link to the next region unless this was the last one.*/
1049 inst_region
->ulNextRegionIdx
= j
< dls_file
.instruments
[i
].regions
.size() - 1 ? last_offset
: 0;
1052 _dls_downloads
.push_back(dl_inst
);
1053 if (FAILED(download_port
->Download(dl_inst
))) {
1054 download_port
->Release();
1055 return "Downloading DLS instrument failed";
1059 download_port
->Release();
1066 std::optional
<std::string_view
> MusicDriver_DMusic::Start(const StringList
&parm
)
1068 /* Initialize COM */
1069 if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED
))) return "COM initialization failed";
1071 /* Create the DirectMusic object */
1072 if (FAILED(CoCreateInstance(
1079 return "Failed to create the music object";
1082 /* Assign sound output device. */
1083 if (FAILED(_music
->SetDirectSound(nullptr, nullptr))) return "Can't set DirectSound interface";
1085 /* MIDI events need to be send to the synth in time before their playback time
1086 * has come. By default, we try send any events at least 50 ms before playback. */
1087 _playback
.preload_time
= GetDriverParamInt(parm
, "preload", 50);
1089 int pIdx
= GetDriverParamInt(parm
, "port", -1);
1090 if (_debug_driver_level
> 0) {
1091 /* Print all valid output ports. */
1092 char desc
[DMUS_MAX_DESCRIPTION
];
1096 caps
.dwSize
= sizeof(DMUS_PORTCAPS
);
1098 Debug(driver
, 1, "Detected DirectMusic ports:");
1099 for (int i
= 0; _music
->EnumPort(i
, &caps
) == S_OK
; i
++) {
1100 if (caps
.dwClass
== DMUS_PC_OUTPUTCLASS
) {
1101 Debug(driver
, 1, " {}: {}{}", i
, convert_from_fs(caps
.wszDescription
, desc
), i
== pIdx
? " (selected)" : "");
1108 /* Check if the passed port is a valid port. */
1111 caps
.dwSize
= sizeof(DMUS_PORTCAPS
);
1112 if (FAILED(_music
->EnumPort(pIdx
, &caps
))) return "Supplied port parameter is not a valid port";
1113 if (caps
.dwClass
!= DMUS_PC_OUTPUTCLASS
) return "Supplied port parameter is not an output port";
1114 guidPort
= caps
.guidPort
;
1116 if (FAILED(_music
->GetDefaultPort(&guidPort
))) return "Can't query default music port";
1119 /* Create new port. */
1120 DMUS_PORTPARAMS params
;
1121 MemSetT(¶ms
, 0);
1122 params
.dwSize
= sizeof(DMUS_PORTPARAMS
);
1123 params
.dwValidParams
= DMUS_PORTPARAMS_CHANNELGROUPS
;
1124 params
.dwChannelGroups
= 1;
1125 if (FAILED(_music
->CreatePort(guidPort
, ¶ms
, &_port
, nullptr))) return "Failed to create port";
1126 /* Activate port. */
1127 if (FAILED(_port
->Activate(TRUE
))) return "Failed to activate port";
1129 /* Create playback buffer. */
1130 DMUS_BUFFERDESC desc
;
1132 desc
.dwSize
= sizeof(DMUS_BUFFERDESC
);
1133 desc
.guidBufferFormat
= KSDATAFORMAT_SUBTYPE_DIRECTMUSIC
;
1134 desc
.cbBuffer
= 1024;
1135 if (FAILED(_music
->CreateMusicBuffer(&desc
, &_buffer
, nullptr))) return "Failed to create music buffer";
1137 /* On soft-synths (e.g. the default DirectMusic one), we might need to load a wavetable set to get music. */
1138 const char *dls
= LoadDefaultDLSFile(GetDriverParam(parm
, "dls"));
1139 if (dls
!= nullptr) return dls
;
1141 /* Create playback thread and synchronization primitives. */
1142 _thread_event
= CreateEvent(nullptr, FALSE
, FALSE
, nullptr);
1143 if (_thread_event
== nullptr) return "Can't create thread shutdown event";
1145 if (!StartNewThread(&_dmusic_thread
, "ottd:dmusic", &MidiThreadProc
)) return "Can't create MIDI output thread";
1147 return std::nullopt
;
1151 MusicDriver_DMusic::~MusicDriver_DMusic()
1157 void MusicDriver_DMusic::Stop()
1159 if (_dmusic_thread
.joinable()) {
1160 _playback
.shutdown
= true;
1161 SetEvent(_thread_event
);
1162 _dmusic_thread
.join();
1165 /* Unloaded any instruments we loaded. */
1166 if (!_dls_downloads
.empty()) {
1167 IDirectMusicPortDownload
*download_port
= nullptr;
1168 _port
->QueryInterface(IID_IDirectMusicPortDownload
, (LPVOID
*)&download_port
);
1170 /* Instruments refer to waves. As the waves are at the beginning of the download list,
1171 * do the unload from the back so that references are cleared properly. */
1172 for (std::vector
<IDirectMusicDownload
*>::reverse_iterator i
= _dls_downloads
.rbegin(); download_port
!= nullptr && i
!= _dls_downloads
.rend(); i
++) {
1173 download_port
->Unload(*i
);
1176 _dls_downloads
.clear();
1178 if (download_port
!= nullptr) download_port
->Release();
1181 if (_buffer
!= nullptr) {
1186 if (_port
!= nullptr) {
1187 _port
->Activate(FALSE
);
1192 if (_music
!= nullptr) {
1197 CloseHandle(_thread_event
);
1203 void MusicDriver_DMusic::PlaySong(const MusicSongInfo
&song
)
1205 std::lock_guard
<std::mutex
> lock(_thread_mutex
);
1207 if (!_playback
.next_file
.LoadSong(song
)) return;
1209 _playback
.next_segment
.start
= song
.override_start
;
1210 _playback
.next_segment
.end
= song
.override_end
;
1211 _playback
.next_segment
.loop
= song
.loop
;
1213 _playback
.do_start
= true;
1214 SetEvent(_thread_event
);
1218 void MusicDriver_DMusic::StopSong()
1220 _playback
.do_stop
= true;
1221 SetEvent(_thread_event
);
1225 bool MusicDriver_DMusic::IsSongPlaying()
1227 return _playback
.playing
|| _playback
.do_start
;
1231 void MusicDriver_DMusic::SetVolume(uint8_t vol
)
1233 _playback
.new_volume
= vol
;