refactor midi_note and remove audio_in/out empty mods

This commit is contained in:
🪞👃🪞 2024-12-27 21:07:56 +01:00
parent e69cf6d9cb
commit cb7ba855ab
12 changed files with 182 additions and 174 deletions

View file

@ -1,9 +1,5 @@
use crate::*;
mod audio_in;
mod audio_out;
mod sampler;
pub(crate) use self::sampler::*;
pub use self::sampler::{Sampler, Sample, Voice};

View file

@ -1,14 +1,36 @@
use crate::*;
pub(crate) mod midi_in; pub(crate) use midi_in::*;
pub(crate) mod midi_launch; pub(crate) use midi_launch::*;
pub(crate) mod midi_note; pub(crate) use midi_note::*;
pub(crate) mod midi_out; pub(crate) use midi_out::*;
pub(crate) mod midi_clip; pub(crate) use midi_clip::*;
pub(crate) mod midi_play; pub(crate) use midi_play::*;
pub(crate) mod midi_pool; pub(crate) use midi_pool::*;
pub(crate) mod midi_clip; pub(crate) use midi_clip::*;
pub(crate) mod midi_launch; pub(crate) use midi_launch::*;
pub(crate) mod midi_play; pub(crate) use midi_play::*;
pub(crate) mod midi_rec; pub(crate) use midi_rec::*;
pub(crate) mod midi_note; pub(crate) use midi_note::*;
pub(crate) mod midi_range; pub(crate) use midi_range::*;
pub(crate) mod midi_point; pub(crate) use midi_point::*;
pub(crate) mod midi_view; pub(crate) use midi_view::*;
/// Trait for thing that may receive MIDI.
pub trait HasMidiIns {
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
fn midi_ins_mut (&mut self) -> &mut Vec<Port<MidiIn>>;
fn has_midi_ins (&self) -> bool {
!self.midi_ins().is_empty()
}
}
/// Trait for thing that may output MIDI.
pub trait HasMidiOuts {
fn midi_outs (&self) -> &Vec<Port<MidiOut>>;
fn midi_outs_mut (&mut self) -> &mut Vec<Port<MidiOut>>;
fn has_midi_outs (&self) -> bool {
!self.midi_outs().is_empty()
}
/// Buffer for serializing a MIDI event. FIXME rename
fn midi_note (&mut self) -> &mut Vec<u8>;
}
/// Add "all notes off" to the start of a buffer.
pub fn all_notes_off (output: &mut [Vec<Vec<u8>>]) {
let mut buf = vec![];

View file

@ -1,10 +0,0 @@
use crate::*;
/// Trait for thing that may receive MIDI.
pub trait HasMidiIns {
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
fn midi_ins_mut (&mut self) -> &mut Vec<Port<MidiIn>>;
fn has_midi_ins (&self) -> bool {
!self.midi_ins().is_empty()
}
}

View file

@ -1,5 +1,7 @@
use crate::*;
pub struct Note;
impl Note {
/// (pulses, name), assuming 96 PPQ
pub const DURATIONS: [(usize, &str);26] = [
@ -32,131 +34,3 @@ impl Note {
""
}
}
pub trait MidiView<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
/// Make sure cursor is within range
fn autoscroll (&self) {
let note_point = self.note_point().min(127);
let note_lo = self.note_lo().get();
let note_hi = self.note_hi();
if note_point < note_lo {
self.note_lo().set(note_point);
} else if note_point > note_hi {
self.note_lo().set((note_lo + note_point).saturating_sub(note_hi));
}
}
/// Make sure range is within display
fn autozoom (&self) {
let time_len = self.time_len().get();
let time_axis = self.time_axis().get();
let time_zoom = self.time_zoom().get();
//while time_len.div_ceil(time_zoom) > time_axis {
//println!("\r{time_len} {time_zoom} {time_axis}");
//time_zoom = Note::next(time_zoom);
//}
//self.time_zoom().set(time_zoom);
}
}
#[derive(Debug, Clone)]
pub struct MidiRangeModel {
pub time_len: Arc<AtomicUsize>,
/// Length of visible time axis
pub time_axis: Arc<AtomicUsize>,
/// Earliest time displayed
pub time_start: Arc<AtomicUsize>,
/// Time step
pub time_zoom: Arc<AtomicUsize>,
/// Auto rezoom to fit in time axis
pub time_lock: Arc<AtomicBool>,
/// Length of visible note axis
pub note_axis: Arc<AtomicUsize>,
// Lowest note displayed
pub note_lo: Arc<AtomicUsize>,
}
from!(|data:(usize, bool)|MidiRangeModel = Self {
time_len: Arc::new(0.into()),
note_axis: Arc::new(0.into()),
note_lo: Arc::new(0.into()),
time_axis: Arc::new(0.into()),
time_start: Arc::new(0.into()),
time_zoom: Arc::new(data.0.into()),
time_lock: Arc::new(data.1.into()),
});
pub trait TimeRange {
fn time_len (&self) -> &AtomicUsize;
fn time_zoom (&self) -> &AtomicUsize;
fn time_lock (&self) -> &AtomicBool;
fn time_start (&self) -> &AtomicUsize;
fn time_axis (&self) -> &AtomicUsize;
fn time_end (&self) -> usize {
self.time_start().get() + self.time_axis().get() * self.time_zoom().get()
}
}
pub trait NoteRange {
fn note_lo (&self) -> &AtomicUsize;
fn note_axis (&self) -> &AtomicUsize;
fn note_hi (&self) -> usize {
(self.note_lo().get() + self.note_axis().get().saturating_sub(1)).min(127)
}
}
pub trait MidiRange: TimeRange + NoteRange {}
impl<T: TimeRange + NoteRange> MidiRange for T {}
impl TimeRange for MidiRangeModel {
fn time_len (&self) -> &AtomicUsize { &self.time_len }
fn time_zoom (&self) -> &AtomicUsize { &self.time_zoom }
fn time_lock (&self) -> &AtomicBool { &self.time_lock }
fn time_start (&self) -> &AtomicUsize { &self.time_start }
fn time_axis (&self) -> &AtomicUsize { &self.time_axis }
}
impl NoteRange for MidiRangeModel {
fn note_lo (&self) -> &AtomicUsize { &self.note_lo }
fn note_axis (&self) -> &AtomicUsize { &self.note_axis }
}
#[derive(Debug, Clone)]
pub struct MidiPointModel {
/// Time coordinate of cursor
pub time_point: Arc<AtomicUsize>,
/// Note coordinate of cursor
pub note_point: Arc<AtomicUsize>,
/// Length of note that will be inserted, in pulses
pub note_len: Arc<AtomicUsize>,
}
impl Default for MidiPointModel {
fn default () -> Self {
Self {
time_point: Arc::new(0.into()),
note_point: Arc::new(36.into()),
note_len: Arc::new(24.into()),
}
}
}
pub trait NotePoint {
fn note_len (&self) -> usize;
fn set_note_len (&self, x: usize);
fn note_point (&self) -> usize;
fn set_note_point (&self, x: usize);
fn note_end (&self) -> usize { self.note_point() + self.note_len() }
}
pub trait TimePoint {
fn time_point (&self) -> usize;
fn set_time_point (&self, x: usize);
}
pub trait MidiPoint: NotePoint + TimePoint {}
impl<T: NotePoint + TimePoint> MidiPoint for T {}
impl NotePoint for MidiPointModel {
fn note_len (&self) -> usize { self.note_len.load(Relaxed)}
fn set_note_len (&self, x: usize) { self.note_len.store(x, Relaxed) }
fn note_point (&self) -> usize { self.note_point.load(Relaxed).min(127) }
fn set_note_point (&self, x: usize) { self.note_point.store(x.min(127), Relaxed) }
}
impl TimePoint for MidiPointModel {
fn time_point (&self) -> usize { self.time_point.load(Relaxed) }
fn set_time_point (&self, x: usize) { self.time_point.store(x, Relaxed) }
}

View file

@ -1,12 +0,0 @@
use crate::*;
/// Trait for thing that may output MIDI.
pub trait HasMidiOuts {
fn midi_outs (&self) -> &Vec<Port<MidiOut>>;
fn midi_outs_mut (&mut self) -> &mut Vec<Port<MidiOut>>;
fn has_midi_outs (&self) -> bool {
!self.midi_outs().is_empty()
}
/// Buffer for serializing a MIDI event. FIXME rename
fn midi_note (&mut self) -> &mut Vec<u8>;
}

View file

@ -0,0 +1,50 @@
use crate::*;
#[derive(Debug, Clone)]
pub struct MidiPointModel {
/// Time coordinate of cursor
pub time_point: Arc<AtomicUsize>,
/// Note coordinate of cursor
pub note_point: Arc<AtomicUsize>,
/// Length of note that will be inserted, in pulses
pub note_len: Arc<AtomicUsize>,
}
impl Default for MidiPointModel {
fn default () -> Self {
Self {
time_point: Arc::new(0.into()),
note_point: Arc::new(36.into()),
note_len: Arc::new(24.into()),
}
}
}
pub trait NotePoint {
fn note_len (&self) -> usize;
fn set_note_len (&self, x: usize);
fn note_point (&self) -> usize;
fn set_note_point (&self, x: usize);
fn note_end (&self) -> usize { self.note_point() + self.note_len() }
}
pub trait TimePoint {
fn time_point (&self) -> usize;
fn set_time_point (&self, x: usize);
}
pub trait MidiPoint: NotePoint + TimePoint {}
impl<T: NotePoint + TimePoint> MidiPoint for T {}
impl NotePoint for MidiPointModel {
fn note_len (&self) -> usize { self.note_len.load(Relaxed)}
fn set_note_len (&self, x: usize) { self.note_len.store(x, Relaxed) }
fn note_point (&self) -> usize { self.note_point.load(Relaxed).min(127) }
fn set_note_point (&self, x: usize) { self.note_point.store(x.min(127), Relaxed) }
}
impl TimePoint for MidiPointModel {
fn time_point (&self) -> usize { self.time_point.load(Relaxed) }
fn set_time_point (&self, x: usize) { self.time_point.store(x, Relaxed) }
}

View file

@ -0,0 +1,64 @@
use crate::*;
#[derive(Debug, Clone)]
pub struct MidiRangeModel {
pub time_len: Arc<AtomicUsize>,
/// Length of visible time axis
pub time_axis: Arc<AtomicUsize>,
/// Earliest time displayed
pub time_start: Arc<AtomicUsize>,
/// Time step
pub time_zoom: Arc<AtomicUsize>,
/// Auto rezoom to fit in time axis
pub time_lock: Arc<AtomicBool>,
/// Length of visible note axis
pub note_axis: Arc<AtomicUsize>,
// Lowest note displayed
pub note_lo: Arc<AtomicUsize>,
}
from!(|data:(usize, bool)|MidiRangeModel = Self {
time_len: Arc::new(0.into()),
note_axis: Arc::new(0.into()),
note_lo: Arc::new(0.into()),
time_axis: Arc::new(0.into()),
time_start: Arc::new(0.into()),
time_zoom: Arc::new(data.0.into()),
time_lock: Arc::new(data.1.into()),
});
pub trait TimeRange {
fn time_len (&self) -> &AtomicUsize;
fn time_zoom (&self) -> &AtomicUsize;
fn time_lock (&self) -> &AtomicBool;
fn time_start (&self) -> &AtomicUsize;
fn time_axis (&self) -> &AtomicUsize;
fn time_end (&self) -> usize {
self.time_start().get() + self.time_axis().get() * self.time_zoom().get()
}
}
pub trait NoteRange {
fn note_lo (&self) -> &AtomicUsize;
fn note_axis (&self) -> &AtomicUsize;
fn note_hi (&self) -> usize {
(self.note_lo().get() + self.note_axis().get().saturating_sub(1)).min(127)
}
}
pub trait MidiRange: TimeRange + NoteRange {}
impl<T: TimeRange + NoteRange> MidiRange for T {}
impl TimeRange for MidiRangeModel {
fn time_len (&self) -> &AtomicUsize { &self.time_len }
fn time_zoom (&self) -> &AtomicUsize { &self.time_zoom }
fn time_lock (&self) -> &AtomicBool { &self.time_lock }
fn time_start (&self) -> &AtomicUsize { &self.time_start }
fn time_axis (&self) -> &AtomicUsize { &self.time_axis }
}
impl NoteRange for MidiRangeModel {
fn note_lo (&self) -> &AtomicUsize { &self.note_lo }
fn note_axis (&self) -> &AtomicUsize { &self.note_axis }
}

View file

@ -0,0 +1,26 @@
use crate::*;
pub trait MidiView<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
/// Make sure cursor is within range
fn autoscroll (&self) {
let note_point = self.note_point().min(127);
let note_lo = self.note_lo().get();
let note_hi = self.note_hi();
if note_point < note_lo {
self.note_lo().set(note_point);
} else if note_point > note_hi {
self.note_lo().set((note_lo + note_point).saturating_sub(note_hi));
}
}
/// Make sure range is within display
fn autozoom (&self) {
let time_len = self.time_len().get();
let time_axis = self.time_axis().get();
let time_zoom = self.time_zoom().get();
//while time_len.div_ceil(time_zoom) > time_axis {
//println!("\r{time_len} {time_zoom} {time_axis}");
//time_zoom = Note::next(time_zoom);
//}
//self.time_zoom().set(time_zoom);
}
}

View file

@ -55,13 +55,11 @@ pub enum GrooveboxCommand {
handle!(<Tui>|self: GrooveboxTui, input|GrooveboxCommand::execute_with_state(self, input));
input_to_command!(GrooveboxCommand: <Tui>|state: GrooveboxTui,input|match input.event() {
key_pat!(Up) |
key_pat!(Down) |
key_pat!(Left) |
key_pat!(Right) =>
GrooveboxCommand::Sampler(SamplerCommand::input_to_command(&state.sampler, input)?),
key_pat!(Up) | key_pat!(Down) | key_pat!(Left) | key_pat!(Right) |
key_pat!(Shift-Char('L')) =>
SamplerCommand::input_to_command(&state.sampler, input).map(Self::Sampler)?,
_ =>
GrooveboxCommand::Sequencer(SequencerCommand::input_to_command(&state.sequencer, input)?),
SequencerCommand::input_to_command(&state.sequencer, input).map(Self::Sequencer)?,
});
command!(|self:GrooveboxCommand,state:GrooveboxTui|match self {

View file

@ -62,27 +62,25 @@ render!(<Tui>|self: SamplerTui|{
let fg = self.color.base.rgb;
let bg = self.color.darkest.rgb;
let border = Fill::wh(Outer(Style::default().fg(fg).bg(bg)));
let inset = 0;
let with_border = |x|lay!([border, Tui::inset_xy(inset, inset, Fill::wh(&x))]);
let with_border = |x|lay!([border, Fill::wh(&x)]);
let with_size = |x|lay!([self.size, x]);
Tui::bg(bg, Fill::wh(with_border(Bsp::s(
Tui::push_x(1, Tui::fg(self.color.light.rgb, Tui::bold(true, "Sampler"))),
Tui::fg(self.color.light.rgb, Tui::bold(true, "Sampler")),
with_size(Tui::shrink_y(1, Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(render(|to: &mut TuiOutput|Ok({
let x = to.area.x() + 1;
let rows = self.size.h() as u16;
let x = to.area.x();
let bg_base = self.color.darkest.rgb;
let bg_selected = self.color.darker.rgb;
let style_empty = Style::default().fg(self.color.base.rgb);
let style_full = Style::default().fg(self.color.lighter.rgb);
let note_hi = self.note_hi();
let note_pt = self.note_point();
for y in 0..rows {
for y in 0..self.size.h() {
let note = note_hi - y as usize;
let bg = if note == note_pt { bg_selected } else { bg_base };
let style = Some(style_empty.bg(bg));
to.blit(&" (no sample) ", x, to.area.y() + y, style)
to.blit(&" (no sample) ", x, to.area.y() + y as u16, style)
}
})))
))),
@ -135,7 +133,9 @@ input_to_command!(SamplerCommand: <Tui>|state: SamplerTui, input|match state.mod
),
_ => match input.event() {
// load sample
key_pat!(Char('l')) => Self::Import(FileBrowserCommand::Begin),
key_pat!(Shift-Char('L')) => {
Self::Import(FileBrowserCommand::Begin)
},
key_pat!(KeyCode::Up) => {
Self::SelectNote(state.note_point().overflowing_add(1).0.min(127))
},