File diff r25654:e264fd698eb2 → r25655:1030dcb7eb52
src/music/dmusic.cpp
Show inline comments
 
@@ -220,52 +220,52 @@ bool DLSFile::ReadDLSRegion(FILE *f, DWO
 
				break;
 

	
 
			case FOURCC_LART: // List chunk
 
				if (!this->ReadDLSArticulation(f, chunk.length, region.articulators)) return false;
 
				break;
 

	
 
			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;
 
}
 

	
 
bool DLSFile::ReadDLSRegionList(FILE *f, DWORD list_length, DLSInstrument &instrument)
 
{
 
	while (list_length > 0) {
 
		ChunkHeader chunk;
 
		if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
 
		list_length -= chunk.length + sizeof(chunk);
 

	
 
		if (chunk.type == FOURCC_LIST) {
 
			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;
 
}
 

	
 
bool DLSFile::ReadDLSInstrument(FILE *f, DWORD list_length)
 
{
 
	this->instruments.push_back(DLSInstrument());
 
	DLSInstrument &instrument = this->instruments.back();
 

	
 
@@ -290,54 +290,54 @@ bool DLSFile::ReadDLSInstrument(FILE *f,
 
				break;
 

	
 
			case FOURCC_LRGN: // List chunk
 
				if (!this->ReadDLSRegionList(f, chunk.length, instrument)) return false;
 
				break;
 

	
 
			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;
 
}
 

	
 
bool DLSFile::ReadDLSInstrumentList(FILE *f, DWORD list_length)
 
{
 
	while (list_length > 0) {
 
		ChunkHeader chunk;
 
		if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
 
		list_length -= chunk.length + sizeof(chunk);
 

	
 
		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;
 
}
 

	
 
bool DLSFile::ReadDLSWave(FILE *f, DWORD list_length, long offset)
 
{
 
	this->waves.push_back(DLSWave());
 
	DLSWave &wave = this->waves.back();
 

	
 
@@ -378,84 +378,84 @@ bool DLSFile::ReadDLSWave(FILE *f, DWORD
 

	
 
			case FOURCC_data:
 
				wave.data.resize(chunk.length);
 
				if (fread(&wave.data[0], sizeof(BYTE), wave.data.size(), f) != wave.data.size()) return false;
 
				break;
 

	
 
			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;
 
}
 

	
 
bool DLSFile::ReadDLSWaveList(FILE *f, DWORD list_length)
 
{
 
	long base_offset = ftell(f);
 

	
 
	while (list_length > 0) {
 
		long chunk_offset = ftell(f);
 

	
 
		ChunkHeader chunk;
 
		if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
 
		list_length -= chunk.length + sizeof(chunk);
 

	
 
		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);
 

	
 
	/* Check DLS file header. */
 
	ChunkHeader hdr;
 
	FOURCC dls_type;
 
	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) {
 
		ChunkHeader chunk;
 
		if (fread(&chunk, sizeof(chunk), 1, f) != 1) return false;
 
		hdr.length -= chunk.length + sizeof(chunk);
 

	
 
		if (chunk.type == FOURCC_LIST) {
 
			/* Unwrap list header. */
 
@@ -486,25 +486,25 @@ bool DLSFile::LoadFile(const wchar_t *fi
 
					POOLCUE cue;
 
					if (fread(&cue, sizeof(cue), 1, f) != 1) return false;
 
					this->pool_cues.push_back(cue);
 
				}
 
				break;
 

	
 
			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? */
 
	if (header.cInstruments != this->instruments.size()) return false;
 

	
 
	/* Resolve wave pool table. */
 
	for (std::vector<POOLCUE>::iterator cue = this->pool_cues.begin(); cue != this->pool_cues.end(); cue++) {
 
		std::vector<DLSWave>::iterator w = std::find(this->waves.begin(), this->waves.end(), cue->ulOffset);
 
		if (w != this->waves.end()) {
 
@@ -576,25 +576,25 @@ static void TransmitNotesOff(IDirectMusi
 

	
 
	/* Explicitly flush buffer to make sure the messages are processed,
 
	 * as we want sound to stop immediately. */
 
	_port->PlayBuffer(_buffer);
 
	_buffer->Flush();
 

	
 
	/* 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
 
	size_t current_block = 0;            // next block index to send
 
	byte current_volume = 0;             // current effective volume setting
 
	byte channel_volumes[16];            // last seen volume controller values in raw data
 

	
 
	/* Get pointer to the reference clock of our output port. */
 
	IReferenceClock *clock;
 
@@ -608,40 +608,40 @@ static void MidiThreadProc()
 

	
 
	DWORD next_timeout = 1000;
 
	while (true) {
 
		/* Wait for a signal from the GUI thread or until the time for the next event has come. */
 
		DWORD wfso = WaitForSingleObject(_thread_event, next_timeout);
 

	
 
		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;
 
			_playback.do_stop = false;
 
			block_time = 0;
 
			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);
 
					current_segment.start_block = 0;
 
					current_block = 0;
 
					_playback.playing = true;
 
					_playback.do_start = false;
 
				}
 

	
 
@@ -660,85 +660,85 @@ static void MidiThreadProc()
 
		if (_playback.playing) {
 
			/* skip beginning of file? */
 
			if (current_segment.start > 0 && current_block == 0 && current_segment.start_block == 0) {
 
				/* find first block after start time and pretend playback started earlier
 
				 * this is to allow all blocks prior to the actual start to still affect playback,
 
				 * as they may contain important controller and program changes */
 
				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;
 
						}
 
					}
 
				}
 
			}
 

	
 
			/* Get current playback timestamp. */
 
			REFERENCE_TIME current_time;
 
			clock->GetTime(&current_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);
 
					}
 
					_port->PlayBuffer(_buffer);
 
					_buffer->Flush();
 
				}
 
			}
 

	
 
			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;
 
					break;
 
				}
 
				/* 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,
 
					 * but handle it anyway just to be safe */
 
					byte status = data[0];
 
					if (status & 0x80) {
 
						last_status = status;
 
						data++;
 
						remaining--;
 
@@ -813,25 +813,25 @@ static void MidiThreadProc()
 
			if (current_block == current_file.blocks.size()) {
 
				if (current_segment.loop) {
 
					current_block = current_segment.start_block;
 
					playback_start_time = block_time - current_file.blocks[current_block].realtime * MIDITIME_TO_REFTIME;
 
				} else {
 
					_playback.do_stop = true;
 
				}
 
				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);
 

	
 
	clock->Release();
 
}
 

	
 
static void * DownloadArticulationData(int base_offset, void *data, const std::vector<CONNECTION> &artic)
 
{
 
	DMUS_ARTICULATION2 *art = (DMUS_ARTICULATION2 *)data;
 
@@ -858,25 +858,25 @@ static const char *LoadDefaultDLSFile(co
 
	if ((caps.dwFlags & (DMUS_PC_DLS | DMUS_PC_DLS2)) != 0 && (caps.dwFlags & DMUS_PC_GMINHARDWARE) == 0) {
 
		DLSFile dls_file;
 

	
 
		if (user_dls == nullptr) {
 
			/* Try loading the default GM DLS file stored in the registry. */
 
			HKEY hkDM;
 
			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) {
 
				static const wchar_t *DLS_GM_FILE = L"%windir%\\System32\\drivers\\gm.dls";
 
				wchar_t path[MAX_PATH];
 
				ExpandEnvironmentStrings(DLS_GM_FILE, path, lengthof(path));
 

	
 
				if (!dls_file.LoadFile(path)) return "Can't load GM DLS collection";
 
			}
 
@@ -1091,28 +1091,28 @@ const char *MusicDriver_DMusic::Start(co
 
	 * has come. By default, we try send any events at least 50 ms before playback. */
 
	_playback.preload_time = GetDriverParamInt(parm, "preload", 50);
 

	
 
	int pIdx = GetDriverParamInt(parm, "port", -1);
 
	if (_debug_driver_level > 0) {
 
		/* Print all valid output ports. */
 
		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) {
 
		/* Check if the passed port is a valid port. */
 
		DMUS_PORTCAPS caps;
 
		MemSetT(&caps, 0);
 
		caps.dwSize = sizeof(DMUS_PORTCAPS);
 
		if (FAILED(_music->EnumPort(pIdx, &caps))) return "Supplied port parameter is not a valid port";
 
		if (caps.dwClass != DMUS_PC_OUTPUTCLASS) return "Supplied port parameter is not an output port";