|
|
@ -121,7 +121,7 @@ struct PlaybackSegment {
|
|
|
|
bool loop;
|
|
|
|
bool loop;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
struct DMusicPlayback {
|
|
|
|
bool shutdown; ///< flag to indicate playback thread shutdown
|
|
|
|
bool shutdown; ///< flag to indicate playback thread shutdown
|
|
|
|
bool playing; ///< flag indicating that playback is active
|
|
|
|
bool playing; ///< flag indicating that playback is active
|
|
|
|
bool do_start; ///< flag for starting playback of next_file at next opportunity
|
|
|
|
bool do_start; ///< flag for starting playback of next_file at next opportunity
|
|
|
@ -132,14 +132,29 @@ static struct {
|
|
|
|
|
|
|
|
|
|
|
|
MidiFile next_file; ///< upcoming file to play
|
|
|
|
MidiFile next_file; ///< upcoming file to play
|
|
|
|
PlaybackSegment next_segment; ///< segment info for upcoming file
|
|
|
|
PlaybackSegment next_segment; ///< segment info for upcoming file
|
|
|
|
} _playback;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Handle to our worker thread. */
|
|
|
|
/** Handle to our worker thread. */
|
|
|
|
static std::thread _dmusic_thread;
|
|
|
|
std::thread dmusic_thread;
|
|
|
|
/** Event to signal the thread that it should look at a state change. */
|
|
|
|
/** Event to signal the thread that it should look at a state change. */
|
|
|
|
static HANDLE _thread_event = nullptr;
|
|
|
|
HANDLE thread_event = nullptr;
|
|
|
|
/** Lock access to playback data that is not thread-safe. */
|
|
|
|
/** Lock access to playback data that is not thread-safe. */
|
|
|
|
static std::mutex _thread_mutex;
|
|
|
|
std::mutex thread_mutex;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void StopThread()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (this->dmusic_thread.joinable()) {
|
|
|
|
|
|
|
|
this->shutdown = true;
|
|
|
|
|
|
|
|
SetEvent(this->thread_event);
|
|
|
|
|
|
|
|
this->dmusic_thread.join();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
~DMusicPlayback()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
this->StopThread();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
static DMusicPlayback _playback;
|
|
|
|
|
|
|
|
|
|
|
|
/** The direct music object manages buffers and ports. */
|
|
|
|
/** The direct music object manages buffers and ports. */
|
|
|
|
static IDirectMusic *_music = nullptr;
|
|
|
|
static IDirectMusic *_music = nullptr;
|
|
|
@ -610,7 +625,7 @@ static void MidiThreadProc()
|
|
|
|
DWORD next_timeout = 1000;
|
|
|
|
DWORD next_timeout = 1000;
|
|
|
|
while (true) {
|
|
|
|
while (true) {
|
|
|
|
/* Wait for a signal from the GUI thread or until the time for the next event has come. */
|
|
|
|
/* 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);
|
|
|
|
DWORD wfso = WaitForSingleObject(_playback.thread_event, next_timeout);
|
|
|
|
|
|
|
|
|
|
|
|
if (_playback.shutdown) {
|
|
|
|
if (_playback.shutdown) {
|
|
|
|
_playback.playing = false;
|
|
|
|
_playback.playing = false;
|
|
|
@ -636,7 +651,7 @@ static void MidiThreadProc()
|
|
|
|
DEBUG(driver, 2, "DMusic thread: Starting playback");
|
|
|
|
DEBUG(driver, 2, "DMusic thread: Starting playback");
|
|
|
|
{
|
|
|
|
{
|
|
|
|
/* New scope to limit the time the mutex is locked. */
|
|
|
|
/* New scope to limit the time the mutex is locked. */
|
|
|
|
std::lock_guard<std::mutex> lock(_thread_mutex);
|
|
|
|
std::lock_guard<std::mutex> lock(_playback.thread_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
current_file.MoveFrom(_playback.next_file);
|
|
|
|
current_file.MoveFrom(_playback.next_file);
|
|
|
|
std::swap(_playback.next_segment, current_segment);
|
|
|
|
std::swap(_playback.next_segment, current_segment);
|
|
|
@ -1148,10 +1163,10 @@ const char *MusicDriver_DMusic::Start(const StringList &parm)
|
|
|
|
if (dls != nullptr) return dls;
|
|
|
|
if (dls != nullptr) return dls;
|
|
|
|
|
|
|
|
|
|
|
|
/* Create playback thread and synchronization primitives. */
|
|
|
|
/* Create playback thread and synchronization primitives. */
|
|
|
|
_thread_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
|
|
|
_playback.thread_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
|
|
|
if (_thread_event == nullptr) return "Can't create thread shutdown event";
|
|
|
|
if (_playback.thread_event == nullptr) return "Can't create thread shutdown event";
|
|
|
|
|
|
|
|
|
|
|
|
if (!StartNewThread(&_dmusic_thread, "ottd:dmusic", &MidiThreadProc)) return "Can't create MIDI output thread";
|
|
|
|
if (!StartNewThread(&_playback.dmusic_thread, "ottd:dmusic", &MidiThreadProc)) return "Can't create MIDI output thread";
|
|
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1165,11 +1180,7 @@ MusicDriver_DMusic::~MusicDriver_DMusic()
|
|
|
|
|
|
|
|
|
|
|
|
void MusicDriver_DMusic::Stop()
|
|
|
|
void MusicDriver_DMusic::Stop()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (_dmusic_thread.joinable()) {
|
|
|
|
_playback.StopThread();
|
|
|
|
_playback.shutdown = true;
|
|
|
|
|
|
|
|
SetEvent(_thread_event);
|
|
|
|
|
|
|
|
_dmusic_thread.join();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Unloaded any instruments we loaded. */
|
|
|
|
/* Unloaded any instruments we loaded. */
|
|
|
|
if (!_dls_downloads.empty()) {
|
|
|
|
if (!_dls_downloads.empty()) {
|
|
|
@ -1203,7 +1214,7 @@ void MusicDriver_DMusic::Stop()
|
|
|
|
_music = nullptr;
|
|
|
|
_music = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CloseHandle(_thread_event);
|
|
|
|
CloseHandle(_playback.thread_event);
|
|
|
|
|
|
|
|
|
|
|
|
CoUninitialize();
|
|
|
|
CoUninitialize();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1211,7 +1222,7 @@ void MusicDriver_DMusic::Stop()
|
|
|
|
|
|
|
|
|
|
|
|
void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song)
|
|
|
|
void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(_thread_mutex);
|
|
|
|
std::lock_guard<std::mutex> lock(_playback.thread_mutex);
|
|
|
|
|
|
|
|
|
|
|
|
if (!_playback.next_file.LoadSong(song)) return;
|
|
|
|
if (!_playback.next_file.LoadSong(song)) return;
|
|
|
|
|
|
|
|
|
|
|
@ -1220,14 +1231,14 @@ void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song)
|
|
|
|
_playback.next_segment.loop = song.loop;
|
|
|
|
_playback.next_segment.loop = song.loop;
|
|
|
|
|
|
|
|
|
|
|
|
_playback.do_start = true;
|
|
|
|
_playback.do_start = true;
|
|
|
|
SetEvent(_thread_event);
|
|
|
|
SetEvent(_playback.thread_event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MusicDriver_DMusic::StopSong()
|
|
|
|
void MusicDriver_DMusic::StopSong()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_playback.do_stop = true;
|
|
|
|
_playback.do_stop = true;
|
|
|
|
SetEvent(_thread_event);
|
|
|
|
SetEvent(_playback.thread_event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|