mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
separate input handling for sampler
This commit is contained in:
parent
b58fbdfd30
commit
b376d75396
11 changed files with 114 additions and 36 deletions
11
crates/app/edn/sampler_keys.edn
Normal file
11
crates/app/edn/sampler_keys.edn
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
(@up select :sample-up)
|
||||||
|
(@w select :sample-up)
|
||||||
|
|
||||||
|
(@down select :sample-down)
|
||||||
|
(@s select :sample-down)
|
||||||
|
|
||||||
|
(@left select :sample-left)
|
||||||
|
(@a select :sample-left)
|
||||||
|
|
||||||
|
(@right select :sample-right)
|
||||||
|
(@d select :sample-right)
|
||||||
|
|
@ -543,27 +543,55 @@ impose!([app: Tek] {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
handle!(TuiIn: |self: Tek, input|Ok({
|
handle!(TuiIn: |self: Tek, input|if let Some(handler) = self.handler {
|
||||||
|
handler(self, input)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn handle_arranger (app: &mut Tek, input: &TuiIn) -> Perhaps<bool> {
|
||||||
|
|
||||||
// If editing, editor keys take priority
|
// If editing, editor keys take priority
|
||||||
if self.is_editing() {
|
if app.is_editing() {
|
||||||
if self.editor.handle(input)? == Some(true) {
|
if app.editor.handle(input)? == Some(true) {
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle from root keymap
|
// Handle from root keymap
|
||||||
if let Some(command) = self.keys.command::<_, TekCommand, _>(self, input) {
|
if let Some(command) = SourceIter(include_str!("../edn/arranger_keys.edn"))
|
||||||
if let Some(undo) = command.execute(self)? { self.history.push(undo); }
|
.command::<_, TekCommand, _>(app, input)
|
||||||
|
{
|
||||||
|
if let Some(undo) = command.execute(app)? { app.history.push(undo); }
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle from selection-dependent keymaps
|
// Handle from selection-dependent keymaps
|
||||||
if let Some(command) = match self.selected() {
|
if let Some(command) = match app.selected() {
|
||||||
Selection::Clip(_, _) => self.keys_clip,
|
Selection::Clip(_, _) => SourceIter(include_str!("../edn/arranger_keys_clip.edn")),
|
||||||
Selection::Track(_) => self.keys_track,
|
Selection::Track(_) => SourceIter(include_str!("../edn/arranger_keys_track.edn")),
|
||||||
Selection::Scene(_) => self.keys_scene,
|
Selection::Scene(_) => SourceIter(include_str!("../edn/arranger_keys_scene.edn")),
|
||||||
Selection::Mix => self.keys_mix,
|
Selection::Mix => SourceIter(include_str!("../edn/arranger_keys_mix.edn")),
|
||||||
}.command::<_, TekCommand, _>(self, input) {
|
}.command::<_, TekCommand, _>(app, input) {
|
||||||
if let Some(undo) = command.execute(self)? { self.history.push(undo); }
|
if let Some(undo) = command.execute(app)? {
|
||||||
|
app.history.push(undo);
|
||||||
|
}
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}));
|
Ok(None)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_sampler (app: &mut Tek, input: &TuiIn) -> Perhaps<bool> {
|
||||||
|
let sampler = app.tracks[0].sampler_mut(0).expect("no sampler");
|
||||||
|
if let Some(command) = SourceIter(include_str!("../edn/sampler_keys.edn"))
|
||||||
|
.command::<_, SamplerCommand, _>(sampler, input)
|
||||||
|
{
|
||||||
|
if let Some(undo) = command.execute(sampler)? {
|
||||||
|
//app.history.push(undo); // TODO UNDO
|
||||||
|
}
|
||||||
|
return Ok(Some(true))
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,17 +59,22 @@ use crate::*;
|
||||||
pub keys_mix: SourceIter<'static>,
|
pub keys_mix: SourceIter<'static>,
|
||||||
// Cache of formatted strings
|
// Cache of formatted strings
|
||||||
pub view_cache: Arc<RwLock<ViewCache>>,
|
pub view_cache: Arc<RwLock<ViewCache>>,
|
||||||
|
// Input handler function
|
||||||
|
pub handler: Option<fn(&mut Self, &TuiIn)->Result<Option<bool>, Box<(dyn std::error::Error + 'static)>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tek {
|
impl Tek {
|
||||||
|
|
||||||
pub(crate) fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
pub(crate) fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
self.scene()?.clips.get(self.selected().track()?)?.clone()
|
self.scene()?.clips.get(self.selected().track()?)?.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn toggle_loop (&mut self) {
|
pub(crate) fn toggle_loop (&mut self) {
|
||||||
if let Some(clip) = self.clip() {
|
if let Some(clip) = self.clip() {
|
||||||
clip.write().unwrap().toggle_loop()
|
clip.write().unwrap().toggle_loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn activate (&mut self) -> Usually<()> {
|
pub(crate) fn activate (&mut self) -> Usually<()> {
|
||||||
let selected = self.selected().clone();
|
let selected = self.selected().clone();
|
||||||
match selected {
|
match selected {
|
||||||
|
|
@ -95,6 +100,7 @@ impl Tek {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tracks_add (
|
pub fn tracks_add (
|
||||||
&mut self, count: usize, width: Option<usize>,
|
&mut self, count: usize, width: Option<usize>,
|
||||||
midi_from: &[PortConnect], midi_to: &[PortConnect],
|
midi_from: &[PortConnect], midi_to: &[PortConnect],
|
||||||
|
|
@ -111,6 +117,7 @@ impl Tek {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn track_add (
|
pub fn track_add (
|
||||||
&mut self, name: Option<&str>, color: Option<ItemPalette>,
|
&mut self, name: Option<&str>, color: Option<ItemPalette>,
|
||||||
midi_froms: &[PortConnect],
|
midi_froms: &[PortConnect],
|
||||||
|
|
@ -141,12 +148,14 @@ impl Tek {
|
||||||
}
|
}
|
||||||
Ok((index, &mut self.tracks_mut()[index]))
|
Ok((index, &mut self.tracks_mut()[index]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn track_del (&mut self, index: usize) {
|
pub fn track_del (&mut self, index: usize) {
|
||||||
self.tracks_mut().remove(index);
|
self.tracks_mut().remove(index);
|
||||||
for scene in self.scenes_mut().iter_mut() {
|
for scene in self.scenes_mut().iter_mut() {
|
||||||
scene.clips.remove(index);
|
scene.clips.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scenes_add (&mut self, n: usize) -> Usually<()> {
|
pub fn scenes_add (&mut self, n: usize) -> Usually<()> {
|
||||||
let scene_color_1 = ItemColor::random();
|
let scene_color_1 = ItemColor::random();
|
||||||
let scene_color_2 = ItemColor::random();
|
let scene_color_2 = ItemColor::random();
|
||||||
|
|
@ -157,6 +166,7 @@ impl Tek {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||||
-> Usually<(usize, &mut Scene)>
|
-> Usually<(usize, &mut Scene)>
|
||||||
{
|
{
|
||||||
|
|
@ -169,9 +179,11 @@ impl Tek {
|
||||||
let index = self.scenes().len() - 1;
|
let index = self.scenes().len() - 1;
|
||||||
Ok((index, &mut self.scenes_mut()[index]))
|
Ok((index, &mut self.scenes_mut()[index]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scene_default_name (&self) -> Arc<str> {
|
pub fn scene_default_name (&self) -> Arc<str> {
|
||||||
format!("Sc{:3>}", self.scenes().len() + 1).into()
|
format!("Sc{:3>}", self.scenes().len() + 1).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
has_size!(<TuiOut>|self: Tek|&self.size);
|
has_size!(<TuiOut>|self: Tek|&self.size);
|
||||||
|
|
@ -385,6 +397,19 @@ impl Track {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
pub fn sampler_mut (&mut self, mut nth: usize) -> Option<&mut Sampler> {
|
||||||
|
for device in self.devices.iter_mut() {
|
||||||
|
match device {
|
||||||
|
Device::Sampler(s) => if nth == 0 {
|
||||||
|
return Some(s);
|
||||||
|
} else {
|
||||||
|
nth -= 1;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasTracks for Tek {
|
impl HasTracks for Tek {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
pub(crate) use tek::*;
|
pub(crate) use tek::*;
|
||||||
pub(crate) use std::sync::{Arc, RwLock};
|
pub(crate) use std::sync::{Arc, RwLock};
|
||||||
pub(crate) use clap::{self, Parser, Subcommand};
|
pub(crate) use clap::{self, Parser, Subcommand};
|
||||||
|
|
||||||
/// Application entrypoint.
|
/// Application entrypoint.
|
||||||
pub fn main () -> Usually<()> {
|
pub fn main () -> Usually<()> {
|
||||||
Cli::parse().run()
|
Cli::parse().run()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(version, about = Some(HEADER), long_about = Some(HEADER))]
|
#[command(version, about = Some(HEADER), long_about = Some(HEADER))]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
|
|
@ -37,7 +39,10 @@ pub struct Cli {
|
||||||
/// Audio ins to connect from right output
|
/// Audio ins to connect from right output
|
||||||
#[arg(short='R', long)] right_to: Vec<String>,
|
#[arg(short='R', long)] right_to: Vec<String>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Subcommand)] pub enum Mode {
|
|
||||||
|
/// Application modes
|
||||||
|
#[derive(Debug, Clone, Subcommand)]
|
||||||
|
pub enum Mode {
|
||||||
/// ⏯️ A standalone transport clock.
|
/// ⏯️ A standalone transport clock.
|
||||||
Clock,
|
Clock,
|
||||||
/// 🎼 A MIDI sequencer.
|
/// 🎼 A MIDI sequencer.
|
||||||
|
|
@ -62,6 +67,7 @@ pub struct Cli {
|
||||||
/// TODO: An audio plugin host
|
/// TODO: An audio plugin host
|
||||||
Plugin,
|
Plugin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cli {
|
impl Cli {
|
||||||
pub fn run (&self) -> Usually<()> {
|
pub fn run (&self) -> Usually<()> {
|
||||||
let name = self.name.as_ref().map_or("tek", |x|x.as_str());
|
let name = self.name.as_ref().map_or("tek", |x|x.as_str());
|
||||||
|
|
@ -122,11 +128,12 @@ impl Cli {
|
||||||
},
|
},
|
||||||
color: ItemPalette::random(),
|
color: ItemPalette::random(),
|
||||||
clock: Clock::new(jack, self.bpm)?,
|
clock: Clock::new(jack, self.bpm)?,
|
||||||
keys: SourceIter(include_str!("./edn/arranger_keys.edn")),
|
|
||||||
keys_clip: SourceIter(include_str!("./edn/arranger_keys_clip.edn")),
|
handler: Some(match mode {
|
||||||
keys_track: SourceIter(include_str!("./edn/arranger_keys_track.edn")),
|
Mode::Sampler => handle_sampler,
|
||||||
keys_scene: SourceIter(include_str!("./edn/arranger_keys_scene.edn")),
|
_ => handle_arranger,
|
||||||
keys_mix: SourceIter(include_str!("./edn/arranger_keys_mix.edn")),
|
}),
|
||||||
|
|
||||||
tracks: match mode {
|
tracks: match mode {
|
||||||
|
|
||||||
Mode::Sequencer => vec![
|
Mode::Sequencer => vec![
|
||||||
|
|
@ -196,6 +203,7 @@ impl Cli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CLI header
|
||||||
const HEADER: &'static str = r#"
|
const HEADER: &'static str = r#"
|
||||||
|
|
||||||
░▒▓████████▓▒░▒▓███████▓▒░▒▓█▓▒░░▒▓█▓▒░░
|
░▒▓████████▓▒░▒▓███████▓▒░▒▓█▓▒░░▒▓█▓▒░░
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
provide!(Arc<str>: |self: Sampler| {});
|
|
||||||
provide!(Option<Arc<RwLock<Sample>>>: |self: Sampler| {});
|
expose! {
|
||||||
provide!(PathBuf: |self: Sampler| {});
|
[self: Sampler] {
|
||||||
provide!(f32: |self: Sampler| {});
|
[Arc<str>] => {}
|
||||||
provide!(u7: |self: Sampler| {});
|
[Option<Arc<RwLock<Sample>>>] => {}
|
||||||
provide!(usize: |self: Sampler| {});
|
[PathBuf] => {}
|
||||||
|
[f32] => {}
|
||||||
|
[u7] => {}
|
||||||
|
[usize] => {
|
||||||
|
":sample-up" => 0,
|
||||||
|
":sample-down" => 0,
|
||||||
|
":sample-left" => 0,
|
||||||
|
":sample-right" => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defcom! { |self, state: Sampler|
|
defcom! { |self, state: Sampler|
|
||||||
|
|
||||||
|
|
@ -82,17 +92,13 @@ atom_command!(FileBrowserCommand: |state: Sampler| {
|
||||||
("select" [i: usize] Some(Self::Select(i.expect("no index"))))
|
("select" [i: usize] Some(Self::Select(i.expect("no index"))))
|
||||||
("chdir" [p: PathBuf] Some(Self::Chdir(p.expect("no path"))))
|
("chdir" [p: PathBuf] Some(Self::Chdir(p.expect("no path"))))
|
||||||
("filter" [f: Arc<str>] Some(Self::Filter(f.expect("no filter")))) });
|
("filter" [f: Arc<str>] Some(Self::Filter(f.expect("no filter")))) });
|
||||||
|
|
||||||
atom_command!(SamplerCommand: |state: Sampler| {
|
atom_command!(SamplerCommand: |state: Sampler| {
|
||||||
("select" [i: usize]
|
("import" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Import))
|
||||||
Some(Self::Select(i.expect("no index"))))
|
("select" [i: usize] Some(Self::Select(i.expect("no index"))))
|
||||||
("import" [,..a]
|
("record/begin" [i: u7] Some(Self::RecordBegin(i.expect("no index"))))
|
||||||
FileBrowserCommand::try_from_expr(state, a).map(Self::Import))
|
("record/cancel" [] Some(Self::RecordCancel))
|
||||||
("record/begin" [i: u7]
|
("record/finish" [] Some(Self::RecordFinish))
|
||||||
Some(Self::RecordBegin(i.expect("no index"))))
|
|
||||||
("record/cancel" []
|
|
||||||
Some(Self::RecordCancel))
|
|
||||||
("record/finish" []
|
|
||||||
Some(Self::RecordFinish))
|
|
||||||
("set/sample" [i: u7, s: Option<Arc<RwLock<Sample>>>]
|
("set/sample" [i: u7, s: Option<Arc<RwLock<Sample>>>]
|
||||||
Some(Self::SetSample(i.expect("no index"), s.expect("no sampler"))))
|
Some(Self::SetSample(i.expect("no index"), s.expect("no sampler"))))
|
||||||
("set/start" [i: u7, s: usize]
|
("set/start" [i: u7, s: usize]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue