12#ifdef WIN32_LEAN_AND_MEAN
13# undef WIN32_LEAN_AND_MEAN
16#include "../os/windows/win32.h"
17#include "../core/mem_func.hpp"
19#include "../fileio_func.h"
20#include "../base_media_base.h"
21#include "../base_media_music.h"
23#include "midifile.hpp"
31#include "../safeguards.h"
34# pragma comment(lib, "ole32.lib")
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')
53 std::vector<WLOOP> wave_loops;
54 std::vector<CONNECTION> articulators;
61 std::vector<CONNECTION> articulators;
62 std::vector<DLSRegion> regions;
70 std::vector<BYTE> data;
73 std::vector<WLOOP> wave_loops;
76 std::vector<DLSInstrument> instruments;
77 std::vector<POOLCUE> pool_cues;
78 std::vector<DLSWave> waves;
81 bool LoadFile(
const std::string &file);
101PACK_N(
struct ChunkHeader {
107PACK_N(
struct WAVE_DOWNLOAD {
108 DMUS_DOWNLOADINFO dlInfo;
109 ULONG ulOffsetTable[2];
111 DMUS_WAVEDATA dmWaveData;
143static IDirectMusicPort *
_port =
nullptr;
155 while (list_length > 0) {
157 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
158 list_length -= chunk.length +
sizeof(chunk);
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);
166 for (ULONG i = 0; i < conns.cConnections; i++) {
168 if (fread(&con,
sizeof(con), 1, f) != 1)
return false;
172 fseek(f, chunk.length, SEEK_CUR);
184 region.wave_sample.cbSize = 0;
186 while (list_length > 0) {
188 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
189 list_length -= chunk.length +
sizeof(chunk);
191 if (chunk.type == FOURCC_LIST) {
193 if (fread(&chunk.type,
sizeof(chunk.type), 1, f) != 1)
return false;
194 chunk.length -=
sizeof(chunk.type);
197 switch (chunk.type) {
199 if (fread(®ion.hdr,
sizeof(region.hdr), 1, f) != 1)
return false;
203 if (fread(®ion.wave_sample,
sizeof(region.wave_sample), 1, f) != 1)
return false;
204 fseek(f, region.wave_sample.cbSize -
sizeof(region.wave_sample), SEEK_CUR);
207 for (ULONG i = 0; i < region.wave_sample.cSampleLoops; i++) {
209 if (fread(&loop,
sizeof(loop), 1, f) != 1)
return false;
210 region.wave_loops.push_back(loop);
215 if (fread(®ion.wave,
sizeof(region.wave), 1, f) != 1)
return false;
224 fseek(f, chunk.length, SEEK_CUR);
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);
239 while (list_length > 0) {
241 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
242 list_length -= chunk.length +
sizeof(chunk);
244 if (chunk.type == FOURCC_LIST) {
246 if (fread(&list_type,
sizeof(list_type), 1, f) != 1)
return false;
248 if (list_type == FOURCC_RGN) {
249 this->
ReadDLSRegion(f, chunk.length -
sizeof(list_type), instrument.regions);
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);
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);
265 DLSInstrument &instrument = this->instruments.emplace_back();
267 while (list_length > 0) {
269 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
270 list_length -= chunk.length +
sizeof(chunk);
272 if (chunk.type == FOURCC_LIST) {
274 if (fread(&chunk.type,
sizeof(chunk.type), 1, f) != 1)
return false;
275 chunk.length -=
sizeof(chunk.type);
278 switch (chunk.type) {
280 if (fread(&instrument.hdr,
sizeof(instrument.hdr), 1, f) != 1)
return false;
293 fseek(f, chunk.length, SEEK_CUR);
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);
308 while (list_length > 0) {
310 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
311 list_length -= chunk.length +
sizeof(chunk);
313 if (chunk.type == FOURCC_LIST) {
315 if (fread(&list_type,
sizeof(list_type), 1, f) != 1)
return false;
317 if (list_type == FOURCC_INS) {
318 Debug(driver, 6,
"DLS: Reading instrument {}", (
int)instruments.size());
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);
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);
336 DLSWave &wave = this->waves.emplace_back();
340 wave.wave_sample.cbSize =
sizeof(WSMPL);
341 wave.wave_sample.usUnityNote = 60;
342 wave.file_offset = offset;
344 while (list_length > 0) {
346 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
347 list_length -= chunk.length +
sizeof(chunk);
349 if (chunk.type == FOURCC_LIST) {
351 if (fread(&chunk.type,
sizeof(chunk.type), 1, f) != 1)
return false;
352 chunk.length -=
sizeof(chunk.type);
355 switch (chunk.type) {
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);
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);
366 for (ULONG i = 0; i < wave.wave_sample.cSampleLoops; i++) {
368 if (fread(&loop,
sizeof(loop), 1, f) != 1)
return false;
369 wave.wave_loops.push_back(loop);
374 wave.data.resize(chunk.length);
375 if (fread(&wave.data[0],
sizeof(BYTE), wave.data.size(), f) != wave.data.size())
return false;
380 fseek(f, chunk.length, SEEK_CUR);
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);
395 long base_offset = ftell(f);
397 while (list_length > 0) {
398 long chunk_offset = ftell(f);
401 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
402 list_length -= chunk.length +
sizeof(chunk);
404 if (chunk.type == FOURCC_LIST) {
406 if (fread(&list_type,
sizeof(list_type), 1, f) != 1)
return false;
408 if (list_type == FOURCC_wave) {
409 Debug(driver, 6,
"DLS: Reading wave {}", waves.size());
411 if (!this->
ReadDLSWave(f, chunk.length -
sizeof(list_type), chunk_offset - base_offset))
return false;
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);
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);
427 Debug(driver, 2,
"DMusic: Try to load DLS file {}", file);
430 if (!of.has_value())
return false;
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;
440 hdr.length -=
sizeof(FOURCC);
442 Debug(driver, 2,
"DMusic: Parsing DLS file");
448 while (hdr.length > 0) {
450 if (fread(&chunk,
sizeof(chunk), 1, f) != 1)
return false;
451 hdr.length -= chunk.length +
sizeof(chunk);
453 if (chunk.type == FOURCC_LIST) {
455 if (fread(&chunk.type,
sizeof(chunk.type), 1, f) != 1)
return false;
456 chunk.length -=
sizeof(chunk.type);
459 switch (chunk.type) {
461 if (fread(&header,
sizeof(header), 1, f) != 1)
return false;
474 if (fread(&ptbl,
sizeof(ptbl), 1, f) != 1)
return false;
475 fseek(f, ptbl.cbSize -
sizeof(ptbl), SEEK_CUR);
478 for (ULONG i = 0; i < ptbl.cCues; i++) {
480 if (fread(&cue,
sizeof(cue), 1, f) != 1)
return false;
481 this->pool_cues.push_back(cue);
487 fseek(f, chunk.length, SEEK_CUR);
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);
498 if (header.cInstruments != this->instruments.size())
return false;
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());
514static uint8_t ScaleVolume(uint8_t original, uint8_t scale)
516 return original * scale / 127;
519static void TransmitChannelMsg(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, uint8_t status, uint8_t p1, uint8_t p2 = 0)
521 if (buffer->PackStructured(rt, 0, status | (p1 << 8) | (p2 << 16)) == E_OUTOFMEMORY) {
523 _port->PlayBuffer(buffer);
526 buffer->PackStructured(rt, 0, status | (p1 << 8) | (p2 << 16));
530static void TransmitSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt,
const uint8_t *&msg_start,
size_t &remaining)
533 const uint8_t *msg_end = msg_start;
534 while (*msg_end != MIDIST_ENDSYSEX) msg_end++;
537 if (buffer->PackUnstructured(rt, 0, msg_end - msg_start,
const_cast<LPBYTE
>(msg_start)) == E_OUTOFMEMORY) {
539 _port->PlayBuffer(buffer);
542 buffer->PackUnstructured(rt, 0, msg_end - msg_start,
const_cast<LPBYTE
>(msg_start));
546 remaining -= msg_end - msg_start;
550static void TransmitStandardSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, MidiSysexMessage msg)
553 const uint8_t *data = MidiGetStandardSysexMessage(msg, length);
554 TransmitSysex(buffer, rt, data, length);
558static void TransmitNotesOff(IDirectMusicBuffer *buffer, REFERENCE_TIME block_time, REFERENCE_TIME cur_time)
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);
567 TransmitStandardSysex(buffer, block_time + 20, MidiSysexMessage::ResetGM);
568 TransmitStandardSysex(buffer, block_time + 30, MidiSysexMessage::RolandSetReverb);
572 _port->PlayBuffer(buffer);
579static void MidiThreadProc()
581 Debug(driver, 2,
"DMusic: Entering playback thread");
583 REFERENCE_TIME last_volume_time = 0;
584 REFERENCE_TIME block_time = 0;
593 IReferenceClock *clock;
594 _port->GetLatencyClock(&clock);
596 REFERENCE_TIME cur_time;
597 clock->GetTime(&cur_time);
602 DWORD next_timeout = 1000;
605 DWORD wfso = WaitForSingleObject(
_thread_event, next_timeout);
607 if (_playback.shutdown) {
608 _playback.playing =
false;
612 if (_playback.do_stop) {
613 Debug(driver, 2,
"DMusic thread: Stopping playback");
616 clock->GetTime(&cur_time);
619 _playback.playing =
false;
620 _playback.do_stop =
false;
626 if (wfso == WAIT_OBJECT_0) {
627 if (_playback.do_start) {
628 Debug(driver, 2,
"DMusic thread: Starting playback");
637 _playback.playing =
true;
638 _playback.do_start =
false;
642 clock->GetTime(&cur_time);
648 last_volume_time = 0;
656 if (_playback.playing) {
662 size_t preload_bytes = 0;
665 preload_bytes += block.
data.size();
668 Debug(driver, 2,
"DMusic: timer: loop from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl, block.
ticktime, ((
int)block.
realtime) / 1000.0, preload_bytes);
675 Debug(driver, 2,
"DMusic: timer: start from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl, block.
ticktime, ((
int)block.
realtime) / 1000.0, preload_bytes);
684 REFERENCE_TIME current_time;
685 clock->GetTime(¤t_time);
690 Debug(driver, 2,
"DMusic thread: volume change");
692 last_volume_time = current_time;
693 for (
int ch = 0; ch < 16; ch++) {
695 TransmitChannelMsg(
_buffer, block_time + 1, MIDIST_CONTROLLER | ch, MIDICT_CHANVOLUME, vol);
708 Debug(driver, 2,
"DMusic thread: Looping song");
712 _playback.do_stop =
true;
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) {
736 uint8_t status = data[0];
738 last_status = status;
742 status = last_status;
744 switch (status & 0xF0) {
746 case MIDIST_CHANPRESS:
748 TransmitChannelMsg(
_buffer, block_time, status, data[0]);
754 case MIDIST_POLYPRESS:
755 case MIDIST_PITCHBEND:
757 TransmitChannelMsg(
_buffer, block_time, status, data[0], data[1]);
761 case MIDIST_CONTROLLER:
763 if (data[0] == MIDICT_CHANVOLUME) {
767 TransmitChannelMsg(
_buffer, block_time, status, data[0], vol);
770 TransmitChannelMsg(
_buffer, block_time, status, data[0], data[1]);
779 TransmitSysex(
_buffer, block_time, data, remaining);
781 case MIDIST_TC_QFRAME:
786 case MIDIST_SONGPOSPTR:
801 DWORD used_buffer = 0;
802 _buffer->GetUsedBytes(&used_buffer);
803 if (used_buffer > 0) {
814 _playback.do_stop =
true;
821 Debug(driver, 2,
"DMusic: Exiting playback thread");
824 clock->GetTime(&cur_time);
826 Sleep(_playback.preload_time * 4);
831static void * DownloadArticulationData(
int base_offset,
void *data,
const std::vector<CONNECTION> &artic)
833 DMUS_ARTICULATION2 *art = (DMUS_ARTICULATION2 *)data;
834 art->ulArtIdx = base_offset + 1;
835 art->ulFirstExtCkIdx = 0;
836 art->ulNextArtIdx = 0;
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());
843 return (CONNECTION *)(con_list + 1) + artic.size();
846static const char *LoadDefaultDLSFile(
const char *user_dls)
850 caps.dwSize =
sizeof(DMUS_PORTCAPS);
851 _port->GetCaps(&caps);
854 if ((caps.dwFlags & (DMUS_PC_DLS | DMUS_PC_DLS2)) != 0 && (caps.dwFlags & DMUS_PC_GMINHARDWARE) == 0) {
857 if (user_dls ==
nullptr) {
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);
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");
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)));
877 if (!dls_file.
LoadFile(
FS2OTTD(path)))
return "Can't load GM DLS collection";
880 if (!dls_file.
LoadFile(user_dls))
return "Can't load GM DLS collection";
884 IDirectMusicPortDownload *download_port =
nullptr;
885 if (FAILED(
_port->QueryInterface(IID_IDirectMusicPortDownload, (LPVOID *)&download_port)))
return "Can't get download port";
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";
894 download_port->GetAppend(&dwAppend);
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";
906 if (FAILED(dl_wave->GetBuffer((LPVOID *)&wave, &wave_size))) {
908 download_port->Release();
909 return "Can't get wave download buffer";
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());
926 if (FAILED(download_port->Download(dl_wave))) {
927 download_port->Release();
928 return "Downloading DLS wave failed";
933 for (DWORD i = 0; i < dls_file.instruments.size(); i++) {
934 DWORD offsets = 1 + (DWORD)dls_file.instruments[i].regions.size();
937 size_t i_size =
sizeof(DMUS_DOWNLOADINFO) +
sizeof(DMUS_INSTRUMENT);
938 if (!dls_file.instruments[i].articulators.empty()) {
941 i_size +=
sizeof(DMUS_ARTICULATION2) +
sizeof(CONNECTIONLIST) +
sizeof(CONNECTION) * dls_file.instruments[i].articulators.size();
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()) {
947 i_size +=
sizeof(DMUS_ARTICULATION2) +
sizeof(CONNECTIONLIST) +
sizeof(CONNECTION) * rgn->articulators.size();
952 if (rgn->wave_sample.cbSize != 0) {
953 i_size +=
sizeof(DMUS_REGION) -
sizeof(DMUS_REGION::WLOOP) +
sizeof(WLOOP) * rgn->wave_loops.size();
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();
959 i_size += offsets *
sizeof(ULONG);
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";
970 if (FAILED(dl_inst->GetBuffer((LPVOID *)&instrument, &inst_size))) {
972 download_port->Release();
973 return "Can't get instrument download buffer";
975 char *inst_base = (
char *)instrument;
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;
986 ULONG *offset_table = (ULONG *)instrument;
987 instrument = offset_table + offsets;
991 DMUS_INSTRUMENT *inst_data = (DMUS_INSTRUMENT *)instrument;
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;
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;
1003 instrument = DownloadArticulationData(inst_data->ulGlobalArtIdx, instrument, dls_file.instruments[i].articulators);
1004 assert((
char *)instrument - inst_base <= (ptrdiff_t)inst_size);
1008 inst_data->ulFirstRegionIdx = last_offset;
1009 for (uint j = 0; j < dls_file.instruments[i].regions.size(); j++) {
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;
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;
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());
1029 instrument = (
char *)(inst_region + 1) -
sizeof(DMUS_REGION::WLOOP) +
sizeof(WLOOP) * rgn.wave_loops.size();
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());
1034 instrument = (
char *)(inst_region + 1) -
sizeof(DMUS_REGION::WLOOP) +
sizeof(WLOOP) * dls_file.waves[wave_id].wave_loops.size();
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;
1043 instrument = DownloadArticulationData(inst_region->ulRegionArtIdx, instrument, rgn.articulators);
1045 inst_region->ulRegionArtIdx = 0;
1047 assert((
char *)instrument - inst_base <= (ptrdiff_t)inst_size);
1050 inst_region->ulNextRegionIdx = j < dls_file.instruments[i].regions.size() - 1 ? last_offset : 0;
1054 if (FAILED(download_port->Download(dl_inst))) {
1055 download_port->Release();
1056 return "Downloading DLS instrument failed";
1060 download_port->Release();
1070 if (FAILED(CoInitializeEx(
nullptr, COINIT_MULTITHREADED)))
return "COM initialization failed";
1073 if (FAILED(CoCreateInstance(
1080 return "Failed to create the music object";
1084 if (FAILED(
_music->SetDirectSound(
nullptr,
nullptr)))
return "Can't set DirectSound interface";
1091 if (_debug_driver_level > 0) {
1093 char desc[DMUS_MAX_DESCRIPTION];
1097 caps.dwSize =
sizeof(DMUS_PORTCAPS);
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)" :
"");
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;
1117 if (FAILED(
_music->GetDefaultPort(&guidPort)))
return "Can't query default music port";
1121 DMUS_PORTPARAMS params;
1123 params.dwSize =
sizeof(DMUS_PORTPARAMS);
1124 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
1125 params.dwChannelGroups = 1;
1126 if (FAILED(
_music->CreatePort(guidPort, ¶ms, &
_port,
nullptr)))
return "Failed to create port";
1128 if (FAILED(
_port->Activate(TRUE)))
return "Failed to activate port";
1131 DMUS_BUFFERDESC desc;
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";
1139 const char *dls = LoadDefaultDLSFile(
GetDriverParam(parm,
"dls"));
1140 if (dls !=
nullptr)
return dls;
1143 _thread_event = CreateEvent(
nullptr, FALSE, FALSE,
nullptr);
1144 if (
_thread_event ==
nullptr)
return "Can't create thread shutdown event";
1148 return std::nullopt;
1152MusicDriver_DMusic::~MusicDriver_DMusic()
1160 if (_dmusic_thread.joinable()) {
1161 _playback.shutdown =
true;
1168 IDirectMusicPortDownload *download_port =
nullptr;
1169 _port->QueryInterface(IID_IDirectMusicPortDownload, (LPVOID *)&download_port);
1173 for (std::vector<IDirectMusicDownload *>::reverse_iterator i =
_dls_downloads.rbegin(); download_port !=
nullptr && i !=
_dls_downloads.rend(); i++) {
1174 download_port->Unload(*i);
1179 if (download_port !=
nullptr) download_port->Release();
1187 if (
_port !=
nullptr) {
1188 _port->Activate(FALSE);
1211 if (!_playback.next_file.LoadSong(song))
return;
1215 _playback.next_segment.loop = song.
loop;
1217 _playback.do_start =
true;
1224 _playback.do_stop =
true;
1231 return _playback.playing || _playback.do_start;
1237 _playback.new_volume = vol;
Factory for the DirectX music player.
static std::optional< FileHandle > Open(const std::string &filename, const std::string &mode)
Open an RAII file handle if possible.
void SetVolume(uint8_t vol) override
Set the volume, if possible.
void StopSong() override
Stop playing the current song.
std::optional< std::string_view > Start(const StringList ¶m) override
Start this driver.
bool IsSongPlaying() override
Are we currently playing a song?
void Stop() override
Stop this driver.
void PlaySong(const MusicSongInfo &song) override
Play a particular song.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
bool playing
flag indicating that playback is active
uint8_t new_volume
volume setting to change to
bool shutdown
flag to indicate playback thread shutdown
static IDirectMusicBuffer * _buffer
The buffer object collects the data to sent.
bool do_stop
flag for stopping playback at next opportunity
int preload_time
preload time for music blocks.
static const int MIDITIME_TO_REFTIME
Time base of the midi file reader is 1 us.
static std::thread _dmusic_thread
Handle to our worker thread.
MidiFile next_file
upcoming file to play
static IDirectMusicPort * _port
The port object lets us send MIDI data to the synthesizer.
bool do_start
flag for starting playback of next_file at next opportunity
static std::vector< IDirectMusicDownload * > _dls_downloads
List of downloaded DLS instruments.
static HANDLE _thread_event
Event to signal the thread that it should look at a state change.
static const int MS_TO_REFTIME
DirectMusic time base is 100 ns.
static std::mutex _thread_mutex
Lock access to playback data that is not thread-safe.
static IDirectMusic * _music
The direct music object manages buffers and ports.
PlaybackSegment next_segment
segment info for upcoming file
static void TransmitNotesOff(IDirectMusicBuffer *buffer, REFERENCE_TIME block_time, REFERENCE_TIME cur_time)
Transmit 'Note off' messages to all MIDI channels.
Base of playing music via DirectMusic.
const char * GetDriverParam(const StringList &parm, const char *name)
Get a string parameter the list of parameters.
int GetDriverParamInt(const StringList &parm, const char *name, int def)
Get an integer parameter the list of parameters.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
void MemCpyT(T *destination, const T *source, size_t num=1)
Type-safe version of memcpy().
void MemSetT(T *ptr, uint8_t value, size_t num=1)
Type-safe version of memset().
#define lengthof(array)
Return the length of an fixed size array.
std::vector< std::string > StringList
Type for a list of strings.
Instrument definition read from a DLS file.
An instrument region maps a note range to wave data.
Wave data definition from a DLS file.
bool ReadDLSInstrumentList(FileHandle &f, DWORD list_length)
Load a list of instruments from a DLS file.
bool LoadFile(const std::string &file)
Try loading a DLS file into memory.
bool ReadDLSWaveList(FileHandle &f, DWORD list_length)
Load a list of waves from a DLS file.
bool ReadDLSRegionList(FileHandle &f, DWORD list_length, DLSInstrument &instrument)
Load a list of regions from a DLS file.
bool ReadDLSWave(FileHandle &f, DWORD list_length, long offset)
Load a single wave from a DLS file.
bool ReadDLSRegion(FileHandle &f, DWORD list_length, std::vector< DLSRegion > &out)
Load a single region from a DLS file.
bool ReadDLSInstrument(FileHandle &f, DWORD list_length)
Load a single instrument from a DLS file.
bool ReadDLSArticulation(FileHandle &f, DWORD list_length, std::vector< CONNECTION > &out)
Load an articulation structure from a DLS file.
std::vector< uint8_t > data
raw midi data contained in block
uint32_t realtime
real-time (microseconds) since start of file this block should be triggered at
uint32_t ticktime
tick number since start of file this block should be triggered at
void MoveFrom(MidiFile &other)
Move data from other to this, and clears other.
std::vector< DataBlock > blocks
sequential time-annotated data of file, merged to a single track
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.
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.
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
uint8_t current_volume
current effective volume setting
MidiFile current_file
file currently being played from
PlaybackSegment current_segment
segment info for current playback
size_t current_block
next block index to send
std::mutex lock
synchronization for playback status fields
uint8_t channel_volumes[16]
last seen volume controller values in raw data
DWORD playback_start_time
timestamp current file began playback