Skip to content

Commit

Permalink
Fix MIDI pause/stop functionality (fluidsynth player only)
Browse files Browse the repository at this point in the history
Now stop will actually reset to the start, and pause will properly resume on pressing play again.

Looks like fluidsynth can seek now, however it goes by MIDI ticks which might be tricky as we go by milliseconds. If a MIDI has tempo changes we can't really sync the seek bar and the song properly. Might look into it later because it'd be nice to be able to seek when playing MIDIs.
  • Loading branch information
sirjuddington committed Dec 21, 2024
1 parent 07079f4 commit a116a40
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 34 deletions.
52 changes: 25 additions & 27 deletions src/Audio/MIDIPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class FluidSynthMIDIPlayer : public MIDIPlayer
// -------------------------------------------------------------------------
// FluidSynthMIDIPlayer class destructor
// -------------------------------------------------------------------------
virtual ~FluidSynthMIDIPlayer()
~FluidSynthMIDIPlayer() override
{
FluidSynthMIDIPlayer::stop();
delete_fluid_audio_driver(fs_adriver_);
Expand Down Expand Up @@ -152,7 +152,7 @@ class FluidSynthMIDIPlayer : public MIDIPlayer
bool retval = false;
for (int a = paths.size() - 1; a >= 0; --a)
{
auto path = paths[a];
const auto& path = paths[a];
if (!path.empty())
{
int fs_id = fluid_synth_sfload(fs_synth_, path.c_str(), 1);
Expand Down Expand Up @@ -184,6 +184,7 @@ class FluidSynthMIDIPlayer : public MIDIPlayer
if (fs_player_)
{
fluid_player_add(fs_player_, filename.c_str());
elapsed_ms_ = 0;
return true;
}

Expand All @@ -210,8 +211,9 @@ class FluidSynthMIDIPlayer : public MIDIPlayer

if (fs_player_)
{
// fluid_player_set_loop(fs_player, -1);
// fluid_player_set_loop(fs_player_, -1);
fluid_player_add_mem(fs_player_, mc.data(), mc.size());
elapsed_ms_ = 0;
return true;
}

Expand All @@ -229,41 +231,39 @@ class FluidSynthMIDIPlayer : public MIDIPlayer
// -------------------------------------------------------------------------
bool play() override
{
stop();
timer_.restart();

if (!fs_initialised_)
if (!fs_initialised_ || isPlaying())
return false;

return (fluid_player_play(fs_player_) == FLUID_OK);
timer_.restart();

return fluid_player_play(fs_player_) == FLUID_OK;
}

// -------------------------------------------------------------------------
// Pauses playback of the currently loaded MIDI stream
// -------------------------------------------------------------------------
bool pause() override
{
if (!isReady())
if (!isPlaying())
return false;

return stop();
elapsed_ms_ = timer_.getElapsedTime().asMilliseconds();

return fluid_player_stop(fs_player_) == FLUID_OK;
}

// -------------------------------------------------------------------------
// Stops playback of the currently loaded MIDI stream
// -------------------------------------------------------------------------
bool stop() override
{
bool stopped = false;

if (fs_initialised_)
{
if (isPlaying())
fluid_player_stop(fs_player_);
// fluid_synth_system_reset(fs_synth_); // Breaks soundfont on play/pause/stop
stopped = true;
}

return stopped;
fluid_player_seek(fs_player_, 0);
elapsed_ms_ = 0;

return true;
}

// -------------------------------------------------------------------------
Expand All @@ -283,15 +283,16 @@ class FluidSynthMIDIPlayer : public MIDIPlayer
int position() override
{
// We cannot query this information from fluidsynth, so we cheat by querying our own timer
return timer_.getElapsedTime().asMilliseconds();
return elapsed_ms_ + timer_.getElapsedTime().asMilliseconds();
}

// -------------------------------------------------------------------------
// Seeks to [pos] in the currently loaded MIDI stream
// -------------------------------------------------------------------------
bool setPosition(int pos) override
{
// Cannot currently seek in fluidsynth
// While we can seek in fluidsynth, it's only by ticks which makes it
// difficult to work with in case of tempo changes etc.
return false;
}

Expand All @@ -303,11 +304,7 @@ class FluidSynthMIDIPlayer : public MIDIPlayer
if (!isReady())
return false;

// Clamp volume
if (volume > 100)
volume = 100;
if (volume < 0)
volume = 0;
volume = std::clamp(volume, 0, 100);

fluid_synth_set_gain(fs_synth_, volume * 0.01f);

Expand All @@ -322,6 +319,7 @@ class FluidSynthMIDIPlayer : public MIDIPlayer

bool fs_initialised_ = false;
vector<int> fs_soundfont_ids_;
int elapsed_ms_ = 0; // Time elapsed before last pause

// -------------------------------------------------------------------------
// Initialises fluidsynth
Expand Down Expand Up @@ -376,7 +374,7 @@ class TimidityMIDIPlayer : public MIDIPlayer
// -------------------------------------------------------------------------
// TimidityMIDIPlayer class destructor
// -------------------------------------------------------------------------
virtual ~TimidityMIDIPlayer() { stop(); }
~TimidityMIDIPlayer() override { stop(); }

// -------------------------------------------------------------------------
// Returns true if the MIDIPlayer has a soundfont loaded
Expand Down Expand Up @@ -427,7 +425,7 @@ class TimidityMIDIPlayer : public MIDIPlayer
wxExecuteEnv env;
env.cwd = string{ strutil::Path::pathOf(snd_timidity_path) };
auto commandline = fmt::format(
"\"{}\" \"{}\" {}", string(snd_timidity_path), file_, string(snd_timidity_options));
R"("{}" "{}" {})", string(snd_timidity_path), file_, string(snd_timidity_options));

// Execute program
pid_ = wxExecute(commandline, wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE, nullptr, &env);
Expand Down
14 changes: 7 additions & 7 deletions src/MainEditor/UI/EntryPanel/AudioEntryPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ CVAR(Bool, snd_autoplay, false, CVar::Flag::Save)
AudioEntryPanel::AudioEntryPanel(wxWindow* parent) :
EntryPanel(parent, "audio"),
timer_seek_{ new wxTimer(this) },
#if (SFML_VERSION_MAJOR > 2)
#if (SFML_VERSION_MAJOR > 2)
sound_{ new sf::Sound(*sound_buffer_) },
#else
#else
sound_{ new sf::Sound() },
#endif
#endif
music_{ new audio::Music() },
mod_{ new audio::ModMusic() },
mp3_{ new audio::Mp3Music() }
Expand Down Expand Up @@ -725,20 +725,20 @@ void AudioEntryPanel::onTimer(wxTimerEvent& e)
// Set slider
slider_seek_->SetValue(pos);

// Stop the timer if playback has reached the end
#if (SFML_VERSION_MAJOR > 2)
// Stop the timer if playback has reached the end
#if (SFML_VERSION_MAJOR > 2)
if (pos >= slider_seek_->GetMax() || (audio_type_ == Sound && sound_->getStatus() == sf::Sound::Status::Stopped)
|| (audio_type_ == Music && music_->getStatus() == sf::Sound::Status::Stopped)
|| (audio_type_ == Mod && mod_->getStatus() == sf::Sound::Status::Stopped)
|| (audio_type_ == Mp3 && mp3_->getStatus() == sf::Sound::Status::Stopped)
|| (audio_type_ == MIDI && !audio::midiPlayer().isPlaying()))
#else
#else
if (pos >= slider_seek_->GetMax() || (audio_type_ == Sound && sound_->getStatus() == sf::Sound::Stopped)
|| (audio_type_ == Music && music_->getStatus() == sf::Sound::Stopped)
|| (audio_type_ == Mod && mod_->getStatus() == sf::Sound::Stopped)
|| (audio_type_ == Mp3 && mp3_->getStatus() == sf::Sound::Stopped)
|| (audio_type_ == MIDI && !audio::midiPlayer().isPlaying()))
#endif
#endif
{
timer_seek_->Stop();
slider_seek_->SetValue(0);
Expand Down

0 comments on commit a116a40

Please sign in to comment.