|
@@ -226,13 +226,13 @@ bool DLSFile::ReadDLSRegion(FILE *f, DWO
|
|
|
case FOURCC_INFO:
|
|
|
/* We don't care about info stuff. */
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
DEBUG(driver, 7, "DLS: Ignoring unknown chunk %c%c%c%c", (char)(chunk.type & 0xFF), (char)((chunk.type >> 8) & 0xFF), (char)((chunk.type >> 16) & 0xFF), (char)((chunk.type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
@@ -249,17 +249,17 @@ bool DLSFile::ReadDLSRegionList(FILE *f,
|
|
|
FOURCC list_type;
|
|
|
if (fread(&list_type, sizeof(list_type), 1, f) != 1) return false;
|
|
|
|
|
|
if (list_type == FOURCC_RGN) {
|
|
|
this->ReadDLSRegion(f, chunk.length - sizeof(list_type), instrument.regions);
|
|
|
} else {
|
|
|
DEBUG(driver, 7, "DLS: Ignoring unknown list chunk of type %c%c%c%c", (char)(list_type & 0xFF), (char)((list_type >> 8) & 0xFF), (char)((list_type >> 16) & 0xFF), (char)((list_type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length - sizeof(list_type), SEEK_CUR);
|
|
|
}
|
|
|
} else {
|
|
|
DEBUG(driver, 7, "DLS: Ignoring chunk %c%c%c%c", (char)(chunk.type & 0xFF), (char)((chunk.type >> 8) & 0xFF), (char)((chunk.type >> 16) & 0xFF), (char)((chunk.type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -296,13 +296,13 @@ bool DLSFile::ReadDLSInstrument(FILE *f,
|
|
|
case FOURCC_INFO:
|
|
|
/* We don't care about info stuff. */
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
DEBUG(driver, 7, "DLS: Ignoring unknown chunk %c%c%c%c", (char)(chunk.type & 0xFF), (char)((chunk.type >> 8) & 0xFF), (char)((chunk.type >> 16) & 0xFF), (char)((chunk.type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
@@ -317,21 +317,21 @@ bool DLSFile::ReadDLSInstrumentList(FILE
|
|
|
|
|
|
if (chunk.type == FOURCC_LIST) {
|
|
|
FOURCC list_type;
|
|
|
if (fread(&list_type, sizeof(list_type), 1, f) != 1) return false;
|
|
|
|
|
|
if (list_type == FOURCC_INS) {
|
|
|
DEBUG(driver, 6, "DLS: Reading instrument %d", (int)instruments.size());
|
|
|
Debug(driver, 6, "DLS: Reading instrument {}", (int)instruments.size());
|
|
|
|
|
|
if (!this->ReadDLSInstrument(f, chunk.length - sizeof(list_type))) return false;
|
|
|
} else {
|
|
|
DEBUG(driver, 7, "DLS: Ignoring unknown list chunk of type %c%c%c%c", (char)(list_type & 0xFF), (char)((list_type >> 8) & 0xFF), (char)((list_type >> 16) & 0xFF), (char)((list_type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length - sizeof(list_type), SEEK_CUR);
|
|
|
}
|
|
|
} else {
|
|
|
DEBUG(driver, 7, "DLS: Ignoring chunk %c%c%c%c", (char)(chunk.type & 0xFF), (char)((chunk.type >> 8) & 0xFF), (char)((chunk.type >> 16) & 0xFF), (char)((chunk.type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -384,13 +384,13 @@ bool DLSFile::ReadDLSWave(FILE *f, DWORD
|
|
|
case FOURCC_INFO:
|
|
|
/* We don't care about info stuff. */
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
DEBUG(driver, 7, "DLS: Ignoring unknown chunk %c%c%c%c", (char)(chunk.type & 0xFF), (char)((chunk.type >> 8) & 0xFF), (char)((chunk.type >> 16) & 0xFF), (char)((chunk.type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
@@ -409,31 +409,31 @@ bool DLSFile::ReadDLSWaveList(FILE *f, D
|
|
|
|
|
|
if (chunk.type == FOURCC_LIST) {
|
|
|
FOURCC list_type;
|
|
|
if (fread(&list_type, sizeof(list_type), 1, f) != 1) return false;
|
|
|
|
|
|
if (list_type == FOURCC_wave) {
|
|
|
DEBUG(driver, 6, "DLS: Reading wave %d", (int)waves.size());
|
|
|
Debug(driver, 6, "DLS: Reading wave {}", waves.size());
|
|
|
|
|
|
if (!this->ReadDLSWave(f, chunk.length - sizeof(list_type), chunk_offset - base_offset)) return false;
|
|
|
} else {
|
|
|
DEBUG(driver, 7, "DLS: Ignoring unknown list chunk of type %c%c%c%c", (char)(list_type & 0xFF), (char)((list_type >> 8) & 0xFF), (char)((list_type >> 16) & 0xFF), (char)((list_type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length - sizeof(list_type), SEEK_CUR);
|
|
|
}
|
|
|
} else {
|
|
|
DEBUG(driver, 7, "DLS: Ignoring chunk %c%c%c%c", (char)(chunk.type & 0xFF), (char)((chunk.type >> 8) & 0xFF), (char)((chunk.type >> 16) & 0xFF), (char)((chunk.type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool DLSFile::LoadFile(const wchar_t *file)
|
|
|
{
|
|
|
DEBUG(driver, 2, "DMusic: Try to load DLS file %s", FS2OTTD(file).c_str());
|
|
|
Debug(driver, 2, "DMusic: Try to load DLS file {}", FS2OTTD(file));
|
|
|
|
|
|
FILE *f = _wfopen(file, L"rb");
|
|
|
if (f == nullptr) return false;
|
|
|
|
|
|
FileCloser f_scope(f);
|
|
|
|
|
@@ -443,13 +443,13 @@ bool DLSFile::LoadFile(const wchar_t *fi
|
|
|
if (fread(&hdr, sizeof(hdr), 1, f) != 1) return false;
|
|
|
if (fread(&dls_type, sizeof(dls_type), 1, f) != 1) return false;
|
|
|
if (hdr.type != FOURCC_RIFF || dls_type != FOURCC_DLS) return false;
|
|
|
|
|
|
hdr.length -= sizeof(FOURCC);
|
|
|
|
|
|
DEBUG(driver, 2, "DMusic: Parsing DLS file");
|
|
|
Debug(driver, 2, "DMusic: Parsing DLS file");
|
|
|
|
|
|
DLSHEADER header;
|
|
|
MemSetT(&header, 0);
|
|
|
|
|
|
/* Iterate over all chunks in the file. */
|
|
|
while (hdr.length > 0) {
|
|
@@ -492,13 +492,13 @@ bool DLSFile::LoadFile(const wchar_t *fi
|
|
|
case FOURCC_INFO:
|
|
|
/* We don't care about info stuff. */
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
DEBUG(driver, 7, "DLS: Ignoring unknown chunk %c%c%c%c", (char)(chunk.type & 0xFF), (char)((chunk.type >> 8) & 0xFF), (char)((chunk.type >> 16) & 0xFF), (char)((chunk.type >> 24) & 0xFF));
|
|
|
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));
|
|
|
fseek(f, chunk.length, SEEK_CUR);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Have we read as many instruments as indicated? */
|
|
@@ -582,13 +582,13 @@ static void TransmitNotesOff(IDirectMusi
|
|
|
/* Wait until message time has passed. */
|
|
|
Sleep(Clamp((block_time - cur_time) / MS_TO_REFTIME, 5, 1000));
|
|
|
}
|
|
|
|
|
|
static void MidiThreadProc()
|
|
|
{
|
|
|
DEBUG(driver, 2, "DMusic: Entering playback thread");
|
|
|
Debug(driver, 2, "DMusic: Entering playback thread");
|
|
|
|
|
|
REFERENCE_TIME last_volume_time = 0; // timestamp of the last volume change
|
|
|
REFERENCE_TIME block_time = 0; // timestamp of the last block sent to the port
|
|
|
REFERENCE_TIME playback_start_time; // timestamp current file began playback
|
|
|
MidiFile current_file; // file currently being played from
|
|
|
PlaybackSegment current_segment; // segment info for current playback
|
|
@@ -614,13 +614,13 @@ static void MidiThreadProc()
|
|
|
if (_playback.shutdown) {
|
|
|
_playback.playing = false;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (_playback.do_stop) {
|
|
|
DEBUG(driver, 2, "DMusic thread: Stopping playback");
|
|
|
Debug(driver, 2, "DMusic thread: Stopping playback");
|
|
|
|
|
|
/* Turn all notes off and wait a bit to allow the messages to be handled. */
|
|
|
clock->GetTime(&cur_time);
|
|
|
TransmitNotesOff(_buffer, block_time, cur_time);
|
|
|
|
|
|
_playback.playing = false;
|
|
@@ -629,13 +629,13 @@ static void MidiThreadProc()
|
|
|
next_timeout = 1000;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if (wfso == WAIT_OBJECT_0) {
|
|
|
if (_playback.do_start) {
|
|
|
DEBUG(driver, 2, "DMusic thread: Starting playback");
|
|
|
Debug(driver, 2, "DMusic thread: Starting playback");
|
|
|
{
|
|
|
/* New scope to limit the time the mutex is locked. */
|
|
|
std::lock_guard<std::mutex> lock(_thread_mutex);
|
|
|
|
|
|
current_file.MoveFrom(_playback.next_file);
|
|
|
std::swap(_playback.next_segment, current_segment);
|
|
@@ -666,20 +666,20 @@ static void MidiThreadProc()
|
|
|
size_t preload_bytes = 0;
|
|
|
for (size_t bl = 0; bl < current_file.blocks.size(); bl++) {
|
|
|
MidiFile::DataBlock &block = current_file.blocks[bl];
|
|
|
preload_bytes += block.data.size();
|
|
|
if (block.ticktime >= current_segment.start) {
|
|
|
if (current_segment.loop) {
|
|
|
DEBUG(driver, 2, "DMusic: timer: loop from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes);
|
|
|
Debug(driver, 2, "DMusic: timer: loop from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl, block.ticktime, ((int)block.realtime) / 1000.0, preload_bytes);
|
|
|
current_segment.start_block = bl;
|
|
|
break;
|
|
|
} else {
|
|
|
/* Skip the transmission delay compensation performed in the Win32 MIDI driver.
|
|
|
* The DMusic driver will most likely be used with the MS softsynth, which is not subject to transmission delays.
|
|
|
*/
|
|
|
DEBUG(driver, 2, "DMusic: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes);
|
|
|
Debug(driver, 2, "DMusic: timer: start from block {} (ticktime {}, realtime {:.3f}, bytes {})", bl, block.ticktime, ((int)block.realtime) / 1000.0, preload_bytes);
|
|
|
playback_start_time -= block.realtime * MIDITIME_TO_REFTIME;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -688,13 +688,13 @@ static void MidiThreadProc()
|
|
|
REFERENCE_TIME current_time;
|
|
|
clock->GetTime(¤t_time);
|
|
|
|
|
|
/* Check for volume change. */
|
|
|
if (current_volume != _playback.new_volume) {
|
|
|
if (current_time - last_volume_time > 10 * MS_TO_REFTIME) {
|
|
|
DEBUG(driver, 2, "DMusic thread: volume change");
|
|
|
Debug(driver, 2, "DMusic thread: volume change");
|
|
|
current_volume = _playback.new_volume;
|
|
|
last_volume_time = current_time;
|
|
|
for (int ch = 0; ch < 16; ch++) {
|
|
|
int vol = ScaleVolume(channel_volumes[ch], current_volume);
|
|
|
TransmitChannelMsg(_buffer, block_time + 1, MIDIST_CONTROLLER | ch, MIDICT_CHANVOLUME, vol);
|
|
|
}
|
|
@@ -706,13 +706,13 @@ static void MidiThreadProc()
|
|
|
while (current_block < current_file.blocks.size()) {
|
|
|
MidiFile::DataBlock &block = current_file.blocks[current_block];
|
|
|
|
|
|
/* check that block isn't at end-of-song override */
|
|
|
if (current_segment.end > 0 && block.ticktime >= current_segment.end) {
|
|
|
if (current_segment.loop) {
|
|
|
DEBUG(driver, 2, "DMusic thread: Looping song");
|
|
|
Debug(driver, 2, "DMusic thread: Looping song");
|
|
|
current_block = current_segment.start_block;
|
|
|
playback_start_time = current_time - current_file.blocks[current_block].realtime * MIDITIME_TO_REFTIME;
|
|
|
} else {
|
|
|
_playback.do_stop = true;
|
|
|
}
|
|
|
next_timeout = 0;
|
|
@@ -720,19 +720,19 @@ static void MidiThreadProc()
|
|
|
}
|
|
|
/* check that block is not in the future */
|
|
|
REFERENCE_TIME playback_time = current_time - playback_start_time;
|
|
|
if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) {
|
|
|
/* Stop the thread loop until we are at the preload time of the next block. */
|
|
|
next_timeout = Clamp(((int64)block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000);
|
|
|
DEBUG(driver, 9, "DMusic thread: Next event in %lu ms (music %u, ref " OTTD_PRINTF64 ")", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time);
|
|
|
Debug(driver, 9, "DMusic thread: Next event in {} ms (music {}, ref {})", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
/* Timestamp of the current block. */
|
|
|
block_time = playback_start_time + block.realtime * MIDITIME_TO_REFTIME;
|
|
|
DEBUG(driver, 9, "DMusic thread: Streaming block " PRINTF_SIZE " (cur=" OTTD_PRINTF64 ", block=" OTTD_PRINTF64 ")", current_block, (long long)(current_time / MS_TO_REFTIME), (long long)(block_time / MS_TO_REFTIME));
|
|
|
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));
|
|
|
|
|
|
const byte *data = block.data.data();
|
|
|
size_t remaining = block.data.size();
|
|
|
byte last_status = 0;
|
|
|
while (remaining > 0) {
|
|
|
/* MidiFile ought to have converted everything out of running status,
|
|
@@ -819,13 +819,13 @@ static void MidiThreadProc()
|
|
|
}
|
|
|
next_timeout = 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
DEBUG(driver, 2, "DMusic: Exiting playback thread");
|
|
|
Debug(driver, 2, "DMusic: Exiting playback thread");
|
|
|
|
|
|
/* Turn all notes off and wait a bit to allow the messages to be handled by real hardware. */
|
|
|
clock->GetTime(&cur_time);
|
|
|
TransmitNotesOff(_buffer, block_time, cur_time);
|
|
|
Sleep(_playback.preload_time * 4);
|
|
|
|
|
@@ -864,13 +864,13 @@ static const char *LoadDefaultDLSFile(co
|
|
|
if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic", 0, KEY_READ, &hkDM))) {
|
|
|
wchar_t dls_path[MAX_PATH];
|
|
|
DWORD buf_size = sizeof(dls_path); // Buffer size as to be given in bytes!
|
|
|
if (SUCCEEDED(RegQueryValueEx(hkDM, L"GMFilePath", nullptr, nullptr, (LPBYTE)dls_path, &buf_size))) {
|
|
|
wchar_t expand_path[MAX_PATH * 2];
|
|
|
ExpandEnvironmentStrings(dls_path, expand_path, lengthof(expand_path));
|
|
|
if (!dls_file.LoadFile(expand_path)) DEBUG(driver, 1, "Failed to load default GM DLS file from registry");
|
|
|
if (!dls_file.LoadFile(expand_path)) Debug(driver, 1, "Failed to load default GM DLS file from registry");
|
|
|
}
|
|
|
RegCloseKey(hkDM);
|
|
|
}
|
|
|
|
|
|
/* If we couldn't load the file from the registry, try again at the default install path of the GM DLS file. */
|
|
|
if (dls_file.instruments.size() == 0) {
|
|
@@ -1097,16 +1097,16 @@ const char *MusicDriver_DMusic::Start(co
|
|
|
char desc[DMUS_MAX_DESCRIPTION];
|
|
|
|
|
|
DMUS_PORTCAPS caps;
|
|
|
MemSetT(&caps, 0);
|
|
|
caps.dwSize = sizeof(DMUS_PORTCAPS);
|
|
|
|
|
|
DEBUG(driver, 1, "Detected DirectMusic ports:");
|
|
|
Debug(driver, 1, "Detected DirectMusic ports:");
|
|
|
for (int i = 0; _music->EnumPort(i, &caps) == S_OK; i++) {
|
|
|
if (caps.dwClass == DMUS_PC_OUTPUTCLASS) {
|
|
|
DEBUG(driver, 1, " %d: %s%s", i, convert_from_fs(caps.wszDescription, desc, lengthof(desc)), i == pIdx ? " (selected)" : "");
|
|
|
Debug(driver, 1, " {}: {}{}", i, convert_from_fs(caps.wszDescription, desc, lengthof(desc)), i == pIdx ? " (selected)" : "");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
GUID guidPort;
|
|
|
if (pIdx >= 0) {
|