mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
edit pt.2; enqueue; fix zero divisions
This commit is contained in:
parent
c93081d061
commit
d1fdc7f8b6
3 changed files with 72 additions and 61 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue