Skip to content

Commit

Permalink
Add playing position control to midi player
Browse files Browse the repository at this point in the history
  • Loading branch information
ales-tsurko committed Oct 24, 2024
1 parent e5e3c3f commit e5b5730
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 13 deletions.
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ ahash = "0.8.11"
async-channel = "2.3.1"
cpal = "0.15.3"
env_logger = "0.11"
iced = { version = "0.13.1", features = ["wgpu", "svg"] }
iced = { version = "0.13.1", features = ["svg", "tokio", "wgpu"] }
log = "0.4"
midi-player = "0.2.1"
quick-xml = "0.31.0"
Expand Down
2 changes: 1 addition & 1 deletion pysrc/athenaCL/libATH/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -6775,7 +6775,7 @@ def gather(self):
else: # files was created but does not exists
msg.append(lang.msgELaudioMoved % self.audioPath)

# only midi file prodiuces an output
# only midi file produces an output
self.midPath = self.ao.aoInfo["fpMidi"]
if "midiFile" in outComplete:
if os.path.isfile(self.midPath): # if file found
Expand Down
68 changes: 61 additions & 7 deletions src/app/app.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Application's GUI.
use iced::futures::sink::SinkExt;
use iced::futures::{join, sink::SinkExt};
use iced::stream;
use iced::widget::{column, container, scrollable, text, text::Style as TextStyle, text_input};
use iced::{Element, Font, Subscription};
use iced::{time, Element, Font, Subscription};

use super::midi_player::{self, GlobalState as GlobalMidiPlayerState, State as MidiPlayerState};
use crate::interpreter;
Expand All @@ -27,10 +27,23 @@ pub struct State {
impl Default for State {
fn default() -> Self {
let midi_player_state = GlobalMidiPlayerState::new(SOUND_FONT);
let output = vec![Output::Normal(
r#"
_ _ ___ __
__ _| |_| |__ ___ _ __ __ _ / __\ / /
/ _` | __| '_ \ / _ \ '_ \ / _` |/ / / /
| (_| | |_| | | | __/ | | | (_| / /___/ /___
\__,_|\__|_| |_|\___|_| |_|\__,_\____/\____/
Welcome to athenaCL!
Type your commands in the input below.
"#
.to_owned(),
)];
Self {
midi_player_state,
answer: String::new(),
output: Vec::new(),
output,
question: None,
}
}
Expand Down Expand Up @@ -69,6 +82,7 @@ pub fn update(state: &mut State, message: Message) {
state.output.push(Output::Error(e.to_string()));
return;
}
state.midi_player_state.controller.set_position(player.position);
state.midi_player_state.controller.play();
state.midi_player_state.playing_id = Some(id);
}
Expand All @@ -85,6 +99,32 @@ pub fn update(state: &mut State, message: Message) {
}
}
}
Message::ChangePlayingPosition(id, position) => {
if let Some(Output::MidiPlayer(player_state)) = state.output.get_mut(id) {
player_state.position = position;

if let Some(playing_id) = state.midi_player_state.playing_id {
if playing_id == id {
state.midi_player_state.controller.set_position(position);
}
}
}
}
Message::Tick(_) => {
if let Some(playing_id) = state.midi_player_state.playing_id {
if let Some(Output::MidiPlayer(player_state)) = state.output.get_mut(playing_id) {
let position = state.midi_player_state.controller.position();

player_state.position = position;

if position >= 1.0 {
player_state.is_playing = false;
player_state.position = 0.0;
state.midi_player_state.playing_id = None;
}
}
}
}
Message::InterpreterMessage(msg) => match msg {
interpreter::Message::SendCmd(ref cmd) => {
state.answer = "".to_owned();
Expand All @@ -111,6 +151,7 @@ pub fn update(state: &mut State, message: Message) {
is_playing: false,
path: path.into(),
id: state.output.len(),
position: 0.0,
}));
}
},
Expand Down Expand Up @@ -231,6 +272,9 @@ pub enum Message {
Answer(String, String),
PlayMidi(usize),
StopMidi(usize),
// id, position
ChangePlayingPosition(usize, f64),
Tick(time::Instant),
}

impl From<interpreter::Message> for Message {
Expand All @@ -240,10 +284,12 @@ impl From<interpreter::Message> for Message {
}

#[allow(missing_docs)]
pub fn subscription(_: &State) -> Subscription<Message> {
pub fn subscription(state: &State) -> Subscription<Message> {
// this worker runs async loop to make the worker, which runs on a System's thread communicate
// with our app, whithout blocking the event loop of iced...
Subscription::run(|| {
// with our app, whithout blocking the event loop of iced

// let position_observer = state.midi_player_state.controller.new_position_observer();
let interpreter_listener = Subscription::run(|| {
let receiver = interpreter::INTERPRETER_WORKER.gui_receiver.clone();

stream::channel(1000, |mut output| async move {
Expand All @@ -259,5 +305,13 @@ pub fn subscription(_: &State) -> Subscription<Message> {
}
})
})
.map(Message::InterpreterMessage)
.map(Message::InterpreterMessage);

let position_listener = if state.midi_player_state.controller.is_playing() {
time::every(time::Duration::from_millis(20)).map(Message::Tick)
} else {
Subscription::none()
};

Subscription::batch([interpreter_listener, position_listener])
}
20 changes: 16 additions & 4 deletions src/app/midi_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use cpal::{
Stream as AudioStream, StreamConfig,
};
use iced::{
widget::{button, svg, text},
widget::{button, row, slider, svg},
Element,
};
use midi_player::{Player, PlayerController, Settings as PlayerSettings};
Expand Down Expand Up @@ -82,15 +82,27 @@ pub(crate) struct State {
pub(crate) is_playing: bool,
pub(crate) path: PathBuf,
pub(crate) id: usize,
pub(crate) position: f64,
}

pub(crate) fn view<'a>(state: &'a State) -> Element<'a, Message> {
let label =
svg(if state.is_playing { "resources/img/pause.svg" } else { "resources/img/play.svg" });
let label = svg(if state.is_playing {
"resources/img/pause.svg"
} else {
"resources/img/play.svg"
});
let message = if state.is_playing {
Message::StopMidi(state.id)
} else {
Message::PlayMidi(state.id)
};
button(label).on_press(message).width(60.0).into()
row![
button(label).on_press(message).width(60.0),
slider(0.0..=1.0, state.position, |v| {
Message::ChangePlayingPosition(state.id, v)
}).step(0.001)
]
.align_y(iced::Alignment::Center)
.spacing(10.0)
.into()
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn main() -> iced::Result {
})
.window(iced::window::Settings {
min_size: Some((800.0, 600.0).into()),
max_size: Some((800.0, f32::MAX).into()),
..Default::default()
})
.run()
Expand Down

0 comments on commit e5b5730

Please sign in to comment.