OpenTTD Source: src/music/dmusic.cpp Source File

OpenTTD Source 15.3

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