Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust playback speed #12

Merged
merged 2 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/audio/midi_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,19 @@ impl AudioPlayer {
pub fn new(
song: Rc<Song>,
song_tempo: i32,
tempo_percentage: usize,
sound_font_file: Option<PathBuf>,
beat_sender: Arc<Sender<usize>>,
) -> Self {
// default to no solo track
let solo_track_id = None;

// player params
let player_params = Arc::new(Mutex::new(MidiPlayerParams::new(song_tempo, solo_track_id)));
let player_params = Arc::new(Mutex::new(MidiPlayerParams::new(
song_tempo,
tempo_percentage,
solo_track_id,
)));

// midi sequencer initialization
let builder = MidiBuilder::new();
Expand Down Expand Up @@ -102,6 +107,11 @@ impl AudioPlayer {
}
}

pub fn set_tempo_percentage(&mut self, new_tempo_percentage: usize) {
let mut params_guard = self.player_params.lock().unwrap();
params_guard.set_tempo_percentage(new_tempo_percentage)
}

pub fn stop(&mut self) {
// Pause stream
if let Some(stream) = &self.stream {
Expand Down Expand Up @@ -233,7 +243,7 @@ fn new_output_stream(
move |output: &mut [f32], _: &cpal::OutputCallbackInfo| {
let mut player_params_guard = player_params.lock().unwrap();
let mut sequencer_guard = sequencer.lock().unwrap();
sequencer_guard.advance(player_params_guard.tempo());
sequencer_guard.advance(player_params_guard.adjusted_tempo());
let mut synthesizer_guard = synthesizer.lock().unwrap();
// process midi events for current tick
if let Some(events) = sequencer_guard.get_next_events() {
Expand Down
12 changes: 9 additions & 3 deletions src/audio/midi_player_params.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/// Hold values changed during playback of a MIDI events.
pub struct MidiPlayerParams {
tempo: i32,
tempo_percentage: usize,
solo_track_id: Option<usize>,
}

impl MidiPlayerParams {
pub fn new(tempo: i32, solo_track_id: Option<usize>) -> Self {
pub fn new(tempo: i32, tempo_percentage: usize, solo_track_id: Option<usize>) -> Self {
Self {
tempo,
tempo_percentage,
solo_track_id,
}
}
Expand All @@ -20,11 +22,15 @@ impl MidiPlayerParams {
self.solo_track_id = solo_track_id;
}

pub fn tempo(&self) -> i32 {
self.tempo
pub fn adjusted_tempo(&self) -> i32 {
(self.tempo as f32 * self.tempo_percentage as f32 / 100.0) as i32
}

pub fn set_tempo(&mut self, tempo: i32) {
self.tempo = tempo;
}

pub fn set_tempo_percentage(&mut self, tempo_percentage: usize) {
self.tempo_percentage = tempo_percentage;
}
}
73 changes: 65 additions & 8 deletions src/ui/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub struct RuxApplication {
all_tracks: Vec<TrackSelection>, // all possible tracks
tablature: Option<Tablature>, // loaded tablature
tablature_id: container::Id, // tablature container id
tempo_selection: TempoSelection, // tempo percentage for playback
audio_player: Option<AudioPlayer>, // audio player
tab_file_is_loading: bool, // file loading flag in progress
sound_font_file: Option<PathBuf>, // sound font file
Expand All @@ -54,6 +55,43 @@ impl SongDisplayInfo {
}
}

#[derive(Debug, Clone, PartialEq)]
pub struct TempoSelection {
percentage: usize,
}

impl Default for TempoSelection {
fn default() -> Self {
TempoSelection::new(100)
}
}

impl TempoSelection {
fn new(percentage: usize) -> Self {
Self { percentage }
}

fn values() -> Vec<TempoSelection> {
vec![
TempoSelection::new(25),
TempoSelection::new(50),
TempoSelection::new(60),
TempoSelection::new(70),
TempoSelection::new(80),
TempoSelection::new(90),
TempoSelection::new(100),
TempoSelection::new(150),
TempoSelection::new(200),
]
}
}

impl Display for TempoSelection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}%", self.percentage)
}
}

#[derive(Debug, Default, Clone, PartialEq)]
pub struct TrackSelection {
index: usize,
Expand All @@ -77,13 +115,14 @@ pub enum Message {
OpenFile, // open file dialog
FileOpened(Result<(Vec<u8>, String), PickerError>), // file content & file name
TrackSelected(TrackSelection), // track selection
FocusMeasure(usize), // used when clicking on measure in tablature
FocusTick(usize), // focus on a specific tick in the tablature
PlayPause, // toggle play/pause
StopPlayer, // stop playback
ToggleSolo, // toggle solo mode
WindowResized, // window resized
TablatureResized(Size), // tablature resized
FocusMeasure(usize), // used when clicking on measure in tablature
FocusTick(usize), // focus on a specific tick in the tablature
PlayPause, // toggle play/pause
StopPlayer, // stop playback
ToggleSolo, // toggle solo mode
WindowResized, // window resized
TablatureResized(Size), // tablature resized
TempoSelected(TempoSelection), // tempo selection
}

impl RuxApplication {
Expand All @@ -95,6 +134,7 @@ impl RuxApplication {
all_tracks: vec![],
tablature: None,
tablature_id: container::Id::new("tablature-outer-container"),
tempo_selection: TempoSelection::default(),
audio_player: None,
tab_file_is_loading: false,
sound_font_file,
Expand Down Expand Up @@ -186,6 +226,7 @@ impl RuxApplication {
let audio_player = AudioPlayer::new(
song_rc.clone(),
song_rc.tempo.value,
self.tempo_selection.percentage,
self.sound_font_file.clone(),
self.beat_sender.clone(),
);
Expand Down Expand Up @@ -270,6 +311,13 @@ impl RuxApplication {
}
Task::none()
}
Message::TempoSelected(tempos_selection) => {
if let Some(audio_player) = &mut self.audio_player {
audio_player.set_tempo_percentage(tempos_selection.percentage)
}
self.tempo_selection = tempos_selection;
Task::none()
}
}
}

Expand Down Expand Up @@ -298,6 +346,15 @@ impl RuxApplication {
let track_control = if self.all_tracks.is_empty() {
row![horizontal_space()]
} else {
let tempo_label = text("Tempo").size(14);
let tempo_percentage = pick_list(
TempoSelection::values(),
Some(&self.tempo_selection),
Message::TempoSelected,
)
.text_size(14)
.padding([5, 10]);

let solo_mode = action_toggle(
solo_icon(),
"Solo",
Expand All @@ -315,7 +372,7 @@ impl RuxApplication {
.text_size(14)
.padding([5, 10]);

row![solo_mode, track_pick_list,]
row![tempo_label, tempo_percentage, solo_mode, track_pick_list,]
.spacing(10)
.align_y(Alignment::Center)
};
Expand Down