mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
refactor PhrasePlayer::process callback
This commit is contained in:
parent
0820c10f8b
commit
3df8e87840
4 changed files with 118 additions and 87 deletions
|
|
@ -22,6 +22,8 @@ pub struct Arranger<E: Engine> {
|
|||
pub arrangement_split: u16,
|
||||
/// Width of phrase pool
|
||||
pub phrases_split: u16,
|
||||
/// Width and height of app at last render
|
||||
pub size: Measure<E>,
|
||||
}
|
||||
/// Sections in the arranger app that may be focused
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
|
|
@ -121,9 +123,9 @@ impl<E: Engine> Arranger<E> {
|
|||
phrases: Arc<RwLock<PhrasePool<E>>>,
|
||||
) -> Self {
|
||||
let mut app = Self {
|
||||
jack: None,
|
||||
focus_cursor: (0, 1),
|
||||
phrases_split: 20,
|
||||
jack: None,
|
||||
focus_cursor: (0, 1),
|
||||
phrases_split: 20,
|
||||
arrangement_split: 21,
|
||||
editor: PhraseEditor::new(),
|
||||
status: ArrangerStatusBar::ArrangementClip,
|
||||
|
|
@ -135,6 +137,7 @@ impl<E: Engine> Arranger<E> {
|
|||
transport,
|
||||
arrangement,
|
||||
phrases,
|
||||
size: Measure::new(),
|
||||
};
|
||||
app.update_focus();
|
||||
app
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ impl Content for Arrangement<Tui> {
|
|||
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)),
|
||||
}?;
|
||||
let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
||||
add(&TuiStyle::fg("Session", color).push_x(1))?;
|
||||
add(&TuiStyle::fg("Session", color).push_x(2))?;
|
||||
add(&self.size)
|
||||
})
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
|||
let border_hi = Color::Rgb(100, 110, 40);
|
||||
let border_lo = Color::Rgb(70, 80, 50);
|
||||
let border_fg = if self.0.focused { border_hi } else { border_lo };
|
||||
let border = Lozenge(Style::default().bg(border_bg).fg(border_fg));
|
||||
//let border = Lozenge(Style::default().bg(border_bg).fg(border_fg));
|
||||
let track_title_h = 2u16;
|
||||
let tracks_footer = 3u16;
|
||||
let scene_title_w = 3 + Scene::longest_name(scenes) as u16; // x of 1st track
|
||||
|
|
|
|||
|
|
@ -403,15 +403,6 @@ impl PhrasePlayer {
|
|||
.map(|(started,_)|started.sample().get())
|
||||
.map(|started|(started - self.clock.instant.sample().get()) as usize)
|
||||
}
|
||||
pub fn playing_phrase (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
|
||||
if let (
|
||||
Some(TransportState::Rolling), Some((started, Some(ref phrase)))
|
||||
) = (*self.clock.playing.read().unwrap(), &self.phrase) {
|
||||
Some((started.sample().get() as usize, phrase.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Displays and edits phrase length
|
||||
pub struct PhraseLength<E: Engine> {
|
||||
|
|
|
|||
|
|
@ -10,100 +10,137 @@ impl<E: Engine> Audio for Sequencer<E> {
|
|||
}
|
||||
impl Audio for PhrasePlayer {
|
||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
let frame0 = scope.last_frame_time() as usize;
|
||||
let frames = scope.n_frames() as usize;
|
||||
let has_midi_outputs = self.has_midi_outputs();
|
||||
let has_midi_inputs = self.has_midi_inputs();
|
||||
if has_midi_outputs {
|
||||
self.clear_midi_out_buf(frames);
|
||||
self.reset_midi_out_buf(false /* FIXME where did force-reset come from? */);
|
||||
}
|
||||
let has_midi_inputs = self.has_midi_inputs();
|
||||
let quant = self.clock.quant().get();
|
||||
if let Some((start_frame, phrase)) = self.playing_phrase() {
|
||||
// Clear output buffer(s)
|
||||
self.clear(scope, false);
|
||||
// Write chunk of phrase to output
|
||||
phrase.read().map(|phrase|{
|
||||
if has_midi_outputs {
|
||||
let output = &mut self.midi_out_buf;
|
||||
let notes_on = &mut self.notes_out.write().unwrap();
|
||||
let frame0 = frame0.saturating_sub(start_frame);
|
||||
let frame_n = frame0 + frames;
|
||||
let ticks = self.clock.timebase().pulses_between_samples(frame0, frame_n);
|
||||
let mut buf = Vec::with_capacity(8);
|
||||
for (sample, tick) in ticks {
|
||||
let tick = tick % phrase.length;
|
||||
for message in phrase.notes[tick].iter() {
|
||||
buf.clear();
|
||||
let channel = 0.into();
|
||||
let message = *message;
|
||||
LiveEvent::Midi { channel, message }.write(&mut buf).unwrap();
|
||||
output[sample].push(buf.clone());
|
||||
update_keys(notes_on, &message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
let mut phrase = phrase.write().unwrap();
|
||||
let length = phrase.length;
|
||||
// Monitor and record input
|
||||
if has_midi_inputs && (self.recording || self.monitoring) {
|
||||
// For highlighting keys and note repeat
|
||||
let mut notes_in = self.notes_in.write().unwrap();
|
||||
// Record from each input
|
||||
for input in self.midi_inputs.iter() {
|
||||
for (frame, event, bytes) in parse_midi_input(input.iter(scope)) {
|
||||
if let LiveEvent::Midi { message, .. } = event {
|
||||
if self.monitoring { self.midi_out_buf[frame].push(bytes.to_vec()) }
|
||||
if self.recording {
|
||||
phrase.record_event({
|
||||
let pulse = self.clock.timebase().samples_to_pulse(
|
||||
(frame0 + frame - start_frame) as f64
|
||||
);
|
||||
let quantized = (pulse / quant).round() * quant;
|
||||
let looped = quantized as usize % length;
|
||||
looped
|
||||
}, message);
|
||||
}
|
||||
update_keys(&mut notes_in, &message);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.play(scope);
|
||||
}
|
||||
if has_midi_inputs {
|
||||
if self.recording || self.monitoring {
|
||||
// Record and/or monitor input
|
||||
self.record(scope)
|
||||
} else if has_midi_outputs && self.monitoring {
|
||||
// Monitor input to output
|
||||
self.monitor(scope)
|
||||
}
|
||||
} else if has_midi_inputs && has_midi_outputs && self.monitoring {
|
||||
}
|
||||
// Write to output port(s)
|
||||
self.write(scope);
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
impl PhrasePlayer {
|
||||
fn has_midi_inputs (&self) -> bool { self.midi_inputs.len() > 0 }
|
||||
fn has_midi_outputs (&self) -> bool { self.midi_outputs.len() > 0 }
|
||||
/// Clear the section of the output buffer that we will be using,
|
||||
/// emitting "all notes off" at start of buffer if requested.
|
||||
fn clear (&mut self, scope: &ProcessScope, force_reset: bool) {
|
||||
for frame in &mut self.midi_out_buf[0..scope.n_frames() as usize] {
|
||||
frame.clear();
|
||||
}
|
||||
if self.reset || force_reset {
|
||||
all_notes_off(&mut self.midi_out_buf); self.reset = false;
|
||||
}
|
||||
}
|
||||
/// Return playing phrase with starting point
|
||||
fn playing (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
|
||||
if let (
|
||||
Some(TransportState::Rolling), Some((started, Some(ref phrase)))
|
||||
) = (*self.clock.playing.read().unwrap(), &self.phrase) {
|
||||
Some((started.sample().get() as usize, phrase.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Return next phrase with starting point
|
||||
fn enqueued (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
|
||||
if let (
|
||||
Some(TransportState::Rolling), Some((start_at, Some(ref phrase)))
|
||||
) = (*self.clock.playing.read().unwrap(), &self.next_phrase) {
|
||||
Some((start_at.sample().get() as usize, phrase.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn play (&mut self, scope: &ProcessScope) {
|
||||
let sample0 = scope.last_frame_time() as usize;
|
||||
let samples = scope.n_frames() as usize;
|
||||
if let Some((start, ref phrase)) = self.playing() {
|
||||
let sample = sample0.saturating_sub(start);
|
||||
let ticks = self.clock.timebase().pulses_between_samples(sample, sample + samples);
|
||||
phrase.read().map(|phrase|{
|
||||
let output = &mut self.midi_out_buf;
|
||||
let notes_on = &mut self.notes_out.write().unwrap();
|
||||
let mut buf = Vec::with_capacity(8);
|
||||
for (sample, tick) in ticks {
|
||||
let tick = tick % phrase.length;
|
||||
for message in phrase.notes[tick].iter() {
|
||||
buf.clear();
|
||||
let channel = 0.into();
|
||||
let message = *message;
|
||||
LiveEvent::Midi { channel, message }.write(&mut buf).unwrap();
|
||||
output[sample].push(buf.clone());
|
||||
update_keys(notes_on, &message);
|
||||
}
|
||||
}
|
||||
}).unwrap()
|
||||
}
|
||||
}
|
||||
fn record (&mut self, scope: &ProcessScope) {
|
||||
let sample0 = scope.last_frame_time() as usize;
|
||||
if let Some((start, ref phrase)) = self.playing() {
|
||||
let quant = self.clock.quant().get();
|
||||
let mut phrase = phrase.write().unwrap();
|
||||
let length = phrase.length;
|
||||
// For highlighting keys and note repeat
|
||||
let mut notes_in = self.notes_in.write().unwrap();
|
||||
// Monitor each input
|
||||
// Record from each input
|
||||
for input in self.midi_inputs.iter() {
|
||||
for (frame, event, bytes) in parse_midi_input(input.iter(scope)) {
|
||||
for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
|
||||
if let LiveEvent::Midi { message, .. } = event {
|
||||
self.midi_out_buf[frame].push(bytes.to_vec());
|
||||
if self.monitoring { self.midi_out_buf[sample].push(bytes.to_vec()) }
|
||||
if self.recording {
|
||||
phrase.record_event({
|
||||
let pulse = self.clock.timebase().samples_to_pulse(
|
||||
(sample0 + sample - start) as f64
|
||||
);
|
||||
let quantized = (pulse / quant).round() * quant;
|
||||
let looped = quantized as usize % length;
|
||||
looped
|
||||
}, message);
|
||||
}
|
||||
update_keys(&mut notes_in, &message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Write to midi output
|
||||
}
|
||||
fn monitor (&mut self, scope: &ProcessScope) {
|
||||
let mut notes_in = self.notes_in.write().unwrap();
|
||||
for input in self.midi_inputs.iter() {
|
||||
for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
|
||||
if let LiveEvent::Midi { message, .. } = event {
|
||||
self.midi_out_buf[sample].push(bytes.to_vec());
|
||||
update_keys(&mut notes_in, &message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn write (&mut self, scope: &ProcessScope) {
|
||||
let samples = scope.n_frames() as usize;
|
||||
for port in self.midi_outputs.iter_mut() {
|
||||
let writer = &mut port.writer(scope);
|
||||
let output = &self.midi_out_buf;
|
||||
for time in 0..frames {
|
||||
for time in 0..samples {
|
||||
for event in output[time].iter() {
|
||||
writer.write(&RawMidi { time: time as u32, bytes: &event })
|
||||
.expect(&format!("{event:?}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
impl PhrasePlayer {
|
||||
pub fn has_midi_inputs (&self) -> bool { self.midi_inputs.len() > 0 }
|
||||
pub fn has_midi_outputs (&self) -> bool { self.midi_outputs.len() > 0 }
|
||||
/// Clear the section of the output buffer that we will be using
|
||||
pub fn clear_midi_out_buf (&mut self, frames: usize) {
|
||||
for frame in &mut self.midi_out_buf[0..frames] { frame.clear(); }
|
||||
}
|
||||
/// Emit "all notes off" at start of buffer if requested
|
||||
pub fn reset_midi_out_buf (&mut self, force_reset: bool) {
|
||||
if self.reset || force_reset { all_notes_off(&mut self.midi_out_buf); self.reset = false; }
|
||||
}
|
||||
}
|
||||
/// Add "all notes off" to the start of a buffer.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue