From ee7f9dcf125f12a81225e3543a344517c1b48d7a Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 8 May 2025 13:50:35 +0300 Subject: [PATCH 1/4] wip: update all command definitions to use proc macro --- crates/app/src/api.rs | 808 ++++++++++++++------------- crates/device/src/clock/clock_api.rs | 81 ++- deps/tengri | 2 +- 3 files changed, 455 insertions(+), 436 deletions(-) diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index 7d5d0851..b0db4c6f 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -256,111 +256,142 @@ impl MidiEditor { } } -//#[tengri_proc::input(TuiIn)] -//impl Tek { - //#[tengri::command("sampler", TekCommand::Sampler)] - //fn cmd_sampler (&mut self, cmd: SamplerCommand) -> Perhaps { - //self.sampler_mut().map(|s|cmd.delegate(s, Self::Sampler)).transpose()?.flatten()) - //} - //#[tengri::command("scene", TekCommand::Scene)] - //fn cmd_scene (&mut self, cmd: SceneCommand) -> Perhaps { - //cmd.delegate(self, scene) - //} -//} - #[tengri_proc::command(Tek)] impl TekCommand { - fn toggle_help (&self, state: &mut Tek, value: Option) -> Option { - //(ToggleHelp [] cmd!(app.toggle_dialog(Some(Dialog::Help)))) - None + fn toggle_help (&self, tek: &mut Tek, value: bool) -> Perhaps { + tek.toggle_dialog(Some(Dialog::Help)); + Ok(None) } - fn toggle_menu (&self, state: &mut Tek, value: Option) -> Option { - //(ToggleMenu [] cmd!(app.toggle_dialog(Some(Dialog::Menu)))) - None + fn toggle_menu (&self, tek: &mut Tek, value: bool) -> Perhaps { + tek.toggle_dialog(Some(Dialog::Menu)); + Ok(None) } - fn toggle_edit (&self, state: &mut Tek, value: Option) -> Option { - //(Edit [value: Option] cmd!(app.toggle_editor(value))) - None + fn toggle_edit (&self, tek: &mut Tek, value: bool) -> Perhaps { + tek.toggle_editor(Some(value)); + Ok(None) } - //(Sampler [cmd: SamplerCommand] app.sampler_mut().map(|s|cmd.delegate(s, Self::Sampler)).transpose()?.flatten()) - //(Scene [cmd: SceneCommand] cmd.delegate(app, Self::Scene)?) - //(Track [cmd: TrackCommand] cmd.delegate(app, Self::Track)?) - //(Output [cmd: OutputCommand] cmd.delegate(app, Self::Output)?) - //(Input [cmd: InputCommand] cmd.delegate(app, Self::Input)?) - //(Clip [cmd: ClipCommand] cmd.delegate(app, Self::Clip)?) - //(Clock [cmd: ClockCommand] cmd.delegate(app, Self::Clock)?) - //(Device [cmd: DeviceCommand] cmd.delegate(app, Self::Device)?) - //(Message [cmd: MessageCommand] cmd.delegate(app, Self::Message)?) - //(Editor [cmd: MidiEditCommand] delegate_to_editor(app, cmd)?) - //(Pool [cmd: PoolCommand] delegate_to_pool(app, cmd)?) - //(Color [p: ItemTheme] app.set_color(Some(p)).map(Self::Color)) - //(Enqueue [c: MaybeClip] cmd_todo!("\n\rtodo: enqueue {c:?}")) - //(History [d: isize] cmd_todo!("\n\rtodo: history {d:?}")) - //(Zoom [z: Option] cmd_todo!("\n\rtodo: zoom {z:?}")) - //(Launch [] cmd!(app.launch())) - //(Select [s: Selection] cmd!(app.select(s))) - //(StopAll [] cmd!(app.stop_all()))) - //("menu" [] Some(Self::ToggleMenu)) - //("help" [] Some(Self::ToggleHelp)) - //("stop" [] Some(Self::StopAll)) - //("undo" [d: usize] Some(Self::History(-(d.unwrap_or(0) as isize)))) - //("redo" [d: usize] Some(Self::History(d.unwrap_or(0) as isize))) - //("zoom" [z: usize] Some(Self::Zoom(z))) - //("edit" [] Some(Self::Edit(None))) - //("edit" [c: bool] Some(Self::Edit(c))) - //("color" [] Some(Self::Color(ItemTheme::random()))) - //("color" [c: Color] Some(Self::Color(c.map(ItemTheme::from).expect("no color")))) - //("enqueue" [c: Arc>] Some(Self::Enqueue(c))) - //("launch" [] Some(Self::Launch)) - //("select" [t: Selection] Some(t.map(Self::Select).expect("no selection"))) - //("clock" [,..a] ns!(ClockCommand, app.clock(), a, Self::Clock)) - //("scene" [,..a] ns!(SceneCommand, app, a, Self::Scene)) - //("track" [,..a] ns!(TrackCommand, app, a, Self::Track)) - //("input" [,..a] ns!(InputCommand, app, a, Self::Input)) - //("output" [,..a] ns!(OutputCommand, app, a, Self::Output)) - //("clip" [,..a] ns!(ClipCommand, app, a, Self::Clip)) - //("device" [,..a] ns!(DeviceCommand, app, a, Self::Device)) - //("message" [,..a] ns!(MessageCommand, app, a, Self::Message)) - //("pool" [,..a] app.pool.as_ref().map(|p|ns!(PoolCommand, p, a, Self::Pool)).flatten()) - //("editor" [,..a] app.editor().map(|e|ns!(MidiEditCommand, e, a, Self::Editor)).flatten()) - //("sampler" [,..a] app.sampler().map(|s|ns!(SamplerCommand, s, a, Self::Sampler)).flatten()) + fn editor (&self, tek: &mut Tek, command: MidiEditCommand) -> Perhaps { + Ok(tek.editor.as_mut().map(|editor|command.execute(editor)) + .transpose()? + .flatten() + .map(|undo|Self::Editor { command: undo })) + } + fn pool (&self, tek: &mut Tek, command: PoolCommand) -> Perhaps { + Ok(if let Some(pool) = tek.pool.as_mut() { + let undo = command.clone().delegate(pool, |command|TekCommand::Pool{command})?; + // update linked editor after pool action + tek.editor.as_mut().map(|editor|match command { + // autoselect: automatically load selected clip in editor + PoolCommand::Select { .. } | + // autocolor: update color in all places simultaneously + PoolCommand::Clip { command: PoolClipCommand::SetColor { .. } } => + editor.set_clip(pool.clip().as_ref()), + _ => {} + }); + undo + } else { + None + }) + } + fn sampler (&self, tek: &mut Tek, ref command: SamplerCommand) -> Perhaps { + Ok(tek.sampler_mut() + .map(|s|command.delegate(s, |command|Self::Sampler{command})) + .transpose()? + .flatten()) + } + fn scene (&self, tek: &mut Tek, command: SceneCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Scene{command})?) + } + fn track (&self, tek: &mut Tek, command: TrackCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Track{command})?) + } + fn input (&self, tek: &mut Tek, command: InputCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Input{command})?) + } + fn output (&self, tek: &mut Tek, command: OutputCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Output{command})?) + } + fn clip (&self, tek: &mut Tek, command: ClipCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Clip{command})?) + } + fn clock (&self, tek: &mut Tek, command: ClockCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Clock{command})?) + } + fn device (&self, tek: &mut Tek, command: DeviceCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Device{command})?) + } + fn message (&self, tek: &mut Tek, command: MessageCommand) -> Perhaps { + Ok(command.delegate(tek, |command|Self::Message{command})?) + } + fn color (&self, tek: &mut Tek, theme: ItemTheme) -> Perhaps { + Ok(tek.set_color(Some(theme)).map(|theme|Self::Color{theme})) + } + fn enqueue (&self, tek: &mut Tek, clip: Option>>) -> Perhaps { + todo!() + } + fn history (&self, tek: &mut Tek, delta: isize) -> Perhaps { + todo!() + } + fn zoom (&self, tek: &mut Tek, zoom: usize) -> Perhaps { + todo!() + } + fn launch (&self, tek: &mut Tek) -> Perhaps { + tek.launch(); + Ok(None) + } + fn select (&self, tek: &mut Tek, selection: Selection) -> Perhaps { + tek.select(selection); + Ok(None) //("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) { //(0, 0) => Self::Select(Selection::Mix), //(t, 0) => Self::Select(Selection::Track(t)), //(0, s) => Self::Select(Selection::Scene(s)), //(t, s) => Self::Select(Selection::TrackClip { track: t, scene: s }) }))) + } + fn stop_all (&self, tek: &mut Tek) -> Perhaps { + tek.stop_all(); + Ok(None) + } } #[tengri_proc::command(Tek)] impl InputCommand { - fn add (&self, state: &mut Tek) -> Option { - state.midi_in_add()?; - None + fn add (&self, tek: &mut Tek) -> Perhaps { + tek.midi_in_add()?; + Ok(None) } } #[tengri_proc::command(Tek)] impl OutputCommand { - fn add (&self, state: &mut Tek) -> Option { - state.midi_out_add()?; - None + fn add (&self, tek: &mut Tek) -> Perhaps { + tek.midi_out_add()?; + Ok(None) } } #[tengri_proc::command(Tek)] impl DeviceCommand { - //(Picker [] cmd!(app.device_picker_show())) - //(Pick [i: usize] cmd!(app.device_pick(i))) - //(Add [i: usize] cmd!(app.device_add(i)))) - //("picker" [] Some(Self::Picker)) - //("pick" [index: usize] Some(Self::Pick(index.unwrap()))) - //("add" [index: usize] Some(Self::Add(index.unwrap())))) + fn picker (&self, tek: &mut Tek) -> Perhaps { + tek.device_picker_show(); + Ok(None) + } + fn pick (&self, tek: &mut Tek, i: usize) -> Perhaps { + tek.device_pick(i); + Ok(None) + } + fn add (&self, tek: &mut Tek, i: usize) -> Perhaps { + tek.device_add(i); + Ok(None) + } } #[tengri_proc::command(Tek)] impl MessageCommand { - //(Dismiss [] cmd!(app.message_dismiss()))) - //("dismiss" [] Some(Self::Dismiss))) + fn dismiss (&self, tek: &mut Tek) -> Perhaps { + tek.message_dismiss(); + Ok(None) + } } #[tengri_proc::command(Tek)] @@ -426,332 +457,323 @@ impl ClipCommand { //("delete" [a: usize, b: usize] Some(Self::Put(a.unwrap(), b.unwrap(), None)))) } -fn delegate_to_editor (app: &mut Tek, cmd: MidiEditCommand) -> Perhaps { - Ok(app.editor.as_mut().map(|editor|cmd.delegate(editor, TekCommand::Editor)) - .transpose()? - .flatten()) -} - -fn delegate_to_pool (app: &mut Tek, cmd: PoolCommand) -> Perhaps { - Ok(if let Some(pool) = app.pool.as_mut() { - let undo = cmd.clone().delegate(pool, TekCommand::Pool)?; - if let Some(editor) = app.editor.as_mut() { - match cmd { - // autoselect: automatically load selected clip in editor - // autocolor: update color in all places simultaneously - PoolCommand::Select(_) | PoolCommand::Clip(PoolClipCommand::SetColor(_, _)) => - editor.set_clip(pool.clip().as_ref()), - _ => {} - } - }; - undo - } else { - None - }) -} - -#[derive(Clone, PartialEq, Debug)] pub enum PoolCommand { +#[tengri_proc::command(MidiPool)] +impl PoolCommand { /// Toggle visibility of pool - Show(bool), + fn show (&self, pool: &mut MidiPool, visible: bool) -> Perhaps { + pool.visible = visible; + Ok(Some(Self::Show(!visible))) + } /// Select a clip from the clip pool - Select(usize), + fn select (&self, pool: &mut MidiPool, index: usize) -> Perhaps { + pool.set_clip_index(index); + Ok(None) + } /// Rename a clip - Rename(ClipRenameCommand), + fn rename (&self, pool: &mut MidiPool, command: ClipRenameCommand) -> Perhaps { + Ok(match command { + ClipRenameCommand::Begin => { + pool.begin_clip_rename(); + None + }, + _ => command.delegate(pool, |command|Self::Rename{command})? + }) + } /// Change the length of a clip - Length(ClipLengthCommand), + fn length (&self, pool: &mut MidiPool, command: ClipLengthCommand) -> Perhaps { + Ok(match command { + ClipLengthCommand::Begin => { + pool.begin_clip_length(); + None + }, + _ => command.delegate(pool, |command|Self::Length{command})? + }) + } /// Import from file - Import(FileBrowserCommand), + fn import (&self, pool: &mut MidiPool, command: FileBrowserCommand) -> Perhaps { + Ok(match command { + ClipImportCommand::Begin => { + pool.begin_import(); + None + }, + _ => command.delegate(pool, |command|Self::Import{command})? + }) + } /// Export to file - Export(FileBrowserCommand), + fn export (&self, pool: &mut MidiPool, command: FileBrowserCommand) -> Perhaps { + Ok(match command { + ClipExportCommand::Begin => { + pool.begin_export(); + None + }, + _ => command.delegate(pool, |command|Self::Export{command})? + }) + } /// Update the contents of the clip pool - Clip(PoolClipCommand), -} - -atom_command!(PoolCommand: |state: MidiPool| { - ("show" [a: bool] Some(Self::Show(a.expect("no flag")))) - ("select" [i: usize] Some(Self::Select(i.expect("no index")))) - ("rename" [,..a] ClipRenameCommand::try_from_expr(state, a).map(Self::Rename)) - ("length" [,..a] ClipLengthCommand::try_from_expr(state, a).map(Self::Length)) - ("import" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Import)) - ("export" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Export)) - ("clip" [,..a] PoolClipCommand::try_from_expr(state, a).map(Self::Clip)) -}); - -command!(|self: PoolCommand, state: MidiPool|{ - use PoolCommand::*; - match self { - Rename(ClipRenameCommand::Begin) => { state.begin_clip_rename(); None } - Rename(command) => command.delegate(state, Rename)?, - Length(ClipLengthCommand::Begin) => { state.begin_clip_length(); None }, - Length(command) => command.delegate(state, Length)?, - Import(FileBrowserCommand::Begin) => { state.begin_import()?; None }, - Import(command) => command.delegate(state, Import)?, - Export(FileBrowserCommand::Begin) => { state.begin_export()?; None }, - Export(command) => command.delegate(state, Export)?, - Clip(command) => command.execute(state)?.map(Clip), - Show(visible) => { state.visible = visible; Some(Self::Show(!visible)) }, - Select(clip) => { state.set_clip_index(clip); None }, - } -}); - -#[derive(Clone, Debug, PartialEq)] pub enum PoolClipCommand { - Add(usize, MidiClip), - Delete(usize), - Swap(usize, usize), - Import(usize, PathBuf), - Export(usize, PathBuf), - SetName(usize, Arc), - SetLength(usize, usize), - SetColor(usize, ItemColor), -} - -impl Command for PoolClipCommand { - fn execute (self, model: &mut T) -> Perhaps { - use PoolClipCommand::*; - Ok(match self { - Add(mut index, clip) => { - let clip = Arc::new(RwLock::new(clip)); - let mut clips = model.clips_mut(); - if index >= clips.len() { - index = clips.len(); - clips.push(clip) - } else { - clips.insert(index, clip); - } - Some(Self::Delete(index)) - }, - Delete(index) => { - let clip = model.clips_mut().remove(index).read().unwrap().clone(); - Some(Self::Add(index, clip)) - }, - Swap(index, other) => { - model.clips_mut().swap(index, other); - Some(Self::Swap(index, other)) - }, - Import(index, path) => { - let bytes = std::fs::read(&path)?; - let smf = Smf::parse(bytes.as_slice())?; - let mut t = 0u32; - let mut events = vec![]; - for track in smf.tracks.iter() { - for event in track.iter() { - t += event.delta.as_int(); - if let TrackEventKind::Midi { channel, message } = event.kind { - events.push((t, channel.as_int(), message)); - } - } - } - let mut clip = MidiClip::new("imported", true, t as usize + 1, None, None); - for event in events.iter() { - clip.notes[event.0 as usize].push(event.2); - } - Self::Add(index, clip).execute(model)? - }, - Export(_index, _path) => { - todo!("export clip to midi file"); - }, - SetName(index, name) => { - let clip = &mut model.clips_mut()[index]; - let old_name = clip.read().unwrap().name.clone(); - clip.write().unwrap().name = name; - Some(Self::SetName(index, old_name)) - }, - SetLength(index, length) => { - let clip = &mut model.clips_mut()[index]; - let old_len = clip.read().unwrap().length; - clip.write().unwrap().length = length; - Some(Self::SetLength(index, old_len)) - }, - SetColor(index, color) => { - let mut color = ItemTheme::from(color); - std::mem::swap(&mut color, &mut model.clips()[index].write().unwrap().color); - Some(Self::SetColor(index, color.base)) - }, - }) + fn clip (&self, pool: &mut MidiPool, command: PoolClipCommand) -> Perhaps { + command.execute(pool)?.map(|command|Self::Clip{command}) } } -atom_command!(ClipRenameCommand: |state: MidiPool| { - ("begin" [] Some(Self::Begin)) - ("cancel" [] Some(Self::Cancel)) - ("confirm" [] Some(Self::Confirm)) - ("set" [n: Arc] Some(Self::Set(n.expect("no name")))) -}); -atom_command!(ClipLengthCommand: |state: MidiPool| { - ("begin" [] Some(Self::Begin)) - ("cancel" [] Some(Self::Cancel)) - ("next" [] Some(Self::Next)) - ("prev" [] Some(Self::Prev)) - ("inc" [] Some(Self::Inc)) - ("dec" [] Some(Self::Dec)) - ("set" [l: usize] Some(Self::Set(l.expect("no length")))) -}); -atom_command!(FileBrowserCommand: |state: MidiPool| { - ("begin" [] Some(Self::Begin)) - ("cancel" [] Some(Self::Cancel)) - ("confirm" [] Some(Self::Confirm)) - ("select" [i: usize] Some(Self::Select(i.expect("no index")))) - ("chdir" [p: PathBuf] Some(Self::Chdir(p.expect("no path")))) - ("filter" [f: Arc] Some(Self::Filter(f.expect("no filter")))) -}); -atom_command!(MidiEditCommand: |state: MidiEditor| { - ("note/append" [] Some(Self::AppendNote)) - ("note/put" [] Some(Self::PutNote)) - ("note/del" [] Some(Self::DelNote)) - ("note/pos" [a: usize] Some(Self::SetNoteCursor(a.expect("no note cursor")))) - ("note/len" [a: usize] Some(Self::SetNoteLength(a.expect("no note length")))) - ("time/pos" [a: usize] Some(Self::SetTimeCursor(a.expect("no time cursor")))) - ("time/zoom" [a: usize] Some(Self::SetTimeZoom(a.expect("no time zoom")))) - ("time/lock" [a: bool] Some(Self::SetTimeLock(a.expect("no time lock")))) - ("time/lock" [] Some(Self::SetTimeLock(!state.time_lock().get()))) -}); -atom_command!(PoolClipCommand: |state: MidiPool| { - ("add" [i: usize, c: MidiClip] - Some(Self::Add(i.expect("no index"), c.expect("no clip")))) - ("delete" [i: usize] - Some(Self::Delete(i.expect("no index")))) - ("swap" [a: usize, b: usize] - Some(Self::Swap(a.expect("no index"), b.expect("no index")))) - ("import" [i: usize, p: PathBuf] - Some(Self::Import(i.expect("no index"), p.expect("no path")))) - ("export" [i: usize, p: PathBuf] - Some(Self::Export(i.expect("no index"), p.expect("no path")))) - ("set-name" [i: usize, n: Arc] - Some(Self::SetName(i.expect("no index"), n.expect("no name")))) - ("set-length" [i: usize, l: usize] - Some(Self::SetLength(i.expect("no index"), l.expect("no length")))) - ("set-color" [i: usize, c: ItemColor] - Some(Self::SetColor(i.expect("no index"), c.expect("no color")))) -}); -// TODO: 1-9 seek markers that by default start every 8th of the clip -defcom!([self, state: MidiEditor] - (MidiEditCommand - (AppendNote [] { state.put_note(true); None }) - (PutNote [] { state.put_note(false); None }) - (DelNote [] { None }) - (SetNoteCursor [x: usize] { state.set_note_pos(x.min(127)); None }) - (SetNoteLength [x: usize] { - let note_len = state.note_len(); - let time_zoom = state.time_zoom().get(); - state.set_note_len(x); - //if note_len / time_zoom != x / time_zoom { - state.redraw(); - //} - None - }) - (SetNoteScroll [x: usize] { state.note_lo().set(x.min(127)); None }) - (SetTimeCursor [x: usize] { state.set_time_pos(x); None }) - (SetTimeScroll [x: usize] { state.time_start().set(x); None }) - (SetTimeZoom [x: usize] { state.time_zoom().set(x); state.redraw(); None }) - (SetTimeLock [x: bool] { state.time_lock().set(x); None }) - (Show [x: MaybeClip] { state.set_clip(x.as_ref()); None }))); - -#[derive(Clone, Debug, PartialEq)] pub enum ClipRenameCommand { - Begin, - Cancel, - Confirm, - Set(Arc), -} - - -command!(|self: ClipRenameCommand, state: MidiPool|if let Some( - PoolMode::Rename(clip, ref mut old_name) -) = state.mode_mut().clone() { - match self { - Self::Set(s) => { - state.clips()[clip].write().unwrap().name = s; - return Ok(Some(Self::Set(old_name.clone().into()))) - }, - Self::Confirm => { - let old_name = old_name.clone(); - *state.mode_mut() = None; - return Ok(Some(Self::Set(old_name))) - }, - Self::Cancel => { - state.clips()[clip].write().unwrap().name = old_name.clone().into(); - return Ok(None) - }, - _ => unreachable!() - } -} else { - unreachable!() -}); - -command!(|self: FileBrowserCommand, state: MidiPool|{ - use PoolMode::*; - use FileBrowserCommand::*; - let mode = &mut state.mode; - match mode { - Some(Import(index, ref mut browser)) => match self { - Cancel => { *mode = None; }, - Chdir(cwd) => { *mode = Some(Import(*index, FileBrowser::new(Some(cwd))?)); }, - Select(index) => { browser.index = index; }, - Confirm => if browser.is_file() { - let index = *index; - let path = browser.path(); - *mode = None; - PoolClipCommand::Import(index, path).execute(state)?; - } else if browser.is_dir() { - *mode = Some(Import(*index, browser.chdir()?)); - }, - _ => todo!(), - }, - Some(Export(index, ref mut browser)) => match self { - Cancel => { *mode = None; }, - Chdir(cwd) => { *mode = Some(Export(*index, FileBrowser::new(Some(cwd))?)); }, - Select(index) => { browser.index = index; }, - _ => unreachable!() - }, - _ => unreachable!(), - }; - None -}); - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum ClipLengthCommand { - Begin, - Cancel, - Set(usize), - Next, - Prev, - Inc, - Dec, -} - -command!(|self: ClipLengthCommand, state: MidiPool|{ - use ClipLengthCommand::*; - use ClipLengthFocus::*; - if let Some( - PoolMode::Length(clip, ref mut length, ref mut focus) - ) = state.mode_mut().clone() { - match self { - Cancel => { *state.mode_mut() = None; }, - Prev => { focus.prev() }, - Next => { focus.next() }, - Inc => match focus { - Bar => { *length += 4 * PPQ }, - Beat => { *length += PPQ }, - Tick => { *length += 1 }, - }, - Dec => match focus { - Bar => { *length = length.saturating_sub(4 * PPQ) }, - Beat => { *length = length.saturating_sub(PPQ) }, - Tick => { *length = length.saturating_sub(1) }, - }, - Set(length) => { - let old_length; - { - let clip = state.clips()[clip].clone();//.write().unwrap(); - old_length = Some(clip.read().unwrap().length); - clip.write().unwrap().length = length; - } - *state.mode_mut() = None; - return Ok(old_length.map(Self::Set)) - }, - _ => unreachable!() +#[tengri_proc::command(MidiPool)] +impl PoolClipCommand { + fn add (&self, pool: &mut MidiPool, index: usize, clip: MidiClip) -> Perhaps { + let mut index = index; + let clip = Arc::new(RwLock::new(clip)); + let mut clips = pool.clips_mut(); + if index >= clips.len() { + index = clips.len(); + clips.push(clip) + } else { + clips.insert(index, clip); } - } else { + Ok(Some(Self::Delete(index))) + } + fn delete (&self, pool: &mut MidiPool, index: usize) -> Perhaps { + let clip = pool.clips_mut().remove(index).read().unwrap().clone(); + Ok(Some(Self::Add(index, clip))) + } + fn swap (&self, pool: &mut MidiPool, index: usize, other: usize) -> Perhaps { + pool.clips_mut().swap(index, other); + Ok(Some(Self::Swap(index, other))) + } + fn import (&self, pool: &mut MidiPool, index: usize, path: PathBuf) -> Perhaps { + let bytes = std::fs::read(&path)?; + let smf = Smf::parse(bytes.as_slice())?; + let mut t = 0u32; + let mut events = vec![]; + for track in smf.tracks.iter() { + for event in track.iter() { + t += event.delta.as_int(); + if let TrackEventKind::Midi { channel, message } = event.kind { + events.push((t, channel.as_int(), message)); + } + } + } + let mut clip = MidiClip::new("imported", true, t as usize + 1, None, None); + for event in events.iter() { + clip.notes[event.0 as usize].push(event.2); + } + Self::Add(index, clip).execute(pool)? + } + fn export (&self, pool: &mut MidiPool, index: usize, path: PathBuf) -> Perhaps { + todo!("export clip to midi file"); + } + fn set_name (&self, pool: &mut MidiPool, index: usize, name: Arc) -> Perhaps { + let clip = &mut pool.clips_mut()[index]; + let old_name = clip.read().unwrap().name.clone(); + clip.write().unwrap().name = name; + Ok(Some(Self::SetName(index, old_name))) + } + fn set_length (&self, pool: &mut MidiPool, index: usize, length: usize) -> Perhaps { + let clip = &mut pool.clips_mut()[index]; + let old_len = clip.read().unwrap().length; + clip.write().unwrap().length = length; + Ok(Some(Self::SetLength(index, old_len))) + } + fn set_color (&self, pool: &mut MidiPool, index: usize, color: ItemColor) -> Perhaps { + let mut color = ItemTheme::from(color); + std::mem::swap(&mut color, &mut pool.clips()[index].write().unwrap().color); + Ok(Some(Self::SetColor(index, color.base))) + } +} + +#[tengri_proc::command(MidiPool)] +impl ClipRenameCommand { + fn begin (&self, pool: &mut MidiPool) -> Perhaps { unreachable!(); } - None -}); + fn cancel (&self, pool: &mut MidiPool) -> Perhaps { + if let Some(PoolMode::Rename(clip, ref mut old_name)) = pool.mode_mut().clone() { + pool.clips()[clip].write().unwrap().name = old_name.clone().into(); + } + return Ok(None) + } + fn confirm (&self, pool: &mut MidiPool) -> Perhaps { + if let Some(PoolMode::Rename(clip, ref mut old_name)) = pool.mode_mut().clone() { + let old_name = old_name.clone(); + pool.mode_mut() = None; + return Ok(Some(Self::Set(old_name))) + } + return Ok(None) + } + fn set (&self, pool: &mut MidiPool, value: Arc) -> Perhaps { + if let Some(PoolMode::Rename(clip, ref mut old_name)) = pool.mode_mut().clone() { + pool.clips()[clip].write().unwrap().name = value; + } + return Ok(None) + } +} + +#[tengri_proc::command(MidiPool)] +impl ClipLengthCommand { + fn begin (&self, pool: &mut MidiPool) -> Perhaps { + unreachable!() + } + fn cancel (&self, pool: &mut MidiPool) -> Perhaps { + if let Some(PoolMode::Length(..)) = pool.mode_mut().clone() { + *pool.mode_mut() = None; + } + Ok(None) + } + fn set (&self, pool: &mut MidiPool, length: usize) -> Perhaps { + if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) + = pool.mode_mut().clone() + { + let old_length; + { + let clip = pool.clips()[clip].clone();//.write().unwrap(); + old_length = Some(clip.read().unwrap().length); + clip.write().unwrap().length = length; + } + pool.mode_mut() = None; + return Ok(old_length.map(|length|Self::Set { length })) + } + Ok(None) + } + fn next (&self, pool: &mut MidiPool) -> Perhaps { + if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) + = pool.mode_mut().clone() + { + focus.next() + } + Ok(None) + } + fn prev (&self, pool: &mut MidiPool) -> Perhaps { + if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) + = pool.mode_mut().clone() + { + focus.prev() + } + Ok(None) + } + fn inc (&self, pool: &mut MidiPool) -> Perhaps { + if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) + = pool.mode_mut().clone() + { + match focus { + Bar => { *length += 4 * PPQ }, + Beat => { *length += PPQ }, + Tick => { *length += 1 }, + } + } + Ok(None) + } + fn dec (&self, pool: &mut MidiPool) -> Perhaps { + if let Some( + PoolMode::Length(clip, ref mut length, ref mut focus) + ) = pool.mode_mut().clone() { + match focus { + Bar => { *length = length.saturating_sub(4 * PPQ) }, + Beat => { *length = length.saturating_sub(PPQ) }, + Tick => { *length = length.saturating_sub(1) }, + } + } + Ok(None) + } +} + +#[tengri_proc::command(MidiPool)] +impl FileBrowserCommand { + fn begin (&self, pool: &mut MidiPool) -> Perhaps { + unreachable!(); + } + fn cancel (&self, pool: &mut MidiPool) -> Perhaps { + pool.mode = None; + Ok(None) + } + fn confirm (&self, pool: &mut MidiPool) -> Perhaps { + Ok(match pool.mode { + Some(PoolMode::Import(index, ref mut browser)) => { + if browser.is_file() { + let index = *index; + let path = browser.path(); + pool.mode = None; + PoolClipCommand::Import(index, path).execute(pool)?; + } else if browser.is_dir() { + pool.mode = Some(PoolMode::Import(*index, browser.chdir()?)); + } + }, + Some(PoolMode::Export(index, ref mut browser)) => match self { + Cancel => { pool.mode = None; }, + _ => unreachable!() + }, + _ => unreachable!(), + }) + } + fn select (&self, pool: &mut MidiPool, index: usize) -> Perhaps { + Ok(match pool.mode { + Some(PoolMode::Import(index, ref mut browser)) => { browser.index = index; }, + Some(PoolMode::Export(index, ref mut browser)) => { browser.index = index; }, + _ => unreachable!(), + }) + } + fn chdir (&self, pool: &mut MidiPool, dir: PathBuf) -> Perhaps { + Ok(match pool.mode { + Some(PoolMode::Import(index, ref mut browser)) => { + pool.mode = Some(PoolMode::Import(*index, FileBrowser::new(Some(dir))?)); + }, + Some(PoolMode::Export(index, ref mut browser)) => { + pool.mode = Some(PoolMode::Export(*index, FileBrowser::new(Some(dir))?)); + }, + _ => unreachable!(), + }) + } + fn filter (&self, pool: &mut MidiPool, filter: Arc) -> Perhaps { + todo!() + } +} + +#[tengri_proc::command(MidiEditor)] +impl MidiEditCommand { + // TODO: 1-9 seek markers that by default start every 8th of the clip + fn note_append (&self, editor: &mut MidiEditor) -> Perhaps { + editor.put_note(true); + Ok(None) + } + fn note_put (&self, editor: &mut MidiEditor) -> Perhaps { + editor.put_note(false); + Ok(None) + } + fn note_del (&self, editor: &mut MidiEditor) -> Perhaps { + todo!() + } + fn note_pos (&self, editor: &mut MidiEditor, pos: usize) -> Perhaps { + editor.set_note_pos(pos.min(127)); + Ok(None) + } + fn note_len (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { + //let note_len = editor.get_note_len(); + //let time_zoom = editor.get_time_zoom(); + editor.set_note_len(value); + //if note_len / time_zoom != x / time_zoom { + editor.redraw(); + //} + Ok(None) + } + fn note_scroll (&self, editor: &mut MidiEditor, value: usize) { + editor.set_note_lo(value.min(127)); + Ok(None) + } + fn time_pos (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { + editor.set_time_pos(value); + Ok(None) + } + fn time_scroll (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { + editor.set_time_start(value); + Ok(None) + } + fn time_zoom (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { + editor.time_zoom().set(value); + editor.redraw(); + Ok(None) + } + fn time_lock (&self, editor: &mut MidiEditor, value: bool) -> Perhaps { + editor.set_time_lock(value); + Ok(None) + } + fn show (&self, editor: &mut MidiEditor, clip: Option>>) -> Perhaps { + editor.set_clip(clip.as_ref()); + Ok(None) + } +} diff --git a/crates/device/src/clock/clock_api.rs b/crates/device/src/clock/clock_api.rs index a2e5927f..097867fc 100644 --- a/crates/device/src/clock/clock_api.rs +++ b/crates/device/src/clock/clock_api.rs @@ -1,55 +1,52 @@ use crate::*; -#[derive(Clone, Debug, PartialEq)] -pub enum ClockCommand { - Play(Option), - Pause(Option), - SeekUsec(f64), - SeekSample(f64), - SeekPulse(f64), - SetBpm(f64), - SetQuant(f64), - SetSync(f64), -} - provide_num!(u32: |self: Clock| {}); - +provide!(Option: |self: Clock| {}); provide!(f64: |self: Clock| {}); -atom_command!(ClockCommand: |state: Clock| { - ("play" [] Some(Self::Play(None))) - ("play" [t: u32] Some(Self::Play(t))) - ("pause" [] Some(Self::Pause(None))) - ("pause" [t: u32] Some(Self::Pause(t))) - ("toggle" [] Some(if state.is_rolling() { Self::Pause(None) } else { Self::Play(None) })) - ("toggle" [t: u32] Some(if state.is_rolling() { Self::Pause(t) } else { Self::Play(t) })) - ("seek/usec" [t: f64] Some(Self::SeekUsec(t.expect("no usec")))) - ("seek/pulse" [t: f64] Some(Self::SeekPulse(t.expect("no pulse")))) - ("seek/sample" [t: f64] Some(Self::SeekSample(t.expect("no sample")))) - ("set/bpm" [t: f64] Some(Self::SetBpm(t.expect("no bpm")))) - ("set/sync" [t: f64] Some(Self::SetSync(t.expect("no sync")))) - ("set/quant" [t: f64] Some(Self::SetQuant(t.expect("no quant")))) -}); - impl Command for ClockCommand { fn execute (self, state: &mut T) -> Perhaps { - self.execute(state.clock_mut()) + self.execute(state.clock_mut()) // awesome } } -impl Command for ClockCommand { - fn execute (self, state: &mut Clock) -> Perhaps { - use ClockCommand::*; - match self { - Play(start) => state.play_from(start)?, - Pause(pause) => state.pause_at(pause)?, - SeekUsec(usec) => state.playhead.update_from_usec(usec), - SeekSample(sample) => state.playhead.update_from_sample(sample), - SeekPulse(pulse) => state.playhead.update_from_pulse(pulse), - SetBpm(bpm) => return Ok(Some(SetBpm(state.timebase().bpm.set(bpm)))), - SetQuant(quant) => return Ok(Some(SetQuant(state.quant.set(quant)))), - SetSync(sync) => return Ok(Some(SetSync(state.sync.set(sync)))), - }; +#[tengri_proc::command(Clock)] +impl ClockCommand { + fn play (self, state: &mut Clock, position: Option) -> Perhaps { + state.play_from(position)?; + Ok(None) // TODO Some(Pause(previousPosition)) + } + fn pause (self, state: &mut Clock, position: Option) -> Perhaps { + state.pause_at(position)?; Ok(None) } + fn toggle_playback (self, state: &mut Clock, position: Option) -> Perhaps { + if state.is_rolling() { + state.pause_at(position)?; + } else { + state.play_from(position)?; + } + Ok(None) + } + fn seek_usec (self, state: &mut Clock, usec: f64) -> Perhaps { + state.playhead.update_from_usec(usec); + Ok(None) + } + fn seek_sample (self, state: &mut Clock, sample: f64) -> Perhaps { + state.playhead.update_from_sample(sample); + Ok(None) + } + fn seek_pulse (self, state: &mut Clock, pulse: f64) -> Perhaps { + state.playhead.update_from_pulse(pulse); + Ok(None) + } + fn set_bpm (self, state: &mut Clock, bpm: f64) -> Perhaps { + Ok(Some(Self::SetBpm { bpm: state.timebase().bpm.set(bpm) })) + } + fn set_quant (self, state: &mut Clock, quant: f64) -> Perhaps { + Ok(Some(Self::SetQuant { quant: state.quant.set(quant) })) + } + fn set_sync (self, state: &mut Clock, sync: f64) -> Perhaps { + Ok(Some(Self::SetSync { sync: state.sync.set(sync) })) + } } diff --git a/deps/tengri b/deps/tengri index fa10f7d4..2a6087e1 160000 --- a/deps/tengri +++ b/deps/tengri @@ -1 +1 @@ -Subproject commit fa10f7d4d36924beae83d2136d180c2006508ae9 +Subproject commit 2a6087e1c7086f09b1ade22c84ff62642df7c723 From 16d267523be7854a5d1825b1788cb48889a73316 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 8 May 2025 17:43:42 +0300 Subject: [PATCH 2/4] wip: now just gotta fix 26 type errors --- crates/app/src/api.rs | 429 +++++++++++++++------------ crates/app/src/model/editor.rs | 6 +- crates/app/src/view.rs | 4 +- crates/device/src/clock/clock_api.rs | 18 +- crates/engine/src/note/note_range.rs | 9 + deps/tengri | 2 +- 6 files changed, 259 insertions(+), 209 deletions(-) diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index b0db4c6f..aa33ef34 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -17,8 +17,7 @@ handle!(TuiIn: |self: Tek, input|Ok(if let Some(command) = self.config.keys.comm None })); -#[tengri_proc::expose] -impl Tek { +#[tengri_proc::expose] impl Tek { fn focus_editor (&self) -> bool { self.is_editing() } @@ -161,8 +160,7 @@ impl Tek { } } -#[tengri_proc::expose] -impl MidiPool { +#[tengri_proc::expose] impl MidiPool { fn clip_new (&self) -> MidiClip { self.new_clip() } @@ -186,8 +184,7 @@ impl MidiPool { } } -#[tengri_proc::expose] -impl MidiEditor { +#[tengri_proc::expose] impl MidiEditor { fn time_lock (&self) -> bool { self.get_time_lock() } @@ -256,27 +253,26 @@ impl MidiEditor { } } -#[tengri_proc::command(Tek)] -impl TekCommand { - fn toggle_help (&self, tek: &mut Tek, value: bool) -> Perhaps { +#[tengri_proc::command(Tek)] impl TekCommand { + fn toggle_help (tek: &mut Tek, value: bool) -> Perhaps { tek.toggle_dialog(Some(Dialog::Help)); Ok(None) } - fn toggle_menu (&self, tek: &mut Tek, value: bool) -> Perhaps { + fn toggle_menu (tek: &mut Tek, value: bool) -> Perhaps { tek.toggle_dialog(Some(Dialog::Menu)); Ok(None) } - fn toggle_edit (&self, tek: &mut Tek, value: bool) -> Perhaps { + fn toggle_edit (tek: &mut Tek, value: bool) -> Perhaps { tek.toggle_editor(Some(value)); Ok(None) } - fn editor (&self, tek: &mut Tek, command: MidiEditCommand) -> Perhaps { + fn editor (tek: &mut Tek, command: MidiEditCommand) -> Perhaps { Ok(tek.editor.as_mut().map(|editor|command.execute(editor)) .transpose()? .flatten() .map(|undo|Self::Editor { command: undo })) } - fn pool (&self, tek: &mut Tek, command: PoolCommand) -> Perhaps { + fn pool (tek: &mut Tek, command: PoolCommand) -> Perhaps { Ok(if let Some(pool) = tek.pool.as_mut() { let undo = command.clone().delegate(pool, |command|TekCommand::Pool{command})?; // update linked editor after pool action @@ -293,53 +289,53 @@ impl TekCommand { None }) } - fn sampler (&self, tek: &mut Tek, ref command: SamplerCommand) -> Perhaps { + fn sampler (tek: &mut Tek, command: SamplerCommand) -> Perhaps { Ok(tek.sampler_mut() .map(|s|command.delegate(s, |command|Self::Sampler{command})) .transpose()? .flatten()) } - fn scene (&self, tek: &mut Tek, command: SceneCommand) -> Perhaps { + fn scene (tek: &mut Tek, command: SceneCommand) -> Perhaps { Ok(command.delegate(tek, |command|Self::Scene{command})?) } - fn track (&self, tek: &mut Tek, command: TrackCommand) -> Perhaps { + fn track (tek: &mut Tek, command: TrackCommand) -> Perhaps { Ok(command.delegate(tek, |command|Self::Track{command})?) } - fn input (&self, tek: &mut Tek, command: InputCommand) -> Perhaps { + fn input (tek: &mut Tek, command: InputCommand) -> Perhaps { Ok(command.delegate(tek, |command|Self::Input{command})?) } - fn output (&self, tek: &mut Tek, command: OutputCommand) -> Perhaps { + fn output (tek: &mut Tek, command: OutputCommand) -> Perhaps { Ok(command.delegate(tek, |command|Self::Output{command})?) } - fn clip (&self, tek: &mut Tek, command: ClipCommand) -> Perhaps { + fn clip (tek: &mut Tek, command: ClipCommand) -> Perhaps { Ok(command.delegate(tek, |command|Self::Clip{command})?) } - fn clock (&self, tek: &mut Tek, command: ClockCommand) -> Perhaps { - Ok(command.delegate(tek, |command|Self::Clock{command})?) + fn clock (tek: &mut Tek, command: ClockCommand) -> Perhaps { + Ok(command.execute(&mut tek.clock)?.map(|command|Self::Clock{command})) } - fn device (&self, tek: &mut Tek, command: DeviceCommand) -> Perhaps { + fn device (tek: &mut Tek, command: DeviceCommand) -> Perhaps { Ok(command.delegate(tek, |command|Self::Device{command})?) } - fn message (&self, tek: &mut Tek, command: MessageCommand) -> Perhaps { + fn message (tek: &mut Tek, command: MessageCommand) -> Perhaps { Ok(command.delegate(tek, |command|Self::Message{command})?) } - fn color (&self, tek: &mut Tek, theme: ItemTheme) -> Perhaps { + fn color (tek: &mut Tek, theme: ItemTheme) -> Perhaps { Ok(tek.set_color(Some(theme)).map(|theme|Self::Color{theme})) } - fn enqueue (&self, tek: &mut Tek, clip: Option>>) -> Perhaps { + fn enqueue (tek: &mut Tek, clip: Option>>) -> Perhaps { todo!() } - fn history (&self, tek: &mut Tek, delta: isize) -> Perhaps { + fn history (tek: &mut Tek, delta: isize) -> Perhaps { todo!() } - fn zoom (&self, tek: &mut Tek, zoom: usize) -> Perhaps { + fn zoom (tek: &mut Tek, zoom: usize) -> Perhaps { todo!() } - fn launch (&self, tek: &mut Tek) -> Perhaps { + fn launch (tek: &mut Tek) -> Perhaps { tek.launch(); Ok(None) } - fn select (&self, tek: &mut Tek, selection: Selection) -> Perhaps { + fn select (tek: &mut Tek, selection: Selection) -> Perhaps { tek.select(selection); Ok(None) //("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) { @@ -348,129 +344,169 @@ impl TekCommand { //(0, s) => Self::Select(Selection::Scene(s)), //(t, s) => Self::Select(Selection::TrackClip { track: t, scene: s }) }))) } - fn stop_all (&self, tek: &mut Tek) -> Perhaps { + fn stop_all (tek: &mut Tek) -> Perhaps { tek.stop_all(); Ok(None) } } -#[tengri_proc::command(Tek)] -impl InputCommand { - fn add (&self, tek: &mut Tek) -> Perhaps { +#[tengri_proc::command(Tek)] impl InputCommand { + fn add (tek: &mut Tek) -> Perhaps { tek.midi_in_add()?; Ok(None) } } -#[tengri_proc::command(Tek)] -impl OutputCommand { - fn add (&self, tek: &mut Tek) -> Perhaps { +#[tengri_proc::command(Tek)] impl OutputCommand { + fn add (tek: &mut Tek) -> Perhaps { tek.midi_out_add()?; Ok(None) } } -#[tengri_proc::command(Tek)] -impl DeviceCommand { - fn picker (&self, tek: &mut Tek) -> Perhaps { +#[tengri_proc::command(Tek)] impl DeviceCommand { + fn picker (tek: &mut Tek) -> Perhaps { tek.device_picker_show(); Ok(None) } - fn pick (&self, tek: &mut Tek, i: usize) -> Perhaps { + fn pick (tek: &mut Tek, i: usize) -> Perhaps { tek.device_pick(i); Ok(None) } - fn add (&self, tek: &mut Tek, i: usize) -> Perhaps { + fn add (tek: &mut Tek, i: usize) -> Perhaps { tek.device_add(i); Ok(None) } } -#[tengri_proc::command(Tek)] -impl MessageCommand { - fn dismiss (&self, tek: &mut Tek) -> Perhaps { +#[tengri_proc::command(Tek)] impl MessageCommand { + fn dismiss (tek: &mut Tek) -> Perhaps { tek.message_dismiss(); Ok(None) } } -#[tengri_proc::command(Tek)] -impl TrackCommand { - //(TogglePlay [] Some(Self::TogglePlay)) - //(ToggleSolo [] Some(Self::ToggleSolo)) - //(SetSize [t: usize] cmd_todo!("\n\rtodo: {self:?}")) - //(SetZoom [z: usize] cmd_todo!("\n\rtodo: {self:?}")) - //(Swap [a: usize, b: usize] cmd_todo!("\n\rtodo: {self:?}")) - //(Del [index: usize] cmd!(app.track_del(index))) - //(Stop [index: usize] cmd!(app.tracks[index].player.enqueue_next(None))) - //(Add [] Some(Self::Del(app.track_add_focus()?))) - //(SetColor [i: usize, c: ItemTheme] Some(Self::SetColor(i, app.track_set_color(i, c)))) - //(ToggleRec [] { app.track_toggle_record(); Some(Self::ToggleRec) }) - //(ToggleMon [] { app.track_toggle_monitor(); Some(Self::ToggleMon) })) - //("add" [] Some(Self::Add)) - //("size" [a: usize] Some(Self::SetSize(a.unwrap()))) - //("zoom" [a: usize] Some(Self::SetZoom(a.unwrap()))) - //("color" [a: usize] Some(Self::SetColor(a.unwrap(), ItemTheme::random()))) - //("delete" [a: Option] Some(Self::Del(a.flatten().unwrap()))) - //("stop" [a: usize] Some(Self::Stop(a.unwrap()))) - //("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap()))) - //("play" [] Some(Self::TogglePlay)) - //("solo" [] Some(Self::ToggleSolo)) - //("rec" [] Some(Self::ToggleRec)) - //("mon" [] Some(Self::ToggleMon)))); +#[tengri_proc::command(Tek)] impl TrackCommand { + fn toggle_play (tek: &mut Tek) -> Perhaps { + todo!() + } + fn toggle_solo (tek: &mut Tek) -> Perhaps { + todo!() + } + fn toggle_rec (tek: &mut Tek) -> Perhaps { + tek.track_toggle_record(); + Ok(Some(Self::ToggleRec)) + } + fn toggle_mon (tek: &mut Tek) -> Perhaps { + tek.track_toggle_monitor(); + Ok(Some(Self::ToggleMon)) + } + fn set_size (tek: &mut Tek, size: usize) -> Perhaps { + todo!() + } + fn set_zoom (tek: &mut Tek, zoom: usize) -> Perhaps { + todo!() + } + fn swap (tek: &mut Tek, index: usize, other: usize) -> Perhaps { + todo!(); + Ok(Some(Self::Swap { index, other })) + } + fn del (tek: &mut Tek, index: usize) -> Perhaps { + tek.track_del(index); + Ok(None) + } + fn stop (tek: &mut Tek, index: usize) -> Perhaps { + tek.tracks[index].player.enqueue_next(None); + Ok(None) + } + fn add (tek: &mut Tek) -> Perhaps { + Ok(Some(Self::Del { index: tek.track_add_focus()? })) + } + fn set_color (tek: &mut Tek, index: usize, color: ItemTheme) -> Perhaps { + Ok(Some(Self::SetColor { index, color: tek.track_set_color(index, color) })) + } } -#[tengri_proc::command(Tek)] -impl SceneCommand { - //(Swap [a: usize, b: usize] cmd_todo!("\n\rtodo: {self:?}")) - //(SetSize [index: usize] cmd_todo!("\n\rtodo: {self:?}")) - //(SetZoom [zoom: usize] cmd_todo!("\n\rtodo: {self:?}")) - //(Enqueue [scene: usize] cmd!(app.scene_enqueue(scene))) - //(Del [index: usize] cmd!(app.scene_del(index))) - //(Add [] Some(Self::Del(app.scene_add_focus()?))) - //(SetColor [i: usize, c: ItemTheme] Some(Self::SetColor(i, app.scene_set_color(i, c))))) - //("add" [] Some(Self::Add)) - //("delete" [a: Option] Some(Self::Del(a.flatten().unwrap()))) - //("zoom" [a: usize] Some(Self::SetZoom(a.unwrap()))) - //("color" [a: usize] Some(Self::SetColor(a.unwrap(), ItemTheme::G[128]))) - //("enqueue" [a: usize] Some(Self::Enqueue(a.unwrap()))) - //("swap" [a: usize, b: usize] Some(Self::Swap(a.unwrap(), b.unwrap())))) +#[tengri_proc::command(Tek)] impl SceneCommand { + fn add (tek: &mut Tek) -> Perhaps { + todo!() + } + fn del (tek: &mut Tek, index: usize) -> Perhaps { + tek.scene_del(index); + Ok(None) + } + fn enqueue (tek: &mut Tek, index: usize) -> Perhaps { + tek.scene_enqueue(index); + Ok(None) + } + fn set_color (tek: &mut Tek, index: usize, color: ItemTheme) -> Perhaps { + Ok(Some(Self::SetColor { index, color: tek.scene_set_color(index, color) })) + } + fn set_size (tek: &mut Tek, index: usize, size: usize) -> Perhaps { + todo!() + } + fn set_zoom (tek: &mut Tek, index: usize, zoom: usize) -> Perhaps { + todo!() + } + fn swap (tek: &mut Tek, index: usize, other: usize) -> Perhaps { + todo!(); + Ok(Some(Self::Swap { index, other })) + } } -#[tengri_proc::command(Tek)] -impl ClipCommand { - //(Get [a: usize, b: usize] cmd_todo!("\n\rtodo: clip: get: {a} {b}")) - //(Edit [clip: MaybeClip] cmd_todo!("\n\rtodo: clip: edit: {clip:?}")) - //(SetLoop [t: usize, s: usize, l: bool] cmd_todo!("\n\rtodo: {self:?}")) - //(Put [t: usize, s: usize, c: MaybeClip] - //Some(Self::Put(t, s, app.clip_put(t, s, c)))) - //(Enqueue [t: usize, s: usize] - //cmd!(app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()))) - //(SetColor [t: usize, s: usize, c: ItemTheme] - //app.clip_set_color(t, s, c).map(|o|Self::SetColor(t, s, o))))); - //("edit" [a: MaybeClip] Some(Self::Edit(a.unwrap()))) - //("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemTheme::random()))) - //("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap()))) - //("get" [a: usize, b: usize] Some(Self::Get(a.unwrap(), b.unwrap()))) - //("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap()))) - //("put" [a: usize, b: usize, c: MaybeClip] Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap()))) +#[tengri_proc::command(Tek)] impl ClipCommand { + fn get (tek: &mut Tek, a: usize, b: usize) -> Perhaps { + //(Get [a: usize, b: usize] cmd_todo!("\n\rtodo: clip: get: {a} {b}")) + //("get" [a: usize, b: usize] Some(Self::Get(a.unwrap(), b.unwrap()))) + todo!() + } + fn edit (tek: &mut Tek, a: usize, b: usize) -> Perhaps { + //(Edit [clip: MaybeClip] cmd_todo!("\n\rtodo: clip: edit: {clip:?}")) + //("edit" [a: MaybeClip] Some(Self::Edit(a.unwrap()))) + todo!() + } + fn set_loop (tek: &mut Tek, a: usize, b: usize) -> Perhaps { + //(SetLoop [t: usize, s: usize, l: bool] cmd_todo!("\n\rtodo: {self:?}")) + //("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap()))) + todo!() + } + fn put (tek: &mut Tek, a: usize, b: usize) -> Perhaps { + //(Put [t: usize, s: usize, c: MaybeClip] + //Some(Self::Put(t, s, app.clip_put(t, s, c)))) + //("put" [a: usize, b: usize, c: MaybeClip] Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap()))) + todo!() + } + fn del (tek: &mut Tek, a: usize, b: usize) -> Perhaps { //("delete" [a: usize, b: usize] Some(Self::Put(a.unwrap(), b.unwrap(), None)))) + todo!() + } + fn enqueue (tek: &mut Tek, a: usize, b: usize) -> Perhaps { + //(Enqueue [t: usize, s: usize] + //cmd!(app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()))) + //("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap()))) + todo!() + } + fn set_color (tek: &mut Tek, a: usize, b: usize) -> Perhaps { + //(SetColor [t: usize, s: usize, c: ItemTheme] + //app.clip_set_color(t, s, c).map(|o|Self::SetColor(t, s, o))))); + //("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemTheme::random()))) + todo!() + } } -#[tengri_proc::command(MidiPool)] -impl PoolCommand { +#[tengri_proc::command(MidiPool)] impl PoolCommand { /// Toggle visibility of pool - fn show (&self, pool: &mut MidiPool, visible: bool) -> Perhaps { + fn show (pool: &mut MidiPool, visible: bool) -> Perhaps { pool.visible = visible; - Ok(Some(Self::Show(!visible))) + Ok(Some(Self::Show { visible: !visible })) } /// Select a clip from the clip pool - fn select (&self, pool: &mut MidiPool, index: usize) -> Perhaps { + fn select (pool: &mut MidiPool, index: usize) -> Perhaps { pool.set_clip_index(index); Ok(None) } /// Rename a clip - fn rename (&self, pool: &mut MidiPool, command: ClipRenameCommand) -> Perhaps { + fn rename (pool: &mut MidiPool, command: ClipRenameCommand) -> Perhaps { Ok(match command { ClipRenameCommand::Begin => { pool.begin_clip_rename(); @@ -480,7 +516,7 @@ impl PoolCommand { }) } /// Change the length of a clip - fn length (&self, pool: &mut MidiPool, command: ClipLengthCommand) -> Perhaps { + fn length (pool: &mut MidiPool, command: ClipLengthCommand) -> Perhaps { Ok(match command { ClipLengthCommand::Begin => { pool.begin_clip_length(); @@ -490,9 +526,9 @@ impl PoolCommand { }) } /// Import from file - fn import (&self, pool: &mut MidiPool, command: FileBrowserCommand) -> Perhaps { + fn import (pool: &mut MidiPool, command: FileBrowserCommand) -> Perhaps { Ok(match command { - ClipImportCommand::Begin => { + FileBrowserCommand::Begin => { pool.begin_import(); None }, @@ -500,9 +536,9 @@ impl PoolCommand { }) } /// Export to file - fn export (&self, pool: &mut MidiPool, command: FileBrowserCommand) -> Perhaps { + fn export (pool: &mut MidiPool, command: FileBrowserCommand) -> Perhaps { Ok(match command { - ClipExportCommand::Begin => { + FileBrowserCommand::Begin => { pool.begin_export(); None }, @@ -510,14 +546,13 @@ impl PoolCommand { }) } /// Update the contents of the clip pool - fn clip (&self, pool: &mut MidiPool, command: PoolClipCommand) -> Perhaps { - command.execute(pool)?.map(|command|Self::Clip{command}) + fn clip (pool: &mut MidiPool, command: PoolClipCommand) -> Perhaps { + Ok(command.execute(pool)?.map(|command|Self::Clip{command})) } } -#[tengri_proc::command(MidiPool)] -impl PoolClipCommand { - fn add (&self, pool: &mut MidiPool, index: usize, clip: MidiClip) -> Perhaps { +#[tengri_proc::command(MidiPool)] impl PoolClipCommand { + fn add (pool: &mut MidiPool, index: usize, clip: MidiClip) -> Perhaps { let mut index = index; let clip = Arc::new(RwLock::new(clip)); let mut clips = pool.clips_mut(); @@ -527,17 +562,17 @@ impl PoolClipCommand { } else { clips.insert(index, clip); } - Ok(Some(Self::Delete(index))) + Ok(Some(Self::Delete { index })) } - fn delete (&self, pool: &mut MidiPool, index: usize) -> Perhaps { + fn delete (pool: &mut MidiPool, index: usize) -> Perhaps { let clip = pool.clips_mut().remove(index).read().unwrap().clone(); - Ok(Some(Self::Add(index, clip))) + Ok(Some(Self::Add { index, clip })) } - fn swap (&self, pool: &mut MidiPool, index: usize, other: usize) -> Perhaps { + fn swap (pool: &mut MidiPool, index: usize, other: usize) -> Perhaps { pool.clips_mut().swap(index, other); - Ok(Some(Self::Swap(index, other))) + Ok(Some(Self::Swap { index, other })) } - fn import (&self, pool: &mut MidiPool, index: usize, path: PathBuf) -> Perhaps { + fn import (pool: &mut MidiPool, index: usize, path: PathBuf) -> Perhaps { let bytes = std::fs::read(&path)?; let smf = Smf::parse(bytes.as_slice())?; let mut t = 0u32; @@ -554,50 +589,49 @@ impl PoolClipCommand { for event in events.iter() { clip.notes[event.0 as usize].push(event.2); } - Self::Add(index, clip).execute(pool)? + Ok(Self::Add { index, clip }.execute(pool)?) } - fn export (&self, pool: &mut MidiPool, index: usize, path: PathBuf) -> Perhaps { + fn export (pool: &mut MidiPool, index: usize, path: PathBuf) -> Perhaps { todo!("export clip to midi file"); } - fn set_name (&self, pool: &mut MidiPool, index: usize, name: Arc) -> Perhaps { + fn set_name (pool: &mut MidiPool, index: usize, name: Arc) -> Perhaps { let clip = &mut pool.clips_mut()[index]; let old_name = clip.read().unwrap().name.clone(); clip.write().unwrap().name = name; - Ok(Some(Self::SetName(index, old_name))) + Ok(Some(Self::SetName { index, name: old_name })) } - fn set_length (&self, pool: &mut MidiPool, index: usize, length: usize) -> Perhaps { + fn set_length (pool: &mut MidiPool, index: usize, length: usize) -> Perhaps { let clip = &mut pool.clips_mut()[index]; let old_len = clip.read().unwrap().length; clip.write().unwrap().length = length; - Ok(Some(Self::SetLength(index, old_len))) + Ok(Some(Self::SetLength { index, length: old_len })) } - fn set_color (&self, pool: &mut MidiPool, index: usize, color: ItemColor) -> Perhaps { + fn set_color (pool: &mut MidiPool, index: usize, color: ItemColor) -> Perhaps { let mut color = ItemTheme::from(color); std::mem::swap(&mut color, &mut pool.clips()[index].write().unwrap().color); - Ok(Some(Self::SetColor(index, color.base))) + Ok(Some(Self::SetColor { index, color: color.base })) } } -#[tengri_proc::command(MidiPool)] -impl ClipRenameCommand { - fn begin (&self, pool: &mut MidiPool) -> Perhaps { +#[tengri_proc::command(MidiPool)] impl ClipRenameCommand { + fn begin (pool: &mut MidiPool) -> Perhaps { unreachable!(); } - fn cancel (&self, pool: &mut MidiPool) -> Perhaps { + fn cancel (pool: &mut MidiPool) -> Perhaps { if let Some(PoolMode::Rename(clip, ref mut old_name)) = pool.mode_mut().clone() { pool.clips()[clip].write().unwrap().name = old_name.clone().into(); } return Ok(None) } - fn confirm (&self, pool: &mut MidiPool) -> Perhaps { + fn confirm (pool: &mut MidiPool) -> Perhaps { if let Some(PoolMode::Rename(clip, ref mut old_name)) = pool.mode_mut().clone() { let old_name = old_name.clone(); - pool.mode_mut() = None; - return Ok(Some(Self::Set(old_name))) + *pool.mode_mut() = None; + return Ok(Some(Self::Set { value: old_name })) } return Ok(None) } - fn set (&self, pool: &mut MidiPool, value: Arc) -> Perhaps { + fn set (pool: &mut MidiPool, value: Arc) -> Perhaps { if let Some(PoolMode::Rename(clip, ref mut old_name)) = pool.mode_mut().clone() { pool.clips()[clip].write().unwrap().name = value; } @@ -605,18 +639,17 @@ impl ClipRenameCommand { } } -#[tengri_proc::command(MidiPool)] -impl ClipLengthCommand { - fn begin (&self, pool: &mut MidiPool) -> Perhaps { +#[tengri_proc::command(MidiPool)] impl ClipLengthCommand { + fn begin (pool: &mut MidiPool) -> Perhaps { unreachable!() } - fn cancel (&self, pool: &mut MidiPool) -> Perhaps { + fn cancel (pool: &mut MidiPool) -> Perhaps { if let Some(PoolMode::Length(..)) = pool.mode_mut().clone() { *pool.mode_mut() = None; } Ok(None) } - fn set (&self, pool: &mut MidiPool, length: usize) -> Perhaps { + fn set (pool: &mut MidiPool, length: usize) -> Perhaps { if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) = pool.mode_mut().clone() { @@ -624,14 +657,14 @@ impl ClipLengthCommand { { let clip = pool.clips()[clip].clone();//.write().unwrap(); old_length = Some(clip.read().unwrap().length); - clip.write().unwrap().length = length; + clip.write().unwrap().length = *length; } - pool.mode_mut() = None; + *pool.mode_mut() = None; return Ok(old_length.map(|length|Self::Set { length })) } Ok(None) } - fn next (&self, pool: &mut MidiPool) -> Perhaps { + fn next (pool: &mut MidiPool) -> Perhaps { if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) = pool.mode_mut().clone() { @@ -639,7 +672,7 @@ impl ClipLengthCommand { } Ok(None) } - fn prev (&self, pool: &mut MidiPool) -> Perhaps { + fn prev (pool: &mut MidiPool) -> Perhaps { if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) = pool.mode_mut().clone() { @@ -647,102 +680,110 @@ impl ClipLengthCommand { } Ok(None) } - fn inc (&self, pool: &mut MidiPool) -> Perhaps { + fn inc (pool: &mut MidiPool) -> Perhaps { if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) = pool.mode_mut().clone() { match focus { - Bar => { *length += 4 * PPQ }, - Beat => { *length += PPQ }, - Tick => { *length += 1 }, + ClipLengthFocus::Bar => { *length += 4 * PPQ }, + ClipLengthFocus::Beat => { *length += PPQ }, + ClipLengthFocus::Tick => { *length += 1 }, } } Ok(None) } - fn dec (&self, pool: &mut MidiPool) -> Perhaps { - if let Some( - PoolMode::Length(clip, ref mut length, ref mut focus) - ) = pool.mode_mut().clone() { + fn dec (pool: &mut MidiPool) -> Perhaps { + if let Some(PoolMode::Length(clip, ref mut length, ref mut focus)) + = pool.mode_mut().clone() + { match focus { - Bar => { *length = length.saturating_sub(4 * PPQ) }, - Beat => { *length = length.saturating_sub(PPQ) }, - Tick => { *length = length.saturating_sub(1) }, + ClipLengthFocus::Bar => { *length = length.saturating_sub(4 * PPQ) }, + ClipLengthFocus::Beat => { *length = length.saturating_sub(PPQ) }, + ClipLengthFocus::Tick => { *length = length.saturating_sub(1) }, } } Ok(None) } } -#[tengri_proc::command(MidiPool)] -impl FileBrowserCommand { - fn begin (&self, pool: &mut MidiPool) -> Perhaps { +#[tengri_proc::command(MidiPool)] impl FileBrowserCommand { + fn begin (pool: &mut MidiPool) -> Perhaps { unreachable!(); } - fn cancel (&self, pool: &mut MidiPool) -> Perhaps { + fn cancel (pool: &mut MidiPool) -> Perhaps { pool.mode = None; Ok(None) } - fn confirm (&self, pool: &mut MidiPool) -> Perhaps { + fn confirm (pool: &mut MidiPool) -> Perhaps { Ok(match pool.mode { Some(PoolMode::Import(index, ref mut browser)) => { if browser.is_file() { - let index = *index; let path = browser.path(); pool.mode = None; - PoolClipCommand::Import(index, path).execute(pool)?; + let _undo = PoolClipCommand::import(pool, index, path)?; + None } else if browser.is_dir() { - pool.mode = Some(PoolMode::Import(*index, browser.chdir()?)); + pool.mode = Some(PoolMode::Import(index, browser.chdir()?)); + None + } else { + None } }, - Some(PoolMode::Export(index, ref mut browser)) => match self { - Cancel => { pool.mode = None; }, - _ => unreachable!() + Some(PoolMode::Export(index, ref mut browser)) => { + todo!() }, _ => unreachable!(), }) } - fn select (&self, pool: &mut MidiPool, index: usize) -> Perhaps { - Ok(match pool.mode { - Some(PoolMode::Import(index, ref mut browser)) => { browser.index = index; }, - Some(PoolMode::Export(index, ref mut browser)) => { browser.index = index; }, - _ => unreachable!(), - }) - } - fn chdir (&self, pool: &mut MidiPool, dir: PathBuf) -> Perhaps { + fn select (pool: &mut MidiPool, index: usize) -> Perhaps { Ok(match pool.mode { Some(PoolMode::Import(index, ref mut browser)) => { - pool.mode = Some(PoolMode::Import(*index, FileBrowser::new(Some(dir))?)); + browser.index = index; + None }, Some(PoolMode::Export(index, ref mut browser)) => { - pool.mode = Some(PoolMode::Export(*index, FileBrowser::new(Some(dir))?)); + browser.index = index; + None }, _ => unreachable!(), }) } - fn filter (&self, pool: &mut MidiPool, filter: Arc) -> Perhaps { + fn chdir (pool: &mut MidiPool, dir: PathBuf) -> Perhaps { + Ok(match pool.mode { + Some(PoolMode::Import(index, ref mut browser)) => { + pool.mode = Some(PoolMode::Import(index, FileBrowser::new(Some(dir))?)); + None + }, + Some(PoolMode::Export(index, ref mut browser)) => { + pool.mode = Some(PoolMode::Export(index, FileBrowser::new(Some(dir))?)); + None + }, + _ => unreachable!(), + }) + } + fn filter (pool: &mut MidiPool, filter: Arc) -> Perhaps { todo!() } } -#[tengri_proc::command(MidiEditor)] -impl MidiEditCommand { +#[tengri_proc::command(MidiEditor)] impl MidiEditCommand { // TODO: 1-9 seek markers that by default start every 8th of the clip - fn note_append (&self, editor: &mut MidiEditor) -> Perhaps { + fn note_append (editor: &mut MidiEditor) -> Perhaps { editor.put_note(true); Ok(None) } - fn note_put (&self, editor: &mut MidiEditor) -> Perhaps { + fn note_put (editor: &mut MidiEditor) -> Perhaps { editor.put_note(false); Ok(None) } - fn note_del (&self, editor: &mut MidiEditor) -> Perhaps { + fn note_del (editor: &mut MidiEditor) -> Perhaps { todo!() } - fn note_pos (&self, editor: &mut MidiEditor, pos: usize) -> Perhaps { + fn note_pos (editor: &mut MidiEditor, pos: usize) -> Perhaps { editor.set_note_pos(pos.min(127)); Ok(None) } - fn note_len (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { + fn note_len (editor: &mut MidiEditor, value: usize) -> Perhaps { //let note_len = editor.get_note_len(); //let time_zoom = editor.get_time_zoom(); editor.set_note_len(value); @@ -751,28 +792,28 @@ impl MidiEditCommand { //} Ok(None) } - fn note_scroll (&self, editor: &mut MidiEditor, value: usize) { + fn note_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_note_lo(value.min(127)); Ok(None) } - fn time_pos (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { + fn time_pos (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_time_pos(value); Ok(None) } - fn time_scroll (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { + fn time_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_time_start(value); Ok(None) } - fn time_zoom (&self, editor: &mut MidiEditor, value: usize) -> Perhaps { - editor.time_zoom().set(value); + fn time_zoom (editor: &mut MidiEditor, value: usize) -> Perhaps { + editor.set_time_zoom(value); editor.redraw(); Ok(None) } - fn time_lock (&self, editor: &mut MidiEditor, value: bool) -> Perhaps { + fn time_lock (editor: &mut MidiEditor, value: bool) -> Perhaps { editor.set_time_lock(value); Ok(None) } - fn show (&self, editor: &mut MidiEditor, clip: Option>>) -> Perhaps { + fn show (editor: &mut MidiEditor, clip: Option>>) -> Perhaps { editor.set_clip(clip.as_ref()); Ok(None) } diff --git a/crates/app/src/model/editor.rs b/crates/app/src/model/editor.rs index 9a9d5836..fec878b1 100644 --- a/crates/app/src/model/editor.rs +++ b/crates/app/src/model/editor.rs @@ -53,9 +53,9 @@ impl MidiEditor { let mut redraw = false; if let Some(clip) = self.clip() { let mut clip = clip.write().unwrap(); - let note_start = self.time_pos(); - let note_pos = self.note_pos(); - let note_len = self.note_len(); + let note_start = self.get_time_pos(); + let note_pos = self.get_note_pos(); + let note_len = self.get_note_len(); let note_end = note_start + (note_len.saturating_sub(1)); let key: u7 = u7::from(note_pos as u8); let vel: u7 = 100.into(); diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index d1377bee..2f1bfdd9 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -1195,8 +1195,8 @@ impl MidiViewer for PianoHorizontal { let clip = clip.read().unwrap(); let buf_size = self.buffer_size(&clip); let mut buffer = BigBuffer::from(buf_size); - let note_len = self.note_len(); - let time_zoom = self.time_zoom().get(); + let note_len = self.get_note_len(); + let time_zoom = self.get_time_zoom(); self.time_len().set(clip.length); PianoHorizontal::draw_bg(&mut buffer, &clip, time_zoom, note_len); PianoHorizontal::draw_fg(&mut buffer, &clip, time_zoom); diff --git a/crates/device/src/clock/clock_api.rs b/crates/device/src/clock/clock_api.rs index 097867fc..6faceb11 100644 --- a/crates/device/src/clock/clock_api.rs +++ b/crates/device/src/clock/clock_api.rs @@ -12,15 +12,15 @@ impl Command for ClockCommand { #[tengri_proc::command(Clock)] impl ClockCommand { - fn play (self, state: &mut Clock, position: Option) -> Perhaps { + fn play (state: &mut Clock, position: Option) -> Perhaps { state.play_from(position)?; Ok(None) // TODO Some(Pause(previousPosition)) } - fn pause (self, state: &mut Clock, position: Option) -> Perhaps { + fn pause (state: &mut Clock, position: Option) -> Perhaps { state.pause_at(position)?; Ok(None) } - fn toggle_playback (self, state: &mut Clock, position: Option) -> Perhaps { + fn toggle_playback (state: &mut Clock, position: Option) -> Perhaps { if state.is_rolling() { state.pause_at(position)?; } else { @@ -28,25 +28,25 @@ impl ClockCommand { } Ok(None) } - fn seek_usec (self, state: &mut Clock, usec: f64) -> Perhaps { + fn seek_usec (state: &mut Clock, usec: f64) -> Perhaps { state.playhead.update_from_usec(usec); Ok(None) } - fn seek_sample (self, state: &mut Clock, sample: f64) -> Perhaps { + fn seek_sample (state: &mut Clock, sample: f64) -> Perhaps { state.playhead.update_from_sample(sample); Ok(None) } - fn seek_pulse (self, state: &mut Clock, pulse: f64) -> Perhaps { + fn seek_pulse (state: &mut Clock, pulse: f64) -> Perhaps { state.playhead.update_from_pulse(pulse); Ok(None) } - fn set_bpm (self, state: &mut Clock, bpm: f64) -> Perhaps { + fn set_bpm (state: &mut Clock, bpm: f64) -> Perhaps { Ok(Some(Self::SetBpm { bpm: state.timebase().bpm.set(bpm) })) } - fn set_quant (self, state: &mut Clock, quant: f64) -> Perhaps { + fn set_quant (state: &mut Clock, quant: f64) -> Perhaps { Ok(Some(Self::SetQuant { quant: state.quant.set(quant) })) } - fn set_sync (self, state: &mut Clock, sync: f64) -> Perhaps { + fn set_sync (state: &mut Clock, sync: f64) -> Perhaps { Ok(Some(Self::SetSync { sync: state.sync.set(sync) })) } } diff --git a/crates/engine/src/note/note_range.rs b/crates/engine/src/note/note_range.rs index 6804bd7a..40e7acc0 100644 --- a/crates/engine/src/note/note_range.rs +++ b/crates/engine/src/note/note_range.rs @@ -37,14 +37,23 @@ pub trait TimeRange { fn get_time_zoom (&self) -> usize { self.time_zoom().load(Ordering::Relaxed) } + fn set_time_zoom (&self, value: usize) -> usize { + self.time_zoom().swap(value, Ordering::Relaxed) + } fn time_lock (&self) -> &AtomicBool; fn get_time_lock (&self) -> bool { self.time_lock().load(Ordering::Relaxed) } + fn set_time_lock (&self, value: bool) -> bool { + self.time_lock().swap(value, Ordering::Relaxed) + } fn time_start (&self) -> &AtomicUsize; fn get_time_start (&self) -> usize { self.time_start().load(Ordering::Relaxed) } + fn set_time_start (&self, value: usize) -> usize { + self.time_start().swap(value, Ordering::Relaxed) + } fn time_axis (&self) -> &AtomicUsize; fn get_time_axis (&self) -> usize { self.time_axis().load(Ordering::Relaxed) diff --git a/deps/tengri b/deps/tengri index 2a6087e1..a16603fb 160000 --- a/deps/tengri +++ b/deps/tengri @@ -1 +1 @@ -Subproject commit 2a6087e1c7086f09b1ade22c84ff62642df7c723 +Subproject commit a16603fbc88a41c0cb4a315383cb647b0f9bb2b5 From e3b12a1d3638b7a7fa351ccd78cbfe751ef69675 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 8 May 2025 22:06:40 +0300 Subject: [PATCH 3/4] add old tengri demo --- .old/demo.rs.old | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 .old/demo.rs.old diff --git a/.old/demo.rs.old b/.old/demo.rs.old new file mode 100644 index 00000000..6b205580 --- /dev/null +++ b/.old/demo.rs.old @@ -0,0 +1,112 @@ +use tek::*; + +fn main () -> Usually<()> { + Tui::run(Arc::new(RwLock::new(Demo::new())))?; + Ok(()) +} + +pub struct Demo { + index: usize, + items: Vec>> +} + +impl Demo { + fn new () -> Self { + Self { + index: 0, + items: vec![] + } + } +} + +impl Content for Demo { + type Engine = Tui; + fn content (&self) -> dyn Render { + let border_style = Style::default().fg(Color::Rgb(0,0,0)); + Align::Center(Layers::new(move|add|{ + + add(&Background(Color::Rgb(0,128,128)))?; + + add(&Margin::XY(1, 1, Stack::down(|add|{ + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(128,96,0)))?; + add(&Border(Square(border_style)))?; + add(&Margin::XY(2, 1, "..."))?; + Ok(()) + }).debug())?; + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(128,64,0)))?; + add(&Border(Lozenge(border_style)))?; + add(&Margin::XY(4, 2, "---"))?; + Ok(()) + }).debug())?; + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(96,64,0)))?; + add(&Border(SquareBold(border_style)))?; + add(&Margin::XY(6, 3, "~~~"))?; + Ok(()) + }).debug())?; + + Ok(()) + })).debug())?; + + Ok(()) + + })) + //Align::Center(Margin::X(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(128,0,0)))?; + //add(&Stack::down(|add|{ + //add(&Margin::Y(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(0,128,0)))?; + //add(&Align::Center("12345"))?; + //add(&Align::Center("FOO")) + //})))?; + //add(&Margin::XY(1, 1, Layers::new(|add|{ + //add(&Align::Center("1234567"))?; + //add(&Align::Center("BAR"))?; + //add(&Background(Color::Rgb(0,0,128))) + //}))) + //})) + //}))) + + //Align::Y(Layers::new(|add|{ + //add(&Background(Color::Rgb(128,0,0)))?; + //add(&Margin::X(1, Align::Center(Stack::down(|add|{ + //add(&Align::X(Margin::Y(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(0,128,0)))?; + //add(&Align::Center("12345"))?; + //add(&Align::Center("FOO")) + //})))?; + //add(&Margin::XY(1, 1, Layers::new(|add|{ + //add(&Align::Center("1234567"))?; + //add(&Align::Center("BAR"))?; + //add(&Background(Color::Rgb(0,0,128))) + //})))?; + //Ok(()) + //}))))) + //})) + } +} + +impl Handle for Demo { + fn handle (&mut self, from: &TuiIn) -> Perhaps { + use KeyCode::{PageUp, PageDown}; + match from.event() { + kexp!(PageUp) => { + self.index = (self.index + 1) % self.items.len(); + }, + kexp!(PageDown) => { + self.index = if self.index > 1 { + self.index - 1 + } else { + self.items.len() - 1 + }; + }, + _ => return Ok(None) + } + Ok(Some(true)) + } +} From 1b48e10d2d6d3e8b18fd2d404d9167de3c804073 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 9 May 2025 00:06:51 +0300 Subject: [PATCH 4/4] clock: replace provide calls with expose stubs --- crates/device/src/clock/clock_api.rs | 15 ++++++++++++--- deps/tengri | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/device/src/clock/clock_api.rs b/crates/device/src/clock/clock_api.rs index 6faceb11..e9161ec4 100644 --- a/crates/device/src/clock/clock_api.rs +++ b/crates/device/src/clock/clock_api.rs @@ -1,8 +1,17 @@ use crate::*; -provide_num!(u32: |self: Clock| {}); -provide!(Option: |self: Clock| {}); -provide!(f64: |self: Clock| {}); +#[tengri_proc::expose] +impl Clock { + fn _todo_provide_u32 (&self) -> u32 { + todo!() + } + fn _todo_provide_opt_u32 (&self) -> Option { + todo!() + } + fn _todo_provide_f64 (&self) -> f64 { + todo!() + } +} impl Command for ClockCommand { fn execute (self, state: &mut T) -> Perhaps { diff --git a/deps/tengri b/deps/tengri index a16603fb..b7bb6119 160000 --- a/deps/tengri +++ b/deps/tengri @@ -1 +1 @@ -Subproject commit a16603fbc88a41c0cb4a315383cb647b0f9bb2b5 +Subproject commit b7bb6119aac975632969719c7ec5b71d97dbe356