diff --git a/bin/baseset/orig_win.obm b/bin/baseset/orig_win.obm index 8e2053e043..c8c4923e0e 100644 --- a/bin/baseset/orig_win.obm +++ b/bin/baseset/orig_win.obm @@ -142,5 +142,17 @@ GM_TT19.GM = Funk Central GM_TT20.GM = Jammit GM_TT21.GM = Movin' On +; MIDI timecodes where the playback should attemp to start and stop short. +; This is to allow fixing undesired silences in original MIDI files. +; However not all music drivers may support this. +[timingtrim] +; Theme has two beats silence at the beginning which prevents clean looping. +GM_TT00.GM = 768:53760 +; Can't Get There From Here from the Windows version has a long silence at the end, +; followed by a solo repeat. This isn't in the original DOS version music and is likely +; unintentional from the people who converted the music from the DOS version. +; Actual song ends after measure 152. +GM_TT10.GM = 0:235008 + [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/media/baseset/orig_win.obm b/media/baseset/orig_win.obm index 8b35b6de9d..b5d4b024ed 100644 --- a/media/baseset/orig_win.obm +++ b/media/baseset/orig_win.obm @@ -90,5 +90,17 @@ GM_TT19.GM = Funk Central GM_TT20.GM = Jammit GM_TT21.GM = Movin' On +; MIDI timecodes where the playback should attemp to start and stop short. +; This is to allow fixing undesired silences in original MIDI files. +; However not all music drivers may support this. +[timingtrim] +; Theme has two beats silence at the beginning which prevents clean looping. +GM_TT00.GM = 768:53760 +; Can't Get There From Here from the Windows version has a long silence at the end, +; followed by a solo repeat. This isn't in the original DOS version music and is likely +; unintentional from the people who converted the music from the DOS version. +; Actual song ends after measure 152. +GM_TT10.GM = 0:235008 + [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/src/base_media_base.h b/src/base_media_base.h index 407dcda86a..2974db5cb4 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -301,6 +301,8 @@ struct MusicSongInfo { const char *filename; ///< file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object for the file) MusicTrackType filetype; ///< decoder required for song file int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI + int override_start; ///< MIDI ticks to skip over in beginning + int override_end; ///< MIDI tick to end the song at (0 if no override) }; /** All data of a music set. */ diff --git a/src/music.cpp b/src/music.cpp index e131c02103..65e35f955d 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -125,6 +125,7 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f this->num_available = 0; IniGroup *names = ini->GetGroup("names"); IniGroup *catindex = ini->GetGroup("catindex"); + IniGroup *timingtrim = ini->GetGroup("timingtrim"); for (uint i = 0, j = 1; i < lengthof(this->songinfo); i++) { const char *filename = this->files[i].filename; if (names == NULL || StrEmpty(filename)) { @@ -150,15 +151,16 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f this->songinfo[i].filetype = MTT_STANDARDMIDI; } + const char *trimmed_filename = filename; /* As we possibly add a path to the filename and we compare * on the filename with the path as in the .obm, we need to * keep stripping path elements until we find a match. */ - for (const char *p = filename; p != NULL; p = strchr(p, PATHSEPCHAR)) { + for (; trimmed_filename != NULL; trimmed_filename = strchr(trimmed_filename, PATHSEPCHAR)) { /* Remove possible double path separator characters from * the beginning, so we don't start reading e.g. root. */ - while (*p == PATHSEPCHAR) p++; + while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++; - item = names->GetItem(p, false); + item = names->GetItem(trimmed_filename, false); if (item != NULL && !StrEmpty(item->value)) break; } @@ -173,6 +175,15 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f this->num_available++; this->songinfo[i].tracknr = j++; + + item = timingtrim->GetItem(trimmed_filename, false); + if (item != NULL && !StrEmpty(item->value)) { + const char *endpos = strchr(item->value, ':'); + if (endpos != NULL) { + this->songinfo[i].override_start = atoi(item->value); + this->songinfo[i].override_end = atoi(endpos + 1); + } + } } } return ret; diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index ce76d22ef4..3b6ae34549 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -723,14 +723,6 @@ static void MidiThreadProc(void *) while (current_block < current_file.blocks.size()) { MidiFile::DataBlock &block = current_file.blocks[current_block]; - /* 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 %u ms (music %u, ref %lld)", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time); - break; - } /* check that block isn't at end-of-song override */ if (current_segment.end > 0 && block.ticktime >= current_segment.end) { if (current_segment.loop) { @@ -743,6 +735,14 @@ static void MidiThreadProc(void *) 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 %u ms (music %u, ref %lld)", 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; @@ -1232,8 +1232,8 @@ void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song) if (!_playback.next_file.LoadSong(song)) return; - _playback.next_segment.start = 0; - _playback.next_segment.end = 0; + _playback.next_segment.start = song.override_start; + _playback.next_segment.end = song.override_end; _playback.next_segment.loop = false; _playback.do_start = true; diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index 51528133b6..93991d88bb 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -209,10 +209,6 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW while (_midi.current_block < _midi.current_file.blocks.size()) { MidiFile::DataBlock &block = _midi.current_file.blocks[_midi.current_block]; - /* check that block is not in the future */ - if (block.realtime / 1000 > playback_time) { - break; - } /* check that block isn't at end-of-song override */ if (_midi.current_segment.end > 0 && block.ticktime >= _midi.current_segment.end) { if (_midi.current_segment.loop) { @@ -223,6 +219,10 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW } break; } + /* check that block is not in the future */ + if (block.realtime / 1000 > playback_time) { + break; + } byte *data = block.data.Begin(); size_t remaining = block.data.Length(); @@ -315,8 +315,8 @@ void MusicDriver_Win32::PlaySong(const MusicSongInfo &song) return; } - _midi.next_segment.start = 0; - _midi.next_segment.end = 0; + _midi.next_segment.start = song.override_start; + _midi.next_segment.end = song.override_end; _midi.next_segment.loop = false; DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag");