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 {
// Source phrase from which the MIDI events will be taken.
let phrase = phrase.read().unwrap();
// Current pulse index in source phrase
let pulse = pulse % phrase.length;
// Output each MIDI event from phrase at appropriate frames of output buffer:
for message in phrase.notes[pulse].iter() {
// Clear output buffer for this MIDI event.
note_buffer.clear();
// TODO: support MIDI channels other than CH1.
let channel = 0.into();
// Serialize MIDI event into message buffer.
LiveEvent::Midi { channel, message: *message }
.write(note_buffer)
.unwrap();
// Append serialized message to output buffer.
output_buffer[sample].push(note_buffer.clone());
// Update the list of currently held notes.
update_keys(&mut*notes, &message);
// Phrase with zero length is not processed
if phrase.length > 0 {
// Current pulse index in source phrase
let pulse = pulse % phrase.length;
// Output each MIDI event from phrase at appropriate frames of output buffer:
for message in phrase.notes[pulse].iter() {
// Clear output buffer for this MIDI event.
note_buffer.clear();
// TODO: support MIDI channels other than CH1.
let channel = 0.into();
// Serialize MIDI event into message buffer.
LiveEvent::Midi { channel, message: *message }
.write(note_buffer)
.unwrap();
// Append serialized message to output buffer.
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)]
pub enum SequencerCommand {
Focus(FocusCommand),
Undo,
Redo,
Clear,
Clock(ClockCommand),
Phrases(PhrasesCommand),
Editor(PhraseCommand),
Enqueue(Option<Arc<RwLock<Phrase>>>),
Clear,
Undo,
Redo,
}
impl Command<SequencerTui> for SequencerCommand {
@ -29,6 +26,10 @@ impl Command<SequencerTui> for SequencerCommand {
Phrases(cmd) => cmd.execute(state)?.map(Phrases),
Editor(cmd) => cmd.execute(state)?.map(Editor),
Clock(cmd) => cmd.execute(state)?.map(Clock),
Enqueue(phrase) => {
state.player.enqueue_next(phrase.as_ref());
None
},
Undo => { todo!() },
Redo => { todo!() },
Clear => { todo!() },
@ -47,43 +48,50 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<S
use SequencerCommand::*;
use PhraseCommand::Show;
use ClockCommand::{Play, Pause};
use KeyCode::Char;
if !state.entered() {
return None
}
use KeyCode::{Char, Enter};
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
//key!(Char('e')) => Editor(Show(state.phrase_to_edit().clone())),
key!(Char(' ')) => Clock(if state.is_stopped() { Play(None) } else { Pause(None) }),
key!(Shift-Char(' ')) => Clock(if state.is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }),
_ => match state.focused() {
SequencerFocus::Transport(_) => match TransportCommand::input_to_command(state, input)? {
TransportCommand::Clock(command) => Clock(command),
_ => return None,
},
SequencerFocus::PhrasePlay => match input.event() {
key!(Char('e')) => Editor(Show(
state.player.play_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone())
)),
_ => return None,
},
SequencerFocus::PhraseNext => match input.event() {
key!(Char('e')) => Editor(Show(
state.player.next_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone())
)),
_ => return None,
},
SequencerFocus::PhraseList => match input.event() {
key!(Char('e')) => Editor(Show(
Some(state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone())
)),
_ => Phrases(
PhrasesCommand::input_to_command(state, input)?
)
},
SequencerFocus::PhraseEditor => Editor(
PhraseCommand::input_to_command(state, input)?
),
key!(Char('e')) => match state.focused() {
SequencerFocus::PhrasePlay => Editor(Show(
state.player.play_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone())
)),
SequencerFocus::PhraseNext => Editor(Show(
state.player.next_phrase().as_ref().map(|x|x.1.as_ref()).flatten().map(|x|x.clone())
)),
SequencerFocus::PhraseList => Editor(Show(
Some(state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone())
)),
_ => return None,
},
_ => if state.entered() {
match state.focused() {
SequencerFocus::Transport(_) => match TransportCommand::input_to_command(state, input)? {
TransportCommand::Clock(command) => Clock(command),
_ => return None,
},
SequencerFocus::PhraseEditor => Editor(
PhraseCommand::input_to_command(state, input)?
),
SequencerFocus::PhraseList => match input.event() {
key!(Enter) => Enqueue(Some(
state.phrases.phrases[state.phrases.phrase.load(Ordering::Relaxed)].clone()
)),
_ => Phrases(
PhrasesCommand::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 {
fn from (transport: &Arc<Transport>) -> Self {
Self {
current: Instant::default().into(),
playing: RwLock::new(None).into(),
quant: Quantize::default().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(),
metronome: false,
}