Update: Translations from eints
[openttd-github.git] / src / music / dmusic.cpp
blob18ee2655a5281ed9933a78ee511ef347dfb02d24
1 /*
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/>.
6 */
8 /** @file dmusic.cpp Playing music via DirectMusic. */
10 #define INITGUID
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
14 #endif
15 #include "../debug.h"
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"
21 #include "dmusic.h"
22 #include "midifile.hpp"
23 #include "midi.h"
25 #include <windows.h>
26 #include <dmksctrl.h>
27 #include <dmusicc.h>
28 #include <mutex>
30 #include "../safeguards.h"
32 #if defined(_MSC_VER)
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')
44 /** A DLS file. */
45 struct DLSFile {
46 /** An instrument region maps a note range to wave data. */
47 struct DLSRegion {
48 RGNHEADER hdr;
49 WAVELINK wave;
50 WSMPL wave_sample;
52 std::vector<WLOOP> wave_loops;
53 std::vector<CONNECTION> articulators;
56 /** Instrument definition read from a DLS file. */
57 struct DLSInstrument {
58 INSTHEADER hdr;
60 std::vector<CONNECTION> articulators;
61 std::vector<DLSRegion> regions;
64 /** Wave data definition from a DLS file. */
65 struct DLSWave {
66 long file_offset;
68 PCMWAVEFORMAT fmt;
69 std::vector<BYTE> data;
71 WSMPL wave_sample;
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);
82 private:
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.
103 }, 2);
105 /** Buffer format for a DLS wave download. */
106 PACK_N(struct WAVE_DOWNLOAD {
107 DMUS_DOWNLOADINFO dlInfo;
108 ULONG ulOffsetTable[2];
109 DMUS_WAVE dmWave;
110 DMUS_WAVEDATA dmWaveData;
111 }, 2);
113 struct PlaybackSegment {
114 uint32_t start, end;
115 size_t start_block;
116 bool loop;
119 static struct {
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
130 } _playback;
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) {
155 ChunkHeader chunk;
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++) {
166 CONNECTION con;
167 if (fread(&con, sizeof(con), 1, f) != 1) return false;
168 out.push_back(con);
170 } else {
171 fseek(f, chunk.length, SEEK_CUR);
175 return true;
178 bool DLSFile::ReadDLSRegion(FileHandle &f, DWORD list_length, std::vector<DLSRegion> &out)
180 DLSRegion &region = out.emplace_back();
182 /* Set default values. */
183 region.wave_sample.cbSize = 0;
185 while (list_length > 0) {
186 ChunkHeader chunk;
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) {
197 case FOURCC_RGNH:
198 if (fread(&region.hdr, sizeof(region.hdr), 1, f) != 1) return false;
199 break;
201 case FOURCC_WSMP:
202 if (fread(&region.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++) {
207 WLOOP loop;
208 if (fread(&loop, sizeof(loop), 1, f) != 1) return false;
209 region.wave_loops.push_back(loop);
211 break;
213 case FOURCC_WLNK:
214 if (fread(&region.wave, sizeof(region.wave), 1, f) != 1) return false;
215 break;
217 case FOURCC_LART: // List chunk
218 if (!this->ReadDLSArticulation(f, chunk.length, region.articulators)) return false;
219 break;
221 case FOURCC_INFO:
222 /* We don't care about info stuff. */
223 fseek(f, chunk.length, SEEK_CUR);
224 break;
226 default:
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);
229 break;
233 return true;
236 bool DLSFile::ReadDLSRegionList(FileHandle &f, DWORD list_length, DLSInstrument &instrument)
238 while (list_length > 0) {
239 ChunkHeader chunk;
240 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
241 list_length -= chunk.length + sizeof(chunk);
243 if (chunk.type == FOURCC_LIST) {
244 FOURCC list_type;
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);
249 } else {
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);
253 } else {
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);
259 return true;
262 bool DLSFile::ReadDLSInstrument(FileHandle &f, DWORD list_length)
264 DLSInstrument &instrument = this->instruments.emplace_back();
266 while (list_length > 0) {
267 ChunkHeader chunk;
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) {
278 case FOURCC_INSH:
279 if (fread(&instrument.hdr, sizeof(instrument.hdr), 1, f) != 1) return false;
280 break;
282 case FOURCC_LART: // List chunk
283 if (!this->ReadDLSArticulation(f, chunk.length, instrument.articulators)) return false;
284 break;
286 case FOURCC_LRGN: // List chunk
287 if (!this->ReadDLSRegionList(f, chunk.length, instrument)) return false;
288 break;
290 case FOURCC_INFO:
291 /* We don't care about info stuff. */
292 fseek(f, chunk.length, SEEK_CUR);
293 break;
295 default:
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);
298 break;
302 return true;
305 bool DLSFile::ReadDLSInstrumentList(FileHandle &f, DWORD list_length)
307 while (list_length > 0) {
308 ChunkHeader chunk;
309 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
310 list_length -= chunk.length + sizeof(chunk);
312 if (chunk.type == FOURCC_LIST) {
313 FOURCC list_type;
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;
320 } else {
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);
324 } else {
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);
330 return true;
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) {
344 ChunkHeader chunk;
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) {
355 case FOURCC_fmt:
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);
358 break;
360 case FOURCC_WSMP:
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++) {
366 WLOOP loop;
367 if (fread(&loop, sizeof(loop), 1, f) != 1) return false;
368 wave.wave_loops.push_back(loop);
370 break;
372 case FOURCC_data:
373 wave.data.resize(chunk.length);
374 if (fread(&wave.data[0], sizeof(BYTE), wave.data.size(), f) != wave.data.size()) return false;
375 break;
377 case FOURCC_INFO:
378 /* We don't care about info stuff. */
379 fseek(f, chunk.length, SEEK_CUR);
380 break;
382 default:
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);
385 break;
389 return true;
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);
399 ChunkHeader chunk;
400 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
401 list_length -= chunk.length + sizeof(chunk);
403 if (chunk.type == FOURCC_LIST) {
404 FOURCC list_type;
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;
411 } else {
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);
415 } else {
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);
421 return true;
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;
430 auto &f = *of;
432 /* Check DLS file header. */
433 ChunkHeader hdr;
434 FOURCC dls_type;
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");
443 DLSHEADER header;
444 MemSetT(&header, 0);
446 /* Iterate over all chunks in the file. */
447 while (hdr.length > 0) {
448 ChunkHeader chunk;
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) {
459 case FOURCC_COLH:
460 if (fread(&header, sizeof(header), 1, f) != 1) return false;
461 break;
463 case FOURCC_LINS: // List chunk
464 if (!this->ReadDLSInstrumentList(f, chunk.length)) return false;
465 break;
467 case FOURCC_WVPL: // List chunk
468 if (!this->ReadDLSWaveList(f, chunk.length)) return false;
469 break;
471 case FOURCC_PTBL:
472 POOLTABLE ptbl;
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++) {
478 POOLCUE cue;
479 if (fread(&cue, sizeof(cue), 1, f) != 1) return false;
480 this->pool_cues.push_back(cue);
482 break;
484 case FOURCC_INFO:
485 /* We don't care about info stuff. */
486 fseek(f, chunk.length, SEEK_CUR);
487 break;
489 default:
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);
492 break;
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());
504 } else {
505 cue->ulOffset = 0;
509 return true;
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);
523 buffer->Flush();
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);
539 buffer->Flush();
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;
546 msg_start = msg_end;
549 static void TransmitStandardSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, MidiSysexMessage msg)
551 size_t length = 0;
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);
572 buffer->Flush();
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);
599 _buffer->Flush();
601 DWORD next_timeout = 1000;
602 while (true) {
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;
608 break;
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;
620 block_time = 0;
621 next_timeout = 1000;
622 continue;
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;
635 current_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;
669 break;
670 } else {
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;
676 break;
682 /* Get current playback timestamp. */
683 REFERENCE_TIME current_time;
684 clock->GetTime(&current_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);
697 _buffer->Flush();
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;
710 } else {
711 _playback.do_stop = true;
713 next_timeout = 0;
714 break;
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);
722 break;
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];
736 if (status & 0x80) {
737 last_status = status;
738 data++;
739 remaining--;
740 } else {
741 status = last_status;
743 switch (status & 0xF0) {
744 case MIDIST_PROGCHG:
745 case MIDIST_CHANPRESS:
746 /* 2 byte channel messages */
747 TransmitChannelMsg(_buffer, block_time, status, data[0]);
748 data++;
749 remaining--;
750 break;
751 case MIDIST_NOTEOFF:
752 case MIDIST_NOTEON:
753 case MIDIST_POLYPRESS:
754 case MIDIST_PITCHBEND:
755 /* 3 byte channel messages */
756 TransmitChannelMsg(_buffer, block_time, status, data[0], data[1]);
757 data += 2;
758 remaining -= 2;
759 break;
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);
767 } else {
768 /* handle other controllers normally */
769 TransmitChannelMsg(_buffer, block_time, status, data[0], data[1]);
771 data += 2;
772 remaining -= 2;
773 break;
774 case 0xF0:
775 /* system messages */
776 switch (status) {
777 case MIDIST_SYSEX: /* system exclusive */
778 TransmitSysex(_buffer, block_time, data, remaining);
779 break;
780 case MIDIST_TC_QFRAME: /* time code quarter frame */
781 case MIDIST_SONGSEL: /* song select */
782 data++;
783 remaining--;
784 break;
785 case MIDIST_SONGPOSPTR: /* song position pointer */
786 data += 2;
787 remaining -= 2;
788 break;
789 default: /* remaining have no data bytes */
790 break;
792 break;
796 current_block++;
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);
804 _buffer->Flush();
807 /* end? */
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;
812 } else {
813 _playback.do_stop = true;
815 next_timeout = 0;
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);
827 clock->Release();
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)
847 DMUS_PORTCAPS caps;
848 MemSetT(&caps, 0);
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) {
854 DLSFile dls_file;
856 if (user_dls == nullptr) {
857 /* Try loading the default GM DLS file stored in the registry. */
858 HKEY hkDM;
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");
867 RegCloseKey(hkDM);
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";
878 } else {
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";
892 DWORD dwAppend = 0;
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";
903 WAVE_DOWNLOAD *wave;
904 DWORD wave_size = 0;
905 if (FAILED(dl_wave->GetBuffer((LPVOID *)&wave, &wave_size))) {
906 dl_wave->Release();
907 download_port->Release();
908 return "Can't get wave download buffer";
911 /* Fill download data. */
912 MemSetT(wave, 0);
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. */
939 offsets += 2;
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()) {
945 offsets += 2;
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();
953 } else {
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";
967 void *instrument;
968 DWORD inst_size = 0;
969 if (FAILED(dl_inst->GetBuffer((LPVOID *)&instrument, &inst_size))) {
970 dl_inst->Release();
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;
987 int last_offset = 0;
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();
1029 } else {
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);
1043 } else {
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();
1062 return nullptr;
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(
1073 CLSID_DirectMusic,
1074 nullptr,
1075 CLSCTX_INPROC,
1076 IID_IDirectMusic,
1077 (LPVOID*)&_music
1078 ))) {
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];
1094 DMUS_PORTCAPS caps;
1095 MemSetT(&caps, 0);
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)" : "");
1106 GUID guidPort;
1107 if (pIdx >= 0) {
1108 /* Check if the passed port is a valid port. */
1109 DMUS_PORTCAPS caps;
1110 MemSetT(&caps, 0);
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;
1115 } else {
1116 if (FAILED(_music->GetDefaultPort(&guidPort))) return "Can't query default music port";
1119 /* Create new port. */
1120 DMUS_PORTPARAMS params;
1121 MemSetT(&params, 0);
1122 params.dwSize = sizeof(DMUS_PORTPARAMS);
1123 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
1124 params.dwChannelGroups = 1;
1125 if (FAILED(_music->CreatePort(guidPort, &params, &_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;
1131 MemSetT(&desc, 0);
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()
1153 this->Stop();
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);
1174 (*i)->Release();
1176 _dls_downloads.clear();
1178 if (download_port != nullptr) download_port->Release();
1181 if (_buffer != nullptr) {
1182 _buffer->Release();
1183 _buffer = nullptr;
1186 if (_port != nullptr) {
1187 _port->Activate(FALSE);
1188 _port->Release();
1189 _port = nullptr;
1192 if (_music != nullptr) {
1193 _music->Release();
1194 _music = nullptr;
1197 CloseHandle(_thread_event);
1199 CoUninitialize();
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;