OpenTTD Source 20260311-master-g511d3794ce
dmusic.cpp
Go to the documentation of this file.
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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 "../thread.h"
18#include "../fileio_func.h"
19#include "../base_media_base.h"
20#include "../base_media_music.h"
21#include "dmusic.h"
22#include "midifile.hpp"
23#include "midi.h"
24
25#include <windows.h>
26#include <dmksctrl.h>
27#include <dmusicc.h>
28#include <mutex>
29
30#include "../safeguards.h"
31
32#if defined(_MSC_VER)
33# pragma comment(lib, "ole32.lib")
34#endif /* defined(_MSC_VER) */
35
36static constexpr REFERENCE_TIME MS_TO_REFTIME = 1000 * 10;
37static constexpr REFERENCE_TIME MIDITIME_TO_REFTIME = 10;
38
39
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')
43
45struct DLSFile {
47 struct DLSRegion {
48 RGNHEADER hdr;
49 WAVELINK wave;
50 WSMPL wave_sample;
51
52 std::vector<WLOOP> wave_loops;
53 std::vector<CONNECTION> articulators;
54 };
55
58 INSTHEADER hdr;
59
60 std::vector<CONNECTION> articulators;
61 std::vector<DLSRegion> regions;
62 };
63
65 struct DLSWave {
66 long file_offset;
67
68 PCMWAVEFORMAT fmt;
69 std::vector<BYTE> data;
70
71 WSMPL wave_sample;
72 std::vector<WLOOP> wave_loops;
73 };
74
75 std::vector<DLSInstrument> instruments;
76 std::vector<POOLCUE> pool_cues;
77 std::vector<DLSWave> waves;
78
84 bool LoadFile(std::string_view file);
85
86private:
94 bool ReadDLSArticulation(FileHandle &f, DWORD list_length, std::vector<CONNECTION> &out);
95
103 bool ReadDLSRegionList(FileHandle &f, DWORD list_length, DLSInstrument &instrument);
104
112 bool ReadDLSRegion(FileHandle &f, DWORD list_length, std::vector<DLSRegion> &out);
113
120 bool ReadDLSInstrumentList(FileHandle &f, DWORD list_length);
121
128 bool ReadDLSInstrument(FileHandle &f, DWORD list_length);
129
136 bool ReadDLSWaveList(FileHandle &f, DWORD list_length);
137
145 bool ReadDLSWave(FileHandle &f, DWORD list_length, long offset);
146};
147
149PACK_N(struct ChunkHeader {
150 FOURCC type;
151 DWORD length;
152}, 2);
153
155PACK_N(struct WAVE_DOWNLOAD {
156 DMUS_DOWNLOADINFO dlInfo;
157 ULONG ulOffsetTable[2];
158 DMUS_WAVE dmWave;
159 DMUS_WAVEDATA dmWaveData;
160}, 2);
161
163 uint32_t start, end;
164 size_t start_block;
165 bool loop;
166};
167
168static struct {
169 bool shutdown;
170 bool playing;
171 bool do_start;
172 bool do_stop;
173
175 uint8_t new_volume;
176
179} _playback;
180
182static std::thread _dmusic_thread;
184static HANDLE _thread_event = nullptr;
186static std::mutex _thread_mutex;
187
189static IDirectMusic *_music = nullptr;
191static IDirectMusicPort *_port = nullptr;
193static IDirectMusicBuffer *_buffer = nullptr;
195static std::vector<IDirectMusicDownload *> _dls_downloads;
196
197
198static FMusicDriver_DMusic iFMusicDriver_DMusic;
199
200
201bool DLSFile::ReadDLSArticulation(FileHandle &f, DWORD list_length, std::vector<CONNECTION> &out)
202{
203 while (list_length > 0) {
204 ChunkHeader chunk;
205 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
206 list_length -= chunk.length + sizeof(chunk);
207
208 if (chunk.type == FOURCC_ART1) {
209 CONNECTIONLIST conns;
210 if (fread(&conns, sizeof(conns), 1, f) != 1) return false;
211 fseek(f, conns.cbSize - sizeof(conns), SEEK_CUR);
212
213 /* Read all defined articulations. */
214 for (ULONG i = 0; i < conns.cConnections; i++) {
215 CONNECTION con;
216 if (fread(&con, sizeof(con), 1, f) != 1) return false;
217 out.push_back(con);
218 }
219 } else {
220 fseek(f, chunk.length, SEEK_CUR);
221 }
222 }
223
224 return true;
225}
226
227bool DLSFile::ReadDLSRegion(FileHandle &f, DWORD list_length, std::vector<DLSRegion> &out)
228{
229 DLSRegion &region = out.emplace_back();
230
231 /* Set default values. */
232 region.wave_sample.cbSize = 0;
233
234 while (list_length > 0) {
235 ChunkHeader chunk;
236 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
237 list_length -= chunk.length + sizeof(chunk);
238
239 if (chunk.type == FOURCC_LIST) {
240 /* Unwrap list header. */
241 if (fread(&chunk.type, sizeof(chunk.type), 1, f) != 1) return false;
242 chunk.length -= sizeof(chunk.type);
243 }
244
245 switch (chunk.type) {
246 case FOURCC_RGNH:
247 if (fread(&region.hdr, sizeof(region.hdr), 1, f) != 1) return false;
248 break;
249
250 case FOURCC_WSMP:
251 if (fread(&region.wave_sample, sizeof(region.wave_sample), 1, f) != 1) return false;
252 fseek(f, region.wave_sample.cbSize - sizeof(region.wave_sample), SEEK_CUR);
253
254 /* Read all defined sample loops. */
255 for (ULONG i = 0; i < region.wave_sample.cSampleLoops; i++) {
256 WLOOP loop;
257 if (fread(&loop, sizeof(loop), 1, f) != 1) return false;
258 region.wave_loops.push_back(loop);
259 }
260 break;
261
262 case FOURCC_WLNK:
263 if (fread(&region.wave, sizeof(region.wave), 1, f) != 1) return false;
264 break;
265
266 case FOURCC_LART: // List chunk
267 if (!this->ReadDLSArticulation(f, chunk.length, region.articulators)) return false;
268 break;
269
270 case FOURCC_INFO:
271 /* We don't care about info stuff. */
272 fseek(f, chunk.length, SEEK_CUR);
273 break;
274
275 default:
276 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));
277 fseek(f, chunk.length, SEEK_CUR);
278 break;
279 }
280 }
281
282 return true;
283}
284
285bool DLSFile::ReadDLSRegionList(FileHandle &f, DWORD list_length, DLSInstrument &instrument)
286{
287 while (list_length > 0) {
288 ChunkHeader chunk;
289 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
290 list_length -= chunk.length + sizeof(chunk);
291
292 if (chunk.type == FOURCC_LIST) {
293 FOURCC list_type;
294 if (fread(&list_type, sizeof(list_type), 1, f) != 1) return false;
295
296 if (list_type == FOURCC_RGN) {
297 this->ReadDLSRegion(f, chunk.length - sizeof(list_type), instrument.regions);
298 } else {
299 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));
300 fseek(f, chunk.length - sizeof(list_type), SEEK_CUR);
301 }
302 } else {
303 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));
304 fseek(f, chunk.length, SEEK_CUR);
305 }
306 }
307
308 return true;
309}
310
311bool DLSFile::ReadDLSInstrument(FileHandle &f, DWORD list_length)
312{
313 DLSInstrument &instrument = this->instruments.emplace_back();
314
315 while (list_length > 0) {
316 ChunkHeader chunk;
317 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
318 list_length -= chunk.length + sizeof(chunk);
319
320 if (chunk.type == FOURCC_LIST) {
321 /* Unwrap list header. */
322 if (fread(&chunk.type, sizeof(chunk.type), 1, f) != 1) return false;
323 chunk.length -= sizeof(chunk.type);
324 }
325
326 switch (chunk.type) {
327 case FOURCC_INSH:
328 if (fread(&instrument.hdr, sizeof(instrument.hdr), 1, f) != 1) return false;
329 break;
330
331 case FOURCC_LART: // List chunk
332 if (!this->ReadDLSArticulation(f, chunk.length, instrument.articulators)) return false;
333 break;
334
335 case FOURCC_LRGN: // List chunk
336 if (!this->ReadDLSRegionList(f, chunk.length, instrument)) return false;
337 break;
338
339 case FOURCC_INFO:
340 /* We don't care about info stuff. */
341 fseek(f, chunk.length, SEEK_CUR);
342 break;
343
344 default:
345 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));
346 fseek(f, chunk.length, SEEK_CUR);
347 break;
348 }
349 }
350
351 return true;
352}
353
355{
356 while (list_length > 0) {
357 ChunkHeader chunk;
358 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
359 list_length -= chunk.length + sizeof(chunk);
360
361 if (chunk.type == FOURCC_LIST) {
362 FOURCC list_type;
363 if (fread(&list_type, sizeof(list_type), 1, f) != 1) return false;
364
365 if (list_type == FOURCC_INS) {
366 Debug(driver, 6, "DLS: Reading instrument {}", (int)instruments.size());
367
368 if (!this->ReadDLSInstrument(f, chunk.length - sizeof(list_type))) return false;
369 } else {
370 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));
371 fseek(f, chunk.length - sizeof(list_type), SEEK_CUR);
372 }
373 } else {
374 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));
375 fseek(f, chunk.length, SEEK_CUR);
376 }
377 }
378
379 return true;
380}
381
382bool DLSFile::ReadDLSWave(FileHandle &f, DWORD list_length, long offset)
383{
384 DLSWave &wave = this->waves.emplace_back();
385
386 /* Set default values. */
387 wave.wave_sample.cbSize = sizeof(WSMPL);
388 wave.wave_sample.usUnityNote = 60;
389 wave.file_offset = offset; // Store file offset so we can resolve the wave pool table later on.
390
391 while (list_length > 0) {
392 ChunkHeader chunk;
393 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
394 list_length -= chunk.length + sizeof(chunk);
395
396 if (chunk.type == FOURCC_LIST) {
397 /* Unwrap list header. */
398 if (fread(&chunk.type, sizeof(chunk.type), 1, f) != 1) return false;
399 chunk.length -= sizeof(chunk.type);
400 }
401
402 switch (chunk.type) {
403 case FOURCC_fmt:
404 if (fread(&wave.fmt, sizeof(wave.fmt), 1, f) != 1) return false;
405 if (chunk.length > sizeof(wave.fmt)) fseek(f, chunk.length - sizeof(wave.fmt), SEEK_CUR);
406 break;
407
408 case FOURCC_WSMP:
409 if (fread(&wave.wave_sample, sizeof(wave.wave_sample), 1, f) != 1) return false;
410 fseek(f, wave.wave_sample.cbSize - sizeof(wave.wave_sample), SEEK_CUR);
411
412 /* Read all defined sample loops. */
413 for (ULONG i = 0; i < wave.wave_sample.cSampleLoops; i++) {
414 WLOOP loop;
415 if (fread(&loop, sizeof(loop), 1, f) != 1) return false;
416 wave.wave_loops.push_back(loop);
417 }
418 break;
419
420 case FOURCC_data:
421 wave.data.resize(chunk.length);
422 if (fread(&wave.data[0], sizeof(BYTE), wave.data.size(), f) != wave.data.size()) return false;
423 break;
424
425 case FOURCC_INFO:
426 /* We don't care about info stuff. */
427 fseek(f, chunk.length, SEEK_CUR);
428 break;
429
430 default:
431 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));
432 fseek(f, chunk.length, SEEK_CUR);
433 break;
434 }
435 }
436
437 return true;
438}
439
440bool DLSFile::ReadDLSWaveList(FileHandle &f, DWORD list_length)
441{
442 long base_offset = ftell(f);
443
444 while (list_length > 0) {
445 long chunk_offset = ftell(f);
446
447 ChunkHeader chunk;
448 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
449 list_length -= chunk.length + sizeof(chunk);
450
451 if (chunk.type == FOURCC_LIST) {
452 FOURCC list_type;
453 if (fread(&list_type, sizeof(list_type), 1, f) != 1) return false;
454
455 if (list_type == FOURCC_wave) {
456 Debug(driver, 6, "DLS: Reading wave {}", waves.size());
457
458 if (!this->ReadDLSWave(f, chunk.length - sizeof(list_type), chunk_offset - base_offset)) return false;
459 } else {
460 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));
461 fseek(f, chunk.length - sizeof(list_type), SEEK_CUR);
462 }
463 } else {
464 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));
465 fseek(f, chunk.length, SEEK_CUR);
466 }
467 }
468
469 return true;
470}
471
472bool DLSFile::LoadFile(std::string_view file)
473{
474 Debug(driver, 2, "DMusic: Try to load DLS file {}", file);
475
476 auto of = FileHandle::Open(file, "rb");
477 if (!of.has_value()) return false;
478 auto &f = *of;
479
480 /* Check DLS file header. */
481 ChunkHeader hdr;
482 FOURCC dls_type;
483 if (fread(&hdr, sizeof(hdr), 1, f) != 1) return false;
484 if (fread(&dls_type, sizeof(dls_type), 1, f) != 1) return false;
485 if (hdr.type != FOURCC_RIFF || dls_type != FOURCC_DLS) return false;
486
487 hdr.length -= sizeof(FOURCC);
488
489 Debug(driver, 2, "DMusic: Parsing DLS file");
490
491 DLSHEADER header{};
492
493 /* Iterate over all chunks in the file. */
494 while (hdr.length > 0) {
495 ChunkHeader chunk;
496 if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
497 hdr.length -= chunk.length + sizeof(chunk);
498
499 if (chunk.type == FOURCC_LIST) {
500 /* Unwrap list header. */
501 if (fread(&chunk.type, sizeof(chunk.type), 1, f) != 1) return false;
502 chunk.length -= sizeof(chunk.type);
503 }
504
505 switch (chunk.type) {
506 case FOURCC_COLH:
507 if (fread(&header, sizeof(header), 1, f) != 1) return false;
508 break;
509
510 case FOURCC_LINS: // List chunk
511 if (!this->ReadDLSInstrumentList(f, chunk.length)) return false;
512 break;
513
514 case FOURCC_WVPL: // List chunk
515 if (!this->ReadDLSWaveList(f, chunk.length)) return false;
516 break;
517
518 case FOURCC_PTBL:
519 POOLTABLE ptbl;
520 if (fread(&ptbl, sizeof(ptbl), 1, f) != 1) return false;
521 fseek(f, ptbl.cbSize - sizeof(ptbl), SEEK_CUR);
522
523 /* Read all defined cues. */
524 for (ULONG i = 0; i < ptbl.cCues; i++) {
525 POOLCUE cue;
526 if (fread(&cue, sizeof(cue), 1, f) != 1) return false;
527 this->pool_cues.push_back(cue);
528 }
529 break;
530
531 case FOURCC_INFO:
532 /* We don't care about info stuff. */
533 fseek(f, chunk.length, SEEK_CUR);
534 break;
535
536 default:
537 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));
538 fseek(f, chunk.length, SEEK_CUR);
539 break;
540 }
541 }
542
543 /* Have we read as many instruments as indicated? */
544 if (header.cInstruments != this->instruments.size()) return false;
545
546 /* Resolve wave pool table. */
547 for (std::vector<POOLCUE>::iterator cue = this->pool_cues.begin(); cue != this->pool_cues.end(); cue++) {
548 std::vector<DLSWave>::iterator w = std::ranges::find(this->waves, cue->ulOffset, &DLSWave::file_offset);
549 if (w != this->waves.end()) {
550 cue->ulOffset = (ULONG)(w - this->waves.begin());
551 } else {
552 cue->ulOffset = 0;
553 }
554 }
555
556 return true;
557}
558
559
560static uint8_t ScaleVolume(uint8_t original, uint8_t scale)
561{
562 return original * scale / 127;
563}
564
565static void TransmitChannelMsg(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, uint8_t status, uint8_t p1, uint8_t p2 = 0)
566{
567 if (buffer->PackStructured(rt, 0, status | (p1 << 8) | (p2 << 16)) == E_OUTOFMEMORY) {
568 /* Buffer is full, clear it and try again. */
569 _port->PlayBuffer(buffer);
570 buffer->Flush();
571
572 buffer->PackStructured(rt, 0, status | (p1 << 8) | (p2 << 16));
573 }
574}
575
576static void TransmitSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, const uint8_t *&msg_start, size_t &remaining)
577{
578 /* Find end of message. */
579 const uint8_t *msg_end = msg_start;
580 while (*msg_end != MIDIST_ENDSYSEX) msg_end++;
581 msg_end++; // Also include SysEx end byte.
582
583 if (buffer->PackUnstructured(rt, 0, msg_end - msg_start, const_cast<LPBYTE>(msg_start)) == E_OUTOFMEMORY) {
584 /* Buffer is full, clear it and try again. */
585 _port->PlayBuffer(buffer);
586 buffer->Flush();
587
588 buffer->PackUnstructured(rt, 0, msg_end - msg_start, const_cast<LPBYTE>(msg_start));
589 }
590
591 /* Update position in buffer. */
592 remaining -= msg_end - msg_start;
593 msg_start = msg_end;
594}
595
596static void TransmitStandardSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, MidiSysexMessage msg)
597{
598 size_t length = 0;
599 const uint8_t *data = MidiGetStandardSysexMessage(msg, length);
600 TransmitSysex(buffer, rt, data, length);
601}
602
609static void TransmitNotesOff(IDirectMusicBuffer *buffer, REFERENCE_TIME block_time, REFERENCE_TIME cur_time)
610{
611 for (int ch = 0; ch < 16; ch++) {
612 TransmitChannelMsg(buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_MODE_ALLNOTESOFF, 0);
613 TransmitChannelMsg(buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_SUSTAINSW, 0);
614 TransmitChannelMsg(buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_MODE_RESETALLCTRL, 0);
615 }
616
617 /* Performing a GM reset stops all sound and resets all parameters. */
618 TransmitStandardSysex(buffer, block_time + 20, MidiSysexMessage::ResetGM);
619 TransmitStandardSysex(buffer, block_time + 30, MidiSysexMessage::RolandSetReverb);
620
621 /* Explicitly flush buffer to make sure the messages are processed,
622 * as we want sound to stop immediately. */
623 _port->PlayBuffer(buffer);
624 buffer->Flush();
625
626 /* Wait until message time has passed. */
627 Sleep(Clamp((block_time - cur_time) / MS_TO_REFTIME, 5, 1000));
628}
629
630static void MidiThreadProc()
631{
632 Debug(driver, 2, "DMusic: Entering playback thread");
633
634 REFERENCE_TIME last_volume_time = 0; // timestamp of the last volume change
635 REFERENCE_TIME block_time = 0; // timestamp of the last block sent to the port
636 REFERENCE_TIME playback_start_time; // timestamp current file began playback
637 MidiFile current_file; // file currently being played from
638 PlaybackSegment current_segment; // segment info for current playback
639 size_t current_block = 0; // next block index to send
640 uint8_t current_volume = 0; // current effective volume setting
641 std::array<uint8_t, 16> channel_volumes; // last seen volume controller values in raw data
642
643 /* Get pointer to the reference clock of our output port. */
644 IReferenceClock *clock;
645 _port->GetLatencyClock(&clock);
646
647 REFERENCE_TIME cur_time;
648 clock->GetTime(&cur_time);
649
650 _port->PlayBuffer(_buffer);
651 _buffer->Flush();
652
653 DWORD next_timeout = 1000;
654 while (true) {
655 /* Wait for a signal from the GUI thread or until the time for the next event has come. */
656 DWORD wfso = WaitForSingleObject(_thread_event, next_timeout);
657
658 if (_playback.shutdown) {
659 _playback.playing = false;
660 break;
661 }
662
663 if (_playback.do_stop) {
664 Debug(driver, 2, "DMusic thread: Stopping playback");
665
666 /* Turn all notes off and wait a bit to allow the messages to be handled. */
667 clock->GetTime(&cur_time);
668 TransmitNotesOff(_buffer, block_time, cur_time);
669
670 _playback.playing = false;
671 _playback.do_stop = false;
672 block_time = 0;
673 next_timeout = 1000;
674 continue;
675 }
676
677 if (wfso == WAIT_OBJECT_0) {
678 if (_playback.do_start) {
679 Debug(driver, 2, "DMusic thread: Starting playback");
680 {
681 /* New scope to limit the time the mutex is locked. */
682 std::lock_guard<std::mutex> lock(_thread_mutex);
683
684 current_file.MoveFrom(_playback.next_file);
685 std::swap(_playback.next_segment, current_segment);
686 current_segment.start_block = 0;
687 current_block = 0;
688 _playback.playing = true;
689 _playback.do_start = false;
690 }
691
692 /* Reset playback device between songs. */
693 clock->GetTime(&cur_time);
694 TransmitNotesOff(_buffer, block_time, cur_time);
695
696 channel_volumes.fill(127);
697 /* Invalidate current volume. */
698 current_volume = UINT8_MAX;
699 last_volume_time = 0;
700
701 /* Take the current time plus the preload time as the music start time. */
702 clock->GetTime(&playback_start_time);
703 playback_start_time += _playback.preload_time * MS_TO_REFTIME;
704 }
705 }
706
707 if (_playback.playing) {
708 /* skip beginning of file? */
709 if (current_segment.start > 0 && current_block == 0 && current_segment.start_block == 0) {
710 /* find first block after start time and pretend playback started earlier
711 * this is to allow all blocks prior to the actual start to still affect playback,
712 * as they may contain important controller and program changes */
713 size_t preload_bytes = 0;
714 for (size_t bl = 0; bl < current_file.blocks.size(); bl++) {
715 MidiFile::DataBlock &block = current_file.blocks[bl];
716 preload_bytes += block.data.size();
717 if (block.ticktime >= current_segment.start) {
718 if (current_segment.loop) {
719 Debug(driver, 2, "DMusic: timer: loop from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl, block.ticktime, block.realtime / 1000.0, preload_bytes);
720 current_segment.start_block = bl;
721 break;
722 } else {
723 /* Skip the transmission delay compensation performed in the Win32 MIDI driver.
724 * The DMusic driver will most likely be used with the MS softsynth, which is not subject to transmission delays.
725 */
726 Debug(driver, 2, "DMusic: timer: start from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl, block.ticktime, block.realtime / 1000.0, preload_bytes);
728 break;
729 }
730 }
731 }
732 }
733
734 /* Get current playback timestamp. */
735 REFERENCE_TIME current_time;
736 clock->GetTime(&current_time);
737
738 /* Check for volume change. */
739 if (current_volume != _playback.new_volume) {
740 if (current_time - last_volume_time > 10 * MS_TO_REFTIME) {
741 Debug(driver, 2, "DMusic thread: volume change");
742 current_volume = _playback.new_volume;
743 last_volume_time = current_time;
744 for (int ch = 0; ch < 16; ch++) {
745 int vol = ScaleVolume(channel_volumes[ch], current_volume);
746 TransmitChannelMsg(_buffer, block_time + 1, MIDIST_CONTROLLER | ch, MIDICT_CHANVOLUME, vol);
747 }
748 _port->PlayBuffer(_buffer);
749 _buffer->Flush();
750 }
751 }
752
753 while (current_block < current_file.blocks.size()) {
755
756 /* check that block isn't at end-of-song override */
757 if (current_segment.end > 0 && block.ticktime >= current_segment.end) {
758 if (current_segment.loop) {
759 Debug(driver, 2, "DMusic thread: Looping song");
760 current_block = current_segment.start_block;
761 playback_start_time = current_time - current_file.blocks[current_block].realtime * MIDITIME_TO_REFTIME;
762 } else {
763 _playback.do_stop = true;
764 }
765 next_timeout = 0;
766 break;
767 }
768 /* check that block is not in the future */
769 REFERENCE_TIME playback_time = current_time - playback_start_time;
770 if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) {
771 /* Stop the thread loop until we are at the preload time of the next block. */
772 next_timeout = Clamp((block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000);
773 Debug(driver, 9, "DMusic thread: Next event in {} ms (music {}, ref {})", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time);
774 break;
775 }
776
777 /* Timestamp of the current block. */
778 block_time = playback_start_time + block.realtime * MIDITIME_TO_REFTIME;
779 Debug(driver, 9, "DMusic thread: Streaming block {} (cur={}, block={})", current_block, current_time / MS_TO_REFTIME, block_time / MS_TO_REFTIME);
780
781 const uint8_t *data = block.data.data();
782 size_t remaining = block.data.size();
783 uint8_t last_status = 0;
784 while (remaining > 0) {
785 /* MidiFile ought to have converted everything out of running status,
786 * but handle it anyway just to be safe */
787 uint8_t status = data[0];
788 if (status & 0x80) {
789 last_status = status;
790 data++;
791 remaining--;
792 } else {
793 status = last_status;
794 }
795 switch (status & 0xF0) {
796 case MIDIST_PROGCHG:
797 case MIDIST_CHANPRESS:
798 /* 2 byte channel messages */
799 TransmitChannelMsg(_buffer, block_time, status, data[0]);
800 data++;
801 remaining--;
802 break;
803 case MIDIST_NOTEOFF:
804 case MIDIST_NOTEON:
805 case MIDIST_POLYPRESS:
806 case MIDIST_PITCHBEND:
807 /* 3 byte channel messages */
808 TransmitChannelMsg(_buffer, block_time, status, data[0], data[1]);
809 data += 2;
810 remaining -= 2;
811 break;
812 case MIDIST_CONTROLLER:
813 /* controller change */
814 if (data[0] == MIDICT_CHANVOLUME) {
815 /* volume controller, adjust for user volume */
816 channel_volumes[status & 0x0F] = data[1];
817 int vol = ScaleVolume(data[1], current_volume);
818 TransmitChannelMsg(_buffer, block_time, status, data[0], vol);
819 } else {
820 /* handle other controllers normally */
821 TransmitChannelMsg(_buffer, block_time, status, data[0], data[1]);
822 }
823 data += 2;
824 remaining -= 2;
825 break;
826 case 0xF0:
827 /* system messages */
828 switch (status) {
829 case MIDIST_SYSEX: /* system exclusive */
830 TransmitSysex(_buffer, block_time, data, remaining);
831 break;
832 case MIDIST_TC_QFRAME: /* time code quarter frame */
833 case MIDIST_SONGSEL: /* song select */
834 data++;
835 remaining--;
836 break;
837 case MIDIST_SONGPOSPTR: /* song position pointer */
838 data += 2;
839 remaining -= 2;
840 break;
841 default: /* remaining have no data bytes */
842 break;
843 }
844 break;
845 }
846 }
847
849 }
850
851 /* Anything in the playback buffer? Send it down the port. */
852 DWORD used_buffer = 0;
853 _buffer->GetUsedBytes(&used_buffer);
854 if (used_buffer > 0) {
855 _port->PlayBuffer(_buffer);
856 _buffer->Flush();
857 }
858
859 /* end? */
860 if (current_block == current_file.blocks.size()) {
861 if (current_segment.loop) {
862 current_block = current_segment.start_block;
863 playback_start_time = block_time - current_file.blocks[current_block].realtime * MIDITIME_TO_REFTIME;
864 } else {
865 _playback.do_stop = true;
866 }
867 next_timeout = 0;
868 }
869 }
870 }
871
872 Debug(driver, 2, "DMusic: Exiting playback thread");
873
874 /* Turn all notes off and wait a bit to allow the messages to be handled by real hardware. */
875 clock->GetTime(&cur_time);
876 TransmitNotesOff(_buffer, block_time, cur_time);
877 Sleep(_playback.preload_time * 4);
878
879 clock->Release();
880}
881
882static void * DownloadArticulationData(int base_offset, void *data, const std::vector<CONNECTION> &artic)
883{
884 DMUS_ARTICULATION2 *art = (DMUS_ARTICULATION2 *)data;
885 art->ulArtIdx = base_offset + 1;
886 art->ulFirstExtCkIdx = 0;
887 art->ulNextArtIdx = 0;
888
889 CONNECTIONLIST *con_list = (CONNECTIONLIST *)(art + 1);
890 con_list->cbSize = sizeof(CONNECTIONLIST);
891 con_list->cConnections = (ULONG)artic.size();
892
893 return std::copy_n(artic.begin(), artic.size(), reinterpret_cast<CONNECTION *>(con_list + 1));
894}
895
896static std::optional<std::string_view> LoadDefaultDLSFile(std::optional<std::string_view> user_dls)
897{
898 DMUS_PORTCAPS caps{};
899 caps.dwSize = sizeof(DMUS_PORTCAPS);
900 _port->GetCaps(&caps);
901
902 /* Nothing to unless it is a synth with instrument download that doesn't come with GM voices by default. */
903 if ((caps.dwFlags & (DMUS_PC_DLS | DMUS_PC_DLS2)) != 0 && (caps.dwFlags & DMUS_PC_GMINHARDWARE) == 0) {
904 DLSFile dls_file;
905
906 if (!user_dls.has_value()) {
907 /* Try loading the default GM DLS file stored in the registry. */
908 HKEY hkDM;
909 if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic", 0, KEY_READ, &hkDM))) {
910 wchar_t dls_path[MAX_PATH];
911 DWORD buf_size = sizeof(dls_path); // Buffer size as to be given in bytes!
912 if (SUCCEEDED(RegQueryValueEx(hkDM, L"GMFilePath", nullptr, nullptr, (LPBYTE)dls_path, &buf_size))) {
913 wchar_t expand_path[MAX_PATH * 2];
914 ExpandEnvironmentStrings(dls_path, expand_path, static_cast<DWORD>(std::size(expand_path)));
915 if (!dls_file.LoadFile(FS2OTTD(expand_path))) Debug(driver, 1, "Failed to load default GM DLS file from registry");
916 }
917 RegCloseKey(hkDM);
918 }
919
920 /* If we couldn't load the file from the registry, try again at the default install path of the GM DLS file. */
921 if (dls_file.instruments.empty()) {
922 static const wchar_t *DLS_GM_FILE = L"%windir%\\System32\\drivers\\gm.dls";
923 wchar_t path[MAX_PATH];
924 ExpandEnvironmentStrings(DLS_GM_FILE, path, static_cast<DWORD>(std::size(path)));
925
926 if (!dls_file.LoadFile(FS2OTTD(path))) return "Can't load GM DLS collection";
927 }
928 } else {
929 if (!dls_file.LoadFile(*user_dls)) return "Can't load GM DLS collection";
930 }
931
932 /* Get download port and allocate download IDs. */
933 IDirectMusicPortDownload *download_port = nullptr;
934 if (FAILED(_port->QueryInterface(IID_IDirectMusicPortDownload, (LPVOID *)&download_port))) return "Can't get download port";
935
936 DWORD dlid_wave = 0, dlid_inst = 0;
937 if (FAILED(download_port->GetDLId(&dlid_wave, (DWORD)dls_file.waves.size())) || FAILED(download_port->GetDLId(&dlid_inst, (DWORD)dls_file.instruments.size()))) {
938 download_port->Release();
939 return "Can't get enough DLS ids";
940 }
941
942 DWORD dwAppend = 0;
943 download_port->GetAppend(&dwAppend);
944
945 /* Download wave data. */
946 for (DWORD i = 0; i < dls_file.waves.size(); i++) {
947 IDirectMusicDownload *dl_wave = nullptr;
948 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))) {
949 download_port->Release();
950 return "Can't allocate wave download buffer";
951 }
952
953 WAVE_DOWNLOAD *wave;
954 DWORD wave_size = 0;
955 if (FAILED(dl_wave->GetBuffer((LPVOID *)&wave, &wave_size))) {
956 dl_wave->Release();
957 download_port->Release();
958 return "Can't get wave download buffer";
959 }
960
961 /* Fill download data. */
962 *wave = {};
963 wave->dlInfo.dwDLType = DMUS_DOWNLOADINFO_WAVE;
964 wave->dlInfo.cbSize = wave_size;
965 wave->dlInfo.dwDLId = dlid_wave + i;
966 wave->dlInfo.dwNumOffsetTableEntries = 2;
967 wave->ulOffsetTable[0] = offsetof(WAVE_DOWNLOAD, dmWave);
968 wave->ulOffsetTable[1] = offsetof(WAVE_DOWNLOAD, dmWaveData);
969 wave->dmWave.ulWaveDataIdx = 1;
970 wave->dmWaveData.cbSize = (DWORD)dls_file.waves[i].data.size();
971 reinterpret_cast<PCMWAVEFORMAT &>(wave->dmWave.WaveformatEx) = dls_file.waves[i].fmt;
972 std::copy_n(dls_file.waves[i].data.begin(), dls_file.waves[i].data.size(), wave->dmWaveData.byData);
973
974 _dls_downloads.push_back(dl_wave);
975 if (FAILED(download_port->Download(dl_wave))) {
976 download_port->Release();
977 return "Downloading DLS wave failed";
978 }
979 }
980
981 /* Download instrument data. */
982 for (DWORD i = 0; i < dls_file.instruments.size(); i++) {
983 DWORD offsets = 1 + (DWORD)dls_file.instruments[i].regions.size();
984
985 /* Calculate download size for the instrument. */
986 size_t i_size = sizeof(DMUS_DOWNLOADINFO) + sizeof(DMUS_INSTRUMENT);
987 if (!dls_file.instruments[i].articulators.empty()) {
988 /* Articulations are stored as two chunks, one containing meta data and one with the actual articulation data. */
989 offsets += 2;
990 i_size += sizeof(DMUS_ARTICULATION2) + sizeof(CONNECTIONLIST) + sizeof(CONNECTION) * dls_file.instruments[i].articulators.size();
991 }
992
993 for (std::vector<DLSFile::DLSRegion>::iterator rgn = dls_file.instruments[i].regions.begin(); rgn != dls_file.instruments[i].regions.end(); rgn++) {
994 if (!rgn->articulators.empty()) {
995 offsets += 2;
996 i_size += sizeof(DMUS_ARTICULATION2) + sizeof(CONNECTIONLIST) + sizeof(CONNECTION) * rgn->articulators.size();
997 }
998
999 /* Region size depends on the number of wave loops. The size of the
1000 * declared structure already accounts for one loop. */
1001 if (rgn->wave_sample.cbSize != 0) {
1002 i_size += sizeof(DMUS_REGION) - sizeof(DMUS_REGION::WLOOP) + sizeof(WLOOP) * rgn->wave_loops.size();
1003 } else {
1004 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();
1005 }
1006 }
1007
1008 i_size += offsets * sizeof(ULONG);
1009
1010 /* Allocate download buffer. */
1011 IDirectMusicDownload *dl_inst = nullptr;
1012 if (FAILED(download_port->AllocateBuffer((DWORD)i_size, &dl_inst))) {
1013 download_port->Release();
1014 return "Can't allocate instrument download buffer";
1015 }
1016
1017 void *instrument;
1018 DWORD inst_size = 0;
1019 if (FAILED(dl_inst->GetBuffer((LPVOID *)&instrument, &inst_size))) {
1020 dl_inst->Release();
1021 download_port->Release();
1022 return "Can't get instrument download buffer";
1023 }
1024 const std::byte *inst_base = reinterpret_cast<const std::byte *>(instrument);
1025
1026 /* Fill download header. */
1027 DMUS_DOWNLOADINFO *d_info = (DMUS_DOWNLOADINFO *)instrument;
1028 d_info->dwDLType = DMUS_DOWNLOADINFO_INSTRUMENT2;
1029 d_info->cbSize = inst_size;
1030 d_info->dwDLId = dlid_inst + i;
1031 d_info->dwNumOffsetTableEntries = offsets;
1032 instrument = d_info + 1;
1033
1034 /* Download offset table; contains the offsets of all chunks relative to the buffer start. */
1035 ULONG *offset_table = (ULONG *)instrument;
1036 instrument = offset_table + offsets;
1037 int last_offset = 0;
1038
1039 /* Instrument header. */
1040 DMUS_INSTRUMENT *inst_data = (DMUS_INSTRUMENT *)instrument;
1041 *inst_data = {};
1042 offset_table[last_offset++] = reinterpret_cast<const std::byte *>(inst_data) - inst_base;
1043 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);
1044 instrument = inst_data + 1;
1045
1046 /* Write global articulations. */
1047 if (!dls_file.instruments[i].articulators.empty()) {
1048 inst_data->ulGlobalArtIdx = last_offset;
1049 offset_table[last_offset++] = reinterpret_cast<const std::byte *>(instrument) - inst_base;
1050 offset_table[last_offset++] = reinterpret_cast<const std::byte *>(instrument) + sizeof(DMUS_ARTICULATION2) - inst_base;
1051
1052 instrument = DownloadArticulationData(inst_data->ulGlobalArtIdx, instrument, dls_file.instruments[i].articulators);
1053 assert(reinterpret_cast<const std::byte *>(instrument) - inst_base <= (ptrdiff_t)inst_size);
1054 }
1055
1056 /* Write out regions. */
1057 inst_data->ulFirstRegionIdx = last_offset;
1058 for (uint j = 0; j < dls_file.instruments[i].regions.size(); j++) {
1059 DLSFile::DLSRegion &rgn = dls_file.instruments[i].regions[j];
1060
1061 DMUS_REGION *inst_region = (DMUS_REGION *)instrument;
1062 offset_table[last_offset++] = reinterpret_cast<const std::byte *>(inst_region) - inst_base;
1063 inst_region->RangeKey = rgn.hdr.RangeKey;
1064 inst_region->RangeVelocity = rgn.hdr.RangeVelocity;
1065 inst_region->fusOptions = rgn.hdr.fusOptions;
1066 inst_region->usKeyGroup = rgn.hdr.usKeyGroup;
1067 inst_region->ulFirstExtCkIdx = 0;
1068
1069 ULONG wave_id = dls_file.pool_cues[rgn.wave.ulTableIndex].ulOffset;
1070 inst_region->WaveLink = rgn.wave;
1071 inst_region->WaveLink.ulTableIndex = wave_id + dlid_wave;
1072
1073 /* The wave sample data will be taken from the region, if defined, otherwise from the wave itself. */
1074 if (rgn.wave_sample.cbSize != 0) {
1075 inst_region->WSMP = rgn.wave_sample;
1076 instrument = std::copy_n(rgn.wave_loops.begin(), rgn.wave_loops.size(), inst_region->WLOOP);
1077 } else {
1078 inst_region->WSMP = rgn.wave_sample;
1079 instrument = std::copy_n(dls_file.waves[wave_id].wave_loops.begin(), dls_file.waves[wave_id].wave_loops.size(), inst_region->WLOOP);
1080 }
1081
1082 /* Write local articulator data. */
1083 if (!rgn.articulators.empty()) {
1084 inst_region->ulRegionArtIdx = last_offset;
1085 offset_table[last_offset++] = reinterpret_cast<const std::byte *>(instrument) - inst_base;
1086 offset_table[last_offset++] = reinterpret_cast<const std::byte *>(instrument) + sizeof(DMUS_ARTICULATION2) - inst_base;
1087
1088 instrument = DownloadArticulationData(inst_region->ulRegionArtIdx, instrument, rgn.articulators);
1089 } else {
1090 inst_region->ulRegionArtIdx = 0;
1091 }
1092 assert(reinterpret_cast<const std::byte *>(instrument) - inst_base <= (ptrdiff_t)inst_size);
1093
1094 /* Link to the next region unless this was the last one.*/
1095 inst_region->ulNextRegionIdx = j < dls_file.instruments[i].regions.size() - 1 ? last_offset : 0;
1096 }
1097
1098 _dls_downloads.push_back(dl_inst);
1099 if (FAILED(download_port->Download(dl_inst))) {
1100 download_port->Release();
1101 return "Downloading DLS instrument failed";
1102 }
1103 }
1104
1105 download_port->Release();
1106 }
1107
1108 return std::nullopt;
1109}
1110
1111
1112std::optional<std::string_view> MusicDriver_DMusic::Start(const StringList &parm)
1113{
1114 /* Initialize COM */
1115 if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) return "COM initialization failed";
1116
1117 /* Create the DirectMusic object */
1118 if (FAILED(CoCreateInstance(
1119 CLSID_DirectMusic,
1120 nullptr,
1121 CLSCTX_INPROC,
1122 IID_IDirectMusic,
1123 (LPVOID*)&_music
1124 ))) {
1125 return "Failed to create the music object";
1126 }
1127
1128 /* Assign sound output device. */
1129 if (FAILED(_music->SetDirectSound(nullptr, nullptr))) return "Can't set DirectSound interface";
1130
1131 /* MIDI events need to be send to the synth in time before their playback time
1132 * has come. By default, we try send any events at least 50 ms before playback. */
1133 _playback.preload_time = GetDriverParamInt(parm, "preload", 50);
1134
1135 int pIdx = GetDriverParamInt(parm, "port", -1);
1136 if (_debug_driver_level > 0) {
1137 /* Print all valid output ports. */
1138 char desc[DMUS_MAX_DESCRIPTION];
1139
1140 DMUS_PORTCAPS caps{};
1141 caps.dwSize = sizeof(DMUS_PORTCAPS);
1142
1143 Debug(driver, 1, "Detected DirectMusic ports:");
1144 for (int i = 0; _music->EnumPort(i, &caps) == S_OK; i++) {
1145 if (caps.dwClass == DMUS_PC_OUTPUTCLASS) {
1146 Debug(driver, 1, " {}: {}{}", i, convert_from_fs(caps.wszDescription, desc), i == pIdx ? " (selected)" : "");
1147 }
1148 }
1149 }
1150
1151 GUID guidPort;
1152 if (pIdx >= 0) {
1153 /* Check if the passed port is a valid port. */
1154 DMUS_PORTCAPS caps{};
1155 caps.dwSize = sizeof(DMUS_PORTCAPS);
1156 if (FAILED(_music->EnumPort(pIdx, &caps))) return "Supplied port parameter is not a valid port";
1157 if (caps.dwClass != DMUS_PC_OUTPUTCLASS) return "Supplied port parameter is not an output port";
1158 guidPort = caps.guidPort;
1159 } else {
1160 if (FAILED(_music->GetDefaultPort(&guidPort))) return "Can't query default music port";
1161 }
1162
1163 /* Create new port. */
1164 DMUS_PORTPARAMS params{};
1165 params.dwSize = sizeof(DMUS_PORTPARAMS);
1166 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
1167 params.dwChannelGroups = 1;
1168 if (FAILED(_music->CreatePort(guidPort, &params, &_port, nullptr))) return "Failed to create port";
1169 /* Activate port. */
1170 if (FAILED(_port->Activate(TRUE))) return "Failed to activate port";
1171
1172 /* Create playback buffer. */
1173 DMUS_BUFFERDESC desc{};
1174 desc.dwSize = sizeof(DMUS_BUFFERDESC);
1175 desc.guidBufferFormat = KSDATAFORMAT_SUBTYPE_DIRECTMUSIC;
1176 desc.cbBuffer = 1024;
1177 if (FAILED(_music->CreateMusicBuffer(&desc, &_buffer, nullptr))) return "Failed to create music buffer";
1178
1179 /* On soft-synths (e.g. the default DirectMusic one), we might need to load a wavetable set to get music. */
1180 auto dls = LoadDefaultDLSFile(GetDriverParam(parm, "dls"));
1181 if (dls.has_value()) return dls;
1182
1183 /* Create playback thread and synchronization primitives. */
1184 _thread_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
1185 if (_thread_event == nullptr) return "Can't create thread shutdown event";
1186
1187 if (!StartNewThread(&_dmusic_thread, "ottd:dmusic", &MidiThreadProc)) return "Can't create MIDI output thread";
1188
1189 return std::nullopt;
1190}
1191
1197
1198
1200{
1201 if (_dmusic_thread.joinable()) {
1202 _playback.shutdown = true;
1203 SetEvent(_thread_event);
1204 _dmusic_thread.join();
1205 }
1206
1207 /* Unloaded any instruments we loaded. */
1208 if (!_dls_downloads.empty()) {
1209 IDirectMusicPortDownload *download_port = nullptr;
1210 _port->QueryInterface(IID_IDirectMusicPortDownload, (LPVOID *)&download_port);
1211
1212 /* Instruments refer to waves. As the waves are at the beginning of the download list,
1213 * do the unload from the back so that references are cleared properly. */
1214 for (std::vector<IDirectMusicDownload *>::reverse_iterator i = _dls_downloads.rbegin(); download_port != nullptr && i != _dls_downloads.rend(); i++) {
1215 download_port->Unload(*i);
1216 (*i)->Release();
1217 }
1218 _dls_downloads.clear();
1219
1220 if (download_port != nullptr) download_port->Release();
1221 }
1222
1223 if (_buffer != nullptr) {
1224 _buffer->Release();
1225 _buffer = nullptr;
1226 }
1227
1228 if (_port != nullptr) {
1229 _port->Activate(FALSE);
1230 _port->Release();
1231 _port = nullptr;
1232 }
1233
1234 if (_music != nullptr) {
1235 _music->Release();
1236 _music = nullptr;
1237 }
1238
1239 if (_thread_event != nullptr) {
1240 CloseHandle(_thread_event);
1241 _thread_event = nullptr;
1242 }
1243
1244 CoUninitialize();
1245}
1246
1247
1249{
1250 std::lock_guard<std::mutex> lock(_thread_mutex);
1251
1252 if (!_playback.next_file.LoadSong(song)) return;
1253
1254 _playback.next_segment.start = song.override_start;
1255 _playback.next_segment.end = song.override_end;
1256 _playback.next_segment.loop = song.loop;
1257
1258 _playback.do_start = true;
1259 SetEvent(_thread_event);
1260}
1261
1262
1264{
1265 _playback.do_stop = true;
1266 SetEvent(_thread_event);
1267}
1268
1269
1271{
1272 return _playback.playing || _playback.do_start;
1273}
1274
1275
1277{
1278 _playback.new_volume = vol;
1279}
Generic functions for replacing base data (graphics, sounds).
Generic functions for replacing base music data.
Factory for the DirectX music player.
Definition dmusic.h:35
static std::optional< FileHandle > Open(const std::string &filename, std::string_view mode)
Open an RAII file handle if possible.
Definition fileio.cpp:1173
void SetVolume(uint8_t vol) override
Set the volume, if possible.
Definition dmusic.cpp:1276
void StopSong() override
Stop playing the current song.
Definition dmusic.cpp:1263
std::optional< std::string_view > Start(const StringList &param) override
Start this driver.
Definition dmusic.cpp:1112
bool IsSongPlaying() override
Are we currently playing a song?
Definition dmusic.cpp:1270
void Stop() override
Stop this driver.
Definition dmusic.cpp:1199
~MusicDriver_DMusic() override
Stop the playing of the music.
Definition dmusic.cpp:1193
void PlaySong(const MusicSongInfo &song) override
Play a particular song.
Definition dmusic.cpp:1248
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
bool playing
flag indicating that playback is active
Definition dmusic.cpp:170
uint8_t new_volume
volume setting to change to
Definition dmusic.cpp:175
bool shutdown
flag to indicate playback thread shutdown
Definition dmusic.cpp:169
static IDirectMusicBuffer * _buffer
The buffer object collects the data to sent.
Definition dmusic.cpp:193
bool do_stop
flag for stopping playback at next opportunity
Definition dmusic.cpp:172
int preload_time
preload time for music blocks.
Definition dmusic.cpp:174
static std::thread _dmusic_thread
Handle to our worker thread.
Definition dmusic.cpp:182
static constexpr REFERENCE_TIME MS_TO_REFTIME
DirectMusic time base is 100 ns.
Definition dmusic.cpp:36
MidiFile next_file
upcoming file to play
Definition dmusic.cpp:177
static IDirectMusicPort * _port
The port object lets us send MIDI data to the synthesizer.
Definition dmusic.cpp:191
bool do_start
flag for starting playback of next_file at next opportunity
Definition dmusic.cpp:171
static std::vector< IDirectMusicDownload * > _dls_downloads
List of downloaded DLS instruments.
Definition dmusic.cpp:195
static HANDLE _thread_event
Event to signal the thread that it should look at a state change.
Definition dmusic.cpp:184
PACK_N(struct ChunkHeader { FOURCC type;DWORD length;}, 2)
A RIFF chunk header.
static constexpr REFERENCE_TIME MIDITIME_TO_REFTIME
Time base of the midi file reader is 1 us.
Definition dmusic.cpp:37
static std::mutex _thread_mutex
Lock access to playback data that is not thread-safe.
Definition dmusic.cpp:186
static IDirectMusic * _music
The direct music object manages buffers and ports.
Definition dmusic.cpp:189
PlaybackSegment next_segment
segment info for upcoming file
Definition dmusic.cpp:178
static void TransmitNotesOff(IDirectMusicBuffer *buffer, REFERENCE_TIME block_time, REFERENCE_TIME cur_time)
Transmit 'Note off' messages to all MIDI channels.
Definition dmusic.cpp:609
Base of playing music via DirectMusic.
int GetDriverParamInt(const StringList &parm, std::string_view name, int def)
Get an integer parameter the list of parameters.
Definition driver.cpp:79
std::optional< std::string_view > GetDriverParam(const StringList &parm, std::string_view name)
Get a string parameter the list of parameters.
Definition driver.cpp:47
Functions for standard in/out file operations.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
Declarations for MIDI data.
const uint8_t * MidiGetStandardSysexMessage(MidiSysexMessage msg, size_t &length)
Retrieve a well-known MIDI system exclusive message.
Definition midifile.cpp:39
MidiSysexMessage
Well-known MIDI system exclusive message values for use with the MidiGetStandardSysexMessage function...
Definition midi.h:142
@ ResetGM
Reset device to General MIDI defaults.
Definition midi.h:144
@ RolandSetReverb
Set up Roland SoundCanvas reverb room as TTD does.
Definition midi.h:150
@ MIDIST_ENDSYSEX
only occurs in realtime data
Definition midi.h:39
Parser for standard MIDI files.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
std::vector< std::string > StringList
Type for a list of strings.
Definition string_type.h:60
Instrument definition read from a DLS file.
Definition dmusic.cpp:57
An instrument region maps a note range to wave data.
Definition dmusic.cpp:47
Wave data definition from a DLS file.
Definition dmusic.cpp:65
A DLS file.
Definition dmusic.cpp:45
bool ReadDLSInstrumentList(FileHandle &f, DWORD list_length)
Load a list of instruments from a DLS file.
Definition dmusic.cpp:354
bool LoadFile(std::string_view file)
Try loading a DLS file into memory.
Definition dmusic.cpp:472
bool ReadDLSWaveList(FileHandle &f, DWORD list_length)
Load a list of waves from a DLS file.
Definition dmusic.cpp:440
bool ReadDLSRegionList(FileHandle &f, DWORD list_length, DLSInstrument &instrument)
Load a list of regions from a DLS file.
Definition dmusic.cpp:285
bool ReadDLSWave(FileHandle &f, DWORD list_length, long offset)
Load a single wave from a DLS file.
Definition dmusic.cpp:382
bool ReadDLSRegion(FileHandle &f, DWORD list_length, std::vector< DLSRegion > &out)
Load a single region from a DLS file.
Definition dmusic.cpp:227
bool ReadDLSInstrument(FileHandle &f, DWORD list_length)
Load a single instrument from a DLS file.
Definition dmusic.cpp:311
bool ReadDLSArticulation(FileHandle &f, DWORD list_length, std::vector< CONNECTION > &out)
Load an articulation structure from a DLS file.
Definition dmusic.cpp:201
std::vector< uint8_t > data
raw midi data contained in block
Definition midifile.hpp:22
uint32_t ticktime
tick number since start of file this block should be triggered at
Definition midifile.hpp:20
int64_t realtime
real-time (microseconds) since start of file this block should be triggered at
Definition midifile.hpp:21
Metadata about a music track.
int override_start
MIDI ticks to skip over in beginning.
bool loop
song should play in a tight loop if possible, never ending
int override_end
MIDI tick to end the song at (0 if no override).
Base of all threads.
bool StartNewThread(std::thread *thr, std::string_view name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
Definition thread.h:47
std::string_view convert_from_fs(const std::wstring_view src, std::span< char > dst_buf)
Convert to OpenTTD's encoding from that of the environment in UNICODE.
Definition win32.cpp:386
std::string FS2OTTD(std::wstring_view name)
Convert to OpenTTD's encoding from a wide string.
Definition win32.cpp:352
Declarations of functions for MS windows systems.
uint8_t current_volume
current effective volume setting
Definition win32_m.cpp:40
MidiFile current_file
file currently being played from
Definition win32_m.cpp:43
PlaybackSegment current_segment
segment info for current playback
Definition win32_m.cpp:44
size_t current_block
next block index to send
Definition win32_m.cpp:46
std::array< uint8_t, 16 > channel_volumes
last seen volume controller values in raw data
Definition win32_m.cpp:50
std::mutex lock
synchronization for playback status fields
Definition win32_m.cpp:35
DWORD playback_start_time
timestamp current file began playback
Definition win32_m.cpp:45