edit pt.2; enqueue; fix zero divisions

This commit is contained in:
🪞👃🪞 2024-11-26 15:22:24 +01:00
parent c93081d061
commit d1fdc7f8b6
3 changed files with 72 additions and 61 deletions

View file

@ -199,22 +199,25 @@ pub trait MidiOutputApi: ClockApi + HasPhrase {
if let Some(ref phrase) = phrase { if let Some(ref phrase) = phrase {
// Source phrase from which the MIDI events will be taken. // Source phrase from which the MIDI events will be taken.
let phrase = phrase.read().unwrap(); let phrase = phrase.read().unwrap();
// Current pulse index in source phrase // Phrase with zero length is not processed
let pulse = pulse % phrase.length; if phrase.length > 0 {
// Output each MIDI event from phrase at appropriate frames of output buffer: // Current pulse index in source phrase
for message in phrase.notes[pulse].iter() { let pulse = pulse % phrase.length;
// Clear output buffer for this MIDI event. // Output each MIDI event from phrase at appropriate frames of output buffer:
note_buffer.clear(); for message in phrase.notes[pulse].iter() {
// TODO: support MIDI channels other than CH1. // Clear output buffer for this MIDI event.
let channel = 0.into(); note_buffer.clear();
// Serialize MIDI event into message buffer. // TODO: support MIDI channels other than CH1.
LiveEvent::Midi { channel, message: *message } let channel = 0.into();
.write(note_buffer) // Serialize MIDI event into message buffer.
.unwrap(); LiveEvent::Midi { channel, message: *message }
// Append serialized message to output buffer. .write(note_buffer)
output_buffer[sample].push(note_buffer.clone()); .unwrap();
// Update the list of currently held notes. // Append serialized message to output buffer.
update_keys(&mut*notes, &message); output_buffer[sample].push(note_buffer.clone());
// Update the list of currently held notes.
update_keys(&mut*notes, &message);
}
} }
} }
} }

View file

@ -6,19 +6,16 @@ impl Handle<Tui> for SequencerTui {
} }
} }
pub trait SequencerControl: TransportControl {}
impl SequencerControl for SequencerTui {}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum SequencerCommand { pub enum SequencerCommand {
Focus(FocusCommand), Focus(FocusCommand),
Undo,
Redo,
Clear,
Clock(ClockCommand), Clock(ClockCommand),
Phrases(PhrasesCommand), Phrases(PhrasesCommand),
Editor(PhraseCommand), Editor(PhraseCommand),
Enqueue(Option<Arc<RwLock<Phrase>>>),
Clear,
Undo,
Redo,
} }
impl Command<SequencerTui> for SequencerCommand { impl Command<SequencerTui> for SequencerCommand {
@ -29,6 +26,10 @@ impl Command<SequencerTui> for SequencerCommand {
Phrases(cmd) => cmd.execute(state)?.map(Phrases), Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor), Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock), Clock(cmd) => cmd.execute(state)?.map(Clock),
Enqueue(phrase) => {
state.player.enqueue_next(phrase.as_ref());
None
},
Undo => { todo!() }, Undo => { todo!() },
Redo => { todo!() }, Redo => { todo!() },
Clear => { todo!() }, Clear => { todo!() },
@ -47,43 +48,50 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<S
use SequencerCommand::*; use SequencerCommand::*;
use PhraseCommand::Show; use PhraseCommand::Show;
use ClockCommand::{Play, Pause}; use ClockCommand::{Play, Pause};
use KeyCode::Char; use KeyCode::{Char, Enter};
if !state.entered() {
return None
}
Some(match input.event() { Some(match input.event() {
// Play/pause
key!(Char(' ')) => Clock(
if state.is_stopped() { Play(None) } else { Pause(None) }
),
// Play from start/rewind to start
key!(Shift-Char(' ')) => Clock(
if state.is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }
),
// Edit phrase // Edit phrase
//key!(Char('e')) => Editor(Show(state.phrase_to_edit().clone())), key!(Char('e')) => match state.focused() {
key!(Char(' ')) => Clock(if state.is_stopped() { Play(None) } else { Pause(None) }), SequencerFocus::PhrasePlay => Editor(Show(
key!(Shift-Char(' ')) => Clock(if state.is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }), state.player.play_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone())
_ => match state.focused() { )),
SequencerFocus::Transport(_) => match TransportCommand::input_to_command(state, input)? { SequencerFocus::PhraseNext => Editor(Show(
TransportCommand::Clock(command) => Clock(command), state.player.next_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone())
_ => return None, )),
}, SequencerFocus::PhraseList => Editor(Show(
SequencerFocus::PhrasePlay => match input.event() { Some(state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone())
key!(Char('e')) => Editor(Show( )),
state.player.play_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone()) _ => return None,
)), },
_ => return None, _ => if state.entered() {
}, match state.focused() {
SequencerFocus::PhraseNext => match input.event() { SequencerFocus::Transport(_) => match TransportCommand::input_to_command(state, input)? {
key!(Char('e')) => Editor(Show( TransportCommand::Clock(command) => Clock(command),
state.player.next_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone()) _ => return None,
)), },
_ => return None, SequencerFocus::PhraseEditor => Editor(
}, PhraseCommand::input_to_command(state, input)?
SequencerFocus::PhraseList => match input.event() { ),
key!(Char('e')) => Editor(Show( SequencerFocus::PhraseList => match input.event() {
Some(state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone()) key!(Enter) => Enqueue(Some(
)), state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone()
_ => Phrases( )),
PhrasesCommand::input_to_command(state, input)? _ => Phrases(
) PhrasesCommand::input_to_command(state, input)?
}, ),
SequencerFocus::PhraseEditor => Editor( }
PhraseCommand::input_to_command(state, input)? _ => return None
), }
} else {
return None
} }
}) })
} }

View file

@ -21,11 +21,11 @@ pub struct ClockModel {
impl From<&Arc<Transport>> for ClockModel { impl From<&Arc<Transport>> for ClockModel {
fn from (transport: &Arc<Transport>) -> Self { fn from (transport: &Arc<Transport>) -> Self {
Self { Self {
current: Instant::default().into(),
playing: RwLock::new(None).into(), playing: RwLock::new(None).into(),
quant: Quantize::default().into(),
started: RwLock::new(None).into(), started: RwLock::new(None).into(),
sync: LaunchSync::default().into(), current: Instant::default().into(),
quant: Arc::new(24.into()),
sync: Arc::new(384.into()),
transport: transport.clone(), transport: transport.clone(),
metronome: false, metronome: false,
} }