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