mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +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 {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue