Compare commits

..

No commits in common. "cdeb355972a8f31f0b7d60182141c005038888e4" and "997d67a4879330af8d2ccb0a30f7c0efeb7bb3ee" have entirely different histories.

11 changed files with 95 additions and 130 deletions

View file

@ -3,16 +3,15 @@
(info "A sequencer with built-in sampler.") (info "A sequencer with built-in sampler.")
(view (view
(bsp/a :view-dialog (bsp/a :view-dialog
(bsp/s (fixed/y 1 :view-transport) (bsp/s (fixed/y 1 :view-transport)
(bsp/n (fixed/y 1 :view-status) (bsp/n (fixed/y 1 :view-status)
(bsp/w :view-meters-output (bsp/w :view-meters-output
(bsp/e :view-meters-input (bsp/e :view-meters-input
(bsp/n :view-sample-info (bsp/n (fixed/y 5 :view-sample-viewer)
(bsp/n (fixed/y 5 :view-sample-viewer) (bsp/w (fixed/x :w-sidebar :view-pool)
(bsp/w (fixed/x :w-sidebar :view-pool) (bsp/e :view-samples-keys
(bsp/e :view-samples-keys (fill/y :view-editor))))))))))
(fill/y :view-editor)))))))))))
(keys (keys
(layer-if :focus-pool-import "./keys_pool_file.edn") (layer-if :focus-pool-import "./keys_pool_file.edn")

View file

@ -1,24 +1,25 @@
(@left editor set-time-pos :time-pos-prev) (@up editor note-pos :note-pos-next)
(@right editor set-time-pos :time-pos-next) (@down editor note-pos :note-pos-prev)
(@pgup editor note-pos :note-pos-next-octave)
(@pgdn editor note-pos :note-pos-prev-octave)
(@equal editor set-time-zoom :time-zoom-prev) (@comma editor note-len :note-len-prev)
(@minus editor set-time-zoom :time-zoom-next) (@period editor note-len :note-len-next)
(@plus editor set-time-zoom :time-zoom-next-fine) (@lt editor note-len :note-len-prev)
(@underscore editor set-time-zoom :time-zoom-prev-fine) (@gt editor note-len :note-len-next)
(@z editor set-time-lock) (@plus editor note-range :note-range-next)
(@underscore editor note-range :note-range-prev)
(@up editor set-note-pos :note-pos-next) (@left editor time-pos :time-pos-prev)
(@down editor set-note-pos :note-pos-prev) (@right editor time-pos :time-pos-next)
(@pgup editor set-note-pos :note-pos-next-octave)
(@pgdn editor set-note-pos :note-pos-prev-octave)
(@comma editor set-note-len :note-len-prev) (@equal editor time-zoom :time-zoom-prev)
(@period editor set-note-len :note-len-next) (@minus editor time-zoom :time-zoom-next)
(@lt editor set-note-len :note-len-prev)
(@gt editor set-note-len :note-len-next)
(@a editor append-note :true) (@z editor time-lock)
(@enter editor append-note :false)
(@del editor delete-note) (@enter editor note-put)
(@shift-del editor delete-note) (@shift-enter editor note-append)
(@del editor note-del)
(@shift-del editor note-del)

View file

@ -1,5 +1,5 @@
(@esc toggle-menu) (@esc menu)
(@f1 toggle-help) (@f1 help)
(@u undo 1) (@u undo 1)
(@shift-u redo 1) (@shift-u redo 1)

View file

@ -203,11 +203,11 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
} }
#[tengri_proc::command(App)] impl AppCommand { #[tengri_proc::command(App)] impl AppCommand {
fn toggle_help (app: &mut App) -> Perhaps<Self> { fn toggle_help (app: &mut App, value: bool) -> Perhaps<Self> {
app.toggle_dialog(Some(Dialog::Help)); app.toggle_dialog(Some(Dialog::Help));
Ok(None) Ok(None)
} }
fn toggle_menu (app: &mut App) -> Perhaps<Self> { fn toggle_menu (app: &mut App, value: bool) -> Perhaps<Self> {
app.toggle_dialog(Some(Dialog::Menu)); app.toggle_dialog(Some(Dialog::Menu));
Ok(None) Ok(None)
} }
@ -280,7 +280,7 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
// update linked sampler after editor action // update linked sampler after editor action
app.sampler_mut().map(|sampler|match command { app.sampler_mut().map(|sampler|match command {
// autoselect: automatically select sample in sampler // autoselect: automatically select sample in sampler
MidiEditCommand::SetNotePos { pos } => { sampler.set_note_pos(pos); }, MidiEditCommand::NotePos { pos } => { sampler.set_note_pos(pos); },
_ => {} _ => {}
}); });
undo undo

View file

@ -41,15 +41,6 @@ impl App {
pub fn view_sample_viewer (&self) -> impl Content<TuiOut> + use<'_> { pub fn view_sample_viewer (&self) -> impl Content<TuiOut> + use<'_> {
self.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos())) self.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos()))
} }
pub fn view_sample_info (&self) -> impl Content<TuiOut> + use<'_> {
self.sampler().map(|s|s.view_sample_info(self.editor().unwrap().get_note_pos()))
}
pub fn view_meters_input (&self) -> impl Content<TuiOut> + use<'_> {
self.sampler().map(|s|s.view_meters_input())
}
pub fn view_meters_output (&self) -> impl Content<TuiOut> + use<'_> {
self.sampler().map(|s|s.view_meters_output())
}
pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> { pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
When::new(self.dialog.is_some(), Bsp::b( When::new(self.dialog.is_some(), Bsp::b(
Fill::xy(Tui::fg_bg(Rgb(64,64,64), Rgb(32,32,32), "")), Fill::xy(Tui::fg_bg(Rgb(64,64,64), Rgb(32,32,32), "")),
@ -65,6 +56,12 @@ impl App {
))) )))
)) ))
} }
pub fn view_meters_input (&self) -> impl Content<TuiOut> + use<'_> {
self.sampler().map(|s|s.view_meters_input())
}
pub fn view_meters_output (&self) -> impl Content<TuiOut> + use<'_> {
self.sampler().map(|s|s.view_meters_output())
}
} }
impl App { impl App {

View file

@ -111,10 +111,10 @@ impl Cli {
_ => todo!("{mode:?}"), _ => todo!("{mode:?}"),
}; };
let config = Configuration::new(&config_path, false)?; let config = Configuration::new(&config_path, false)?;
let clock = Clock::new(jack, self.bpm)?;
let mut app = App { let mut app = App {
jack: jack.clone(), jack: jack.clone(),
color: ItemTheme::random(), color: ItemTheme::random(),
clock: Clock::new(jack, self.bpm)?,
pool: match mode { pool: match mode {
LaunchMode::Sequencer | LaunchMode::Groovebox => clip.as_ref().map(Into::into), LaunchMode::Sequencer | LaunchMode::Groovebox => clip.as_ref().map(Into::into),
LaunchMode::Arranger { .. } => Some(Default::default()), LaunchMode::Arranger { .. } => Some(Default::default()),
@ -141,7 +141,7 @@ impl Cli {
&name, &name,
None, None,
jack, jack,
Some(&clock), None,
clip.as_ref(), clip.as_ref(),
midi_froms.as_slice(), midi_froms.as_slice(),
midi_tos.as_slice() midi_tos.as_slice()
@ -152,7 +152,7 @@ impl Cli {
&name, &name,
None, None,
jack, jack,
Some(&clock), None,
clip.as_ref(), clip.as_ref(),
midi_froms.as_slice(), midi_froms.as_slice(),
midi_froms.as_slice(), midi_froms.as_slice(),
@ -165,7 +165,6 @@ impl Cli {
scenes, scenes,
selected: Selection::TrackClip { track: 0, scene: 0 }, selected: Selection::TrackClip { track: 0, scene: 0 },
config, config,
clock,
..Default::default() ..Default::default()
}; };
if let &LaunchMode::Arranger { scenes, tracks, track_width, .. } = mode { if let &LaunchMode::Arranger { scenes, tracks, track_width, .. } = mode {

View file

@ -4,6 +4,12 @@ use crate::*;
fn _todo_opt_clip_stub (&self) -> Option<Arc<RwLock<MidiClip>>> { fn _todo_opt_clip_stub (&self) -> Option<Arc<RwLock<MidiClip>>> {
todo!() todo!()
} }
fn time_lock (&self) -> bool {
self.get_time_lock()
}
fn time_lock_toggled (&self) -> bool {
!self.get_time_lock()
}
fn note_length (&self) -> usize { fn note_length (&self) -> usize {
self.get_note_len() self.get_note_len()
@ -49,10 +55,10 @@ use crate::*;
self.get_time_pos() self.get_time_pos()
} }
fn time_pos_next (&self) -> usize { fn time_pos_next (&self) -> usize {
self.get_time_pos() + self.get_note_len() self.get_time_pos() + self.time_zoom()
} }
fn time_pos_prev (&self) -> usize { fn time_pos_prev (&self) -> usize {
self.get_time_pos().saturating_sub(self.get_note_len()) self.get_time_pos().saturating_sub(self.time_zoom())
} }
fn time_zoom (&self) -> usize { fn time_zoom (&self) -> usize {
@ -61,37 +67,29 @@ use crate::*;
fn time_zoom_next (&self) -> usize { fn time_zoom_next (&self) -> usize {
self.get_time_zoom() + 1 self.get_time_zoom() + 1
} }
fn time_zoom_next_fine (&self) -> usize {
self.get_time_zoom() + 1
}
fn time_zoom_prev (&self) -> usize { fn time_zoom_prev (&self) -> usize {
self.get_time_zoom().saturating_sub(1).max(1) self.get_time_zoom().saturating_sub(1).max(1)
} }
fn time_zoom_prev_fine (&self) -> usize {
self.get_time_zoom().saturating_sub(1).max(1)
}
fn time_lock (&self) -> bool {
self.get_time_lock()
}
fn time_lock_toggled (&self) -> bool {
!self.get_time_lock()
}
} }
#[tengri_proc::command(MidiEditor)] impl MidiEditCommand { #[tengri_proc::command(MidiEditor)] impl MidiEditCommand {
fn append_note (editor: &mut MidiEditor, advance: bool) -> Perhaps<Self> { // TODO: 1-9 seek markers that by default start every 8th of the clip
editor.put_note(advance); fn note_append (editor: &mut MidiEditor) -> Perhaps<Self> {
editor.put_note(true);
Ok(None) Ok(None)
} }
fn delete_note (_editor: &mut MidiEditor) -> Perhaps<Self> { fn note_put (editor: &mut MidiEditor) -> Perhaps<Self> {
editor.put_note(false);
Ok(None)
}
fn note_del (_editor: &mut MidiEditor) -> Perhaps<Self> {
todo!() todo!()
} }
fn set_note_pos (editor: &mut MidiEditor, pos: usize) -> Perhaps<Self> { fn note_pos (editor: &mut MidiEditor, pos: usize) -> Perhaps<Self> {
editor.set_note_pos(pos.min(127)); editor.set_note_pos(pos.min(127));
Ok(None) Ok(None)
} }
fn set_note_len (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> { fn note_len (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> {
//let note_len = editor.get_note_len(); //let note_len = editor.get_note_len();
//let time_zoom = editor.get_time_zoom(); //let time_zoom = editor.get_time_zoom();
editor.set_note_len(value); editor.set_note_len(value);
@ -100,24 +98,24 @@ use crate::*;
//} //}
Ok(None) Ok(None)
} }
fn set_note_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> { fn note_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> {
editor.set_note_lo(value.min(127)); editor.set_note_lo(value.min(127));
Ok(None) Ok(None)
} }
fn set_time_pos (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> { fn time_pos (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> {
editor.set_time_pos(value); editor.set_time_pos(value);
Ok(None) Ok(None)
} }
fn set_time_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> { fn time_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> {
editor.set_time_start(value); editor.set_time_start(value);
Ok(None) Ok(None)
} }
fn set_time_zoom (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> { fn time_zoom (editor: &mut MidiEditor, value: usize) -> Perhaps<Self> {
editor.set_time_zoom(value); editor.set_time_zoom(value);
editor.redraw(); editor.redraw();
Ok(None) Ok(None)
} }
fn set_time_lock (editor: &mut MidiEditor, value: bool) -> Perhaps<Self> { fn time_lock (editor: &mut MidiEditor, value: bool) -> Perhaps<Self> {
editor.set_time_lock(value); editor.set_time_lock(value);
Ok(None) Ok(None)
} }
@ -125,5 +123,4 @@ use crate::*;
editor.set_clip(clip.as_ref()); editor.set_clip(clip.as_ref());
Ok(None) Ok(None)
} }
// TODO: 1-9 seek markers that by default start every 8th of the clip
} }

View file

@ -61,7 +61,7 @@ impl MidiEditor {
clip.notes[note_end].push(note_off); clip.notes[note_end].push(note_off);
} }
if advance { if advance {
self.set_time_pos(note_end + 1); self.set_time_pos(note_end);
} }
redraw = true; redraw = true;
} }

View file

@ -4,7 +4,7 @@ use crate::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Sampler { pub struct Sampler {
/// Name of sampler. /// Name of sampler.
pub name: Arc<str>, pub name: String,
/// Device color. /// Device color.
pub color: ItemTheme, pub color: ItemTheme,
/// Audio input ports. Samples get recorded here. /// Audio input ports. Samples get recorded here.
@ -55,7 +55,7 @@ impl Default for Sampler {
input_meters: vec![0.0;2], input_meters: vec![0.0;2],
output_meters: vec![0.0;2], output_meters: vec![0.0;2],
audio_outs: vec![], audio_outs: vec![],
name: "tek_sampler".into(), name: "tek_sampler".to_string(),
mapped: [const { None };128], mapped: [const { None };128],
unmapped: vec![], unmapped: vec![],
voices: Arc::new(RwLock::new(vec![])), voices: Arc::new(RwLock::new(vec![])),

View file

@ -100,46 +100,33 @@ impl Sampler {
}))) })))
} }
pub fn view_sample_info (&self, note_pt: usize) -> impl Content<TuiOut> + use<'_> { pub fn status (&self, index: usize) -> impl Content<TuiOut> {
Fill::x(Fixed::y(1, draw_info(if let Some((_, sample)) = &self.recording {
Some(sample)
} else if let Some(sample) = &self.mapped[note_pt] {
Some(sample)
} else {
None
})))
}
pub fn view_status (&self, index: usize) -> impl Content<TuiOut> {
draw_status(self.mapped[index].as_ref()) draw_status(self.mapped[index].as_ref())
} }
pub fn view_meters_input (&self) -> impl Content<TuiOut> + use<'_> { pub fn view_meters_input (&self) -> impl Content<TuiOut> + use<'_> {
draw_meters(&self.input_meters) Tui::bg(Black, Fixed::x(2, Map::east(1, ||self.input_meters.iter(), |value, _index|{
Fill::y(RmsMeter(*value))
})))
} }
pub fn view_meters_output (&self) -> impl Content<TuiOut> + use<'_> { pub fn view_meters_output (&self) -> impl Content<TuiOut> + use<'_> {
draw_meters(&self.output_meters) Tui::bg(Black, Fixed::x(2, Map::east(1, ||self.output_meters.iter(), |value, _index|{
Fill::y(RmsMeter(*value))
})))
} }
} }
fn draw_meters (meters: &[f32]) -> impl Content<TuiOut> + use<'_> {
Tui::bg(Black, Fixed::x(2, Map::east(1, ||meters.iter(), |value, _index|{
Fill::y(RmsMeter(*value))
})))
}
fn draw_list_item (sample: &Option<Arc<RwLock<Sample>>>) -> String { fn draw_list_item (sample: &Option<Arc<RwLock<Sample>>>) -> String {
if let Some(sample) = sample { if let Some(sample) = sample {
let sample = sample.read().unwrap(); let sample = sample.read().unwrap();
format!("{:8}", sample.name) format!("{:8} {:3} {:6}-{:6}/{:6}",
//format!("{:8} {:3} {:6}-{:6}/{:6}", sample.name,
//sample.name, sample.gain,
//sample.gain, sample.start,
//sample.start, sample.end,
//sample.end, sample.channels[0].len()
//sample.channels[0].len() )
//)
} else { } else {
String::from("........") String::from("........")
} }
@ -200,21 +187,6 @@ fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> +
}) })
} }
fn draw_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> + use<'_> {
When(sample.is_some(), Thunk::new(move||{
let sample = sample.unwrap().read().unwrap();
let theme = ItemTheme::G[96];
row!(
FieldH(theme, "Name", format!("{:<10}", sample.name.clone())),
FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())),
FieldH(theme, "Start", format!("{:<8}", sample.start)),
FieldH(theme, "End", format!("{:<8}", sample.end)),
FieldH(theme, "Transpose", " 0 "),
FieldH(theme, "Gain", format!("{}", sample.gain)),
)
}))
}
fn draw_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> { fn draw_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> {
Tui::bold(true, Tui::fg(Tui::g(224), sample Tui::bold(true, Tui::fg(Tui::g(224), sample
.map(|sample|{ .map(|sample|{

View file

@ -18,29 +18,29 @@ pub trait HasSequencer {
/// Contains state for playing a clip /// Contains state for playing a clip
pub struct Sequencer { pub struct Sequencer {
/// State of clock and playhead /// State of clock and playhead
pub clock: Clock, pub clock: Clock,
/// Start time and clip being played /// Start time and clip being played
pub play_clip: Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>, pub play_clip: Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>,
/// Start time and next clip /// Start time and next clip
pub next_clip: Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>, pub next_clip: Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>,
/// Play input through output. /// Play input through output.
pub monitoring: bool, pub monitoring: bool,
/// Write input to sequence. /// Write input to sequence.
pub recording: bool, pub recording: bool,
/// Overdub input to sequence. /// Overdub input to sequence.
pub overdub: bool, pub overdub: bool,
/// Send all notes off /// Send all notes off
pub reset: bool, // TODO?: after Some(nframes) pub reset: bool, // TODO?: after Some(nframes)
/// Record from MIDI ports to current sequence. /// Record from MIDI ports to current sequence.
pub midi_ins: Vec<JackMidiIn>, pub midi_ins: Vec<JackMidiIn>,
/// Play from current sequence to MIDI ports /// Play from current sequence to MIDI ports
pub midi_outs: Vec<JackMidiOut>, pub midi_outs: Vec<JackMidiOut>,
/// Notes currently held at input /// Notes currently held at input
pub notes_in: Arc<RwLock<[bool; 128]>>, pub notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output /// Notes currently held at output
pub notes_out: Arc<RwLock<[bool; 128]>>, pub notes_out: Arc<RwLock<[bool; 128]>>,
/// MIDI output buffer /// MIDI output buffer
pub note_buf: Vec<u8>, pub note_buf: Vec<u8>,
} }
impl Default for Sequencer { impl Default for Sequencer {