mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
disable piano mode switch for now
This commit is contained in:
parent
57158d4d6f
commit
00453a7697
5 changed files with 154 additions and 157 deletions
|
|
@ -5,6 +5,8 @@ pub struct EventMap<'a, const N: usize, E, T, U>(
|
||||||
pub Option<&'a dyn Fn(T) -> U>,
|
pub Option<&'a dyn Fn(T) -> U>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pub type KeyMapping<const N: usize, E, T, U> = [(E, &'static dyn Fn(&T)->U);N];
|
||||||
|
|
||||||
impl<'a, const N: usize, E: PartialEq, T, U> EventMap<'a, N, E, T, U> {
|
impl<'a, const N: usize, E: PartialEq, T, U> EventMap<'a, N, E, T, U> {
|
||||||
pub fn handle (&self, context: T, event: &E) -> Option<U> {
|
pub fn handle (&self, context: T, event: &E) -> Option<U> {
|
||||||
for (binding, handler) in self.0.iter() {
|
for (binding, handler) in self.0.iter() {
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ render!(Tui: (self: Groovebox) => {
|
||||||
))),
|
))),
|
||||||
Bsp::n(
|
Bsp::n(
|
||||||
MidiEditStatus(&self.editor),
|
MidiEditStatus(&self.editor),
|
||||||
Fill::xy(Align::c("kyp"))
|
Fill::xy(&self.editor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use MidiEditCommand::*;
|
||||||
pub trait HasEditor {
|
pub trait HasEditor {
|
||||||
fn editor (&self) -> &MidiEditor;
|
fn editor (&self) -> &MidiEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! has_editor {
|
#[macro_export] macro_rules! has_editor {
|
||||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasEditor for $Struct $(<$($L),*$($T),*>)? {
|
impl $(<$($L),*$($T $(: $U)?),*>)? HasEditor for $Struct $(<$($L),*$($T),*>)? {
|
||||||
|
|
@ -14,6 +13,144 @@ pub trait HasEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains state for viewing and editing a phrase
|
||||||
|
pub struct MidiEditor {
|
||||||
|
pub mode: PianoHorizontal,
|
||||||
|
pub size: Measure<Tui>
|
||||||
|
}
|
||||||
|
|
||||||
|
from!(|phrase: &Arc<RwLock<MidiClip>>|MidiEditor = {
|
||||||
|
let mut model = Self::from(Some(phrase.clone()));
|
||||||
|
model.redraw();
|
||||||
|
model
|
||||||
|
});
|
||||||
|
|
||||||
|
from!(|phrase: Option<Arc<RwLock<MidiClip>>>|MidiEditor = {
|
||||||
|
let mut model = Self::default();
|
||||||
|
*model.phrase_mut() = phrase;
|
||||||
|
model.redraw();
|
||||||
|
model
|
||||||
|
});
|
||||||
|
|
||||||
|
impl Default for MidiEditor {
|
||||||
|
fn default () -> Self {
|
||||||
|
let mut mode = PianoHorizontal::new(None);
|
||||||
|
mode.redraw();
|
||||||
|
Self { mode, size: Measure::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
has_size!(<Tui>|self: MidiEditor|&self.size);
|
||||||
|
|
||||||
|
render!(Tui: (self: MidiEditor) => {
|
||||||
|
self.autoscroll();
|
||||||
|
self.autozoom();
|
||||||
|
Bsp::a(&self.size, &self.mode)
|
||||||
|
});
|
||||||
|
|
||||||
|
impl MidiView<Tui> for MidiEditor {}
|
||||||
|
|
||||||
|
impl TimeRange for MidiEditor {
|
||||||
|
fn time_len (&self) -> &AtomicUsize { self.mode.time_len() }
|
||||||
|
fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() }
|
||||||
|
fn time_lock (&self) -> &AtomicBool { self.mode.time_lock() }
|
||||||
|
fn time_start (&self) -> &AtomicUsize { self.mode.time_start() }
|
||||||
|
fn time_axis (&self) -> &AtomicUsize { self.mode.time_axis() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NoteRange for MidiEditor {
|
||||||
|
fn note_lo (&self) -> &AtomicUsize { self.mode.note_lo() }
|
||||||
|
fn note_axis (&self) -> &AtomicUsize { self.mode.note_axis() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NotePoint for MidiEditor {
|
||||||
|
fn note_len (&self) -> usize { self.mode.note_len() }
|
||||||
|
fn set_note_len (&self, x: usize) { self.mode.set_note_len(x) }
|
||||||
|
fn note_point (&self) -> usize { self.mode.note_point() }
|
||||||
|
fn set_note_point (&self, x: usize) { self.mode.set_note_point(x) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimePoint for MidiEditor {
|
||||||
|
fn time_point (&self) -> usize { self.mode.time_point() }
|
||||||
|
fn set_time_point (&self, x: usize) { self.mode.set_time_point(x) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MidiViewMode for MidiEditor {
|
||||||
|
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) {
|
||||||
|
self.mode.buffer_size(phrase)
|
||||||
|
}
|
||||||
|
fn redraw (&mut self) {
|
||||||
|
self.mode.redraw()
|
||||||
|
}
|
||||||
|
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
||||||
|
self.mode.phrase()
|
||||||
|
}
|
||||||
|
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> {
|
||||||
|
self.mode.phrase_mut()
|
||||||
|
}
|
||||||
|
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
|
self.mode.set_phrase(phrase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MidiEditor {
|
||||||
|
/// Put note at current position
|
||||||
|
pub fn put_note (&mut self, advance: bool) {
|
||||||
|
let mut redraw = false;
|
||||||
|
if let Some(phrase) = self.phrase() {
|
||||||
|
let mut phrase = phrase.write().unwrap();
|
||||||
|
let note_start = self.time_point();
|
||||||
|
let note_point = self.note_point();
|
||||||
|
let note_len = self.note_len();
|
||||||
|
let note_end = note_start + (note_len.saturating_sub(1));
|
||||||
|
let key: u7 = u7::from(note_point as u8);
|
||||||
|
let vel: u7 = 100.into();
|
||||||
|
let length = phrase.length;
|
||||||
|
let note_end = note_end % length;
|
||||||
|
let note_on = MidiMessage::NoteOn { key, vel };
|
||||||
|
if !phrase.notes[note_start].iter().any(|msg|*msg == note_on) {
|
||||||
|
phrase.notes[note_start].push(note_on);
|
||||||
|
}
|
||||||
|
let note_off = MidiMessage::NoteOff { key, vel };
|
||||||
|
if !phrase.notes[note_end].iter().any(|msg|*msg == note_off) {
|
||||||
|
phrase.notes[note_end].push(note_off);
|
||||||
|
}
|
||||||
|
if advance {
|
||||||
|
self.set_time_point(note_end);
|
||||||
|
}
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
|
if redraw {
|
||||||
|
self.mode.redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MidiViewMode: HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
||||||
|
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize);
|
||||||
|
fn redraw (&mut self);
|
||||||
|
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
||||||
|
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>>;
|
||||||
|
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
|
*self.phrase_mut() = phrase.cloned();
|
||||||
|
self.redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content<Tui> for Box<dyn MidiViewMode> {
|
||||||
|
fn content (&self) -> impl Content<Tui> {
|
||||||
|
Some(&(*self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for MidiEditor {
|
||||||
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
f.debug_struct("MidiEditor")
|
||||||
|
.field("mode", &self.mode)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MidiEditCommand {
|
pub enum MidiEditCommand {
|
||||||
// TODO: 1-9 seek markers that by default start every 8th of the phrase
|
// TODO: 1-9 seek markers that by default start every 8th of the phrase
|
||||||
|
|
@ -31,8 +168,6 @@ pub enum MidiEditCommand {
|
||||||
|
|
||||||
event_map_input_to_command!(Tui: MidiEditor: MidiEditCommand: MidiEditor::KEYS);
|
event_map_input_to_command!(Tui: MidiEditor: MidiEditCommand: MidiEditor::KEYS);
|
||||||
|
|
||||||
pub(crate) type KeyMapping<const N: usize, E, T, U> = [(E, &'static dyn Fn(&T)->U);N];
|
|
||||||
|
|
||||||
impl MidiEditor {
|
impl MidiEditor {
|
||||||
const KEYS: KeyMapping<31, Event, Self, MidiEditCommand> = [
|
const KEYS: KeyMapping<31, Event, Self, MidiEditCommand> = [
|
||||||
(kexp!(Ctrl-Alt-Up), &|s: &Self|SetNoteScroll(s.note_point() + 3)),
|
(kexp!(Ctrl-Alt-Up), &|s: &Self|SetNoteScroll(s.note_point() + 3)),
|
||||||
|
|
@ -93,144 +228,3 @@ impl Command<MidiEditor> for MidiEditCommand {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains state for viewing and editing a phrase
|
|
||||||
pub struct MidiEditor {
|
|
||||||
/// Contents the phrase
|
|
||||||
pub mode: Box<dyn PhraseViewMode>,
|
|
||||||
pub size: Measure<Tui>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MidiEditor {
|
|
||||||
fn default () -> Self {
|
|
||||||
let mut mode = Box::new(PianoHorizontal::new(None));
|
|
||||||
mode.redraw();
|
|
||||||
Self { mode, size: Measure::new() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
has_size!(<Tui>|self: MidiEditor|&self.size);
|
|
||||||
|
|
||||||
render!(Tui: (self: MidiEditor) => {
|
|
||||||
self.autoscroll();
|
|
||||||
self.autozoom();
|
|
||||||
&self.mode
|
|
||||||
});
|
|
||||||
|
|
||||||
//render!(<Tui>|self: MidiEditor|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks
|
|
||||||
|
|
||||||
pub trait PhraseViewMode: HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
|
||||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize);
|
|
||||||
fn redraw (&mut self);
|
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
|
||||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>>;
|
|
||||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
|
||||||
*self.phrase_mut() = phrase.cloned();
|
|
||||||
self.redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Content<Tui> for Box<dyn PhraseViewMode> {
|
|
||||||
fn content (&self) -> impl Content<Tui> {
|
|
||||||
Some(&(*self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MidiView<Tui> for MidiEditor {}
|
|
||||||
|
|
||||||
impl TimeRange for MidiEditor {
|
|
||||||
fn time_len (&self) -> &AtomicUsize { self.mode.time_len() }
|
|
||||||
fn time_zoom (&self) -> &AtomicUsize { self.mode.time_zoom() }
|
|
||||||
fn time_lock (&self) -> &AtomicBool { self.mode.time_lock() }
|
|
||||||
fn time_start (&self) -> &AtomicUsize { self.mode.time_start() }
|
|
||||||
fn time_axis (&self) -> &AtomicUsize { self.mode.time_axis() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NoteRange for MidiEditor {
|
|
||||||
fn note_lo (&self) -> &AtomicUsize { self.mode.note_lo() }
|
|
||||||
fn note_axis (&self) -> &AtomicUsize { self.mode.note_axis() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NotePoint for MidiEditor {
|
|
||||||
fn note_len (&self) -> usize { self.mode.note_len() }
|
|
||||||
fn set_note_len (&self, x: usize) { self.mode.set_note_len(x) }
|
|
||||||
fn note_point (&self) -> usize { self.mode.note_point() }
|
|
||||||
fn set_note_point (&self, x: usize) { self.mode.set_note_point(x) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TimePoint for MidiEditor {
|
|
||||||
fn time_point (&self) -> usize { self.mode.time_point() }
|
|
||||||
fn set_time_point (&self, x: usize) { self.mode.set_time_point(x) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhraseViewMode for MidiEditor {
|
|
||||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) {
|
|
||||||
self.mode.buffer_size(phrase)
|
|
||||||
}
|
|
||||||
fn redraw (&mut self) {
|
|
||||||
self.mode.redraw()
|
|
||||||
}
|
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
|
||||||
self.mode.phrase()
|
|
||||||
}
|
|
||||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> {
|
|
||||||
self.mode.phrase_mut()
|
|
||||||
}
|
|
||||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
|
||||||
self.mode.set_phrase(phrase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MidiEditor {
|
|
||||||
/// Put note at current position
|
|
||||||
pub fn put_note (&mut self, advance: bool) {
|
|
||||||
let mut redraw = false;
|
|
||||||
if let Some(phrase) = self.phrase() {
|
|
||||||
let mut phrase = phrase.write().unwrap();
|
|
||||||
let note_start = self.time_point();
|
|
||||||
let note_point = self.note_point();
|
|
||||||
let note_len = self.note_len();
|
|
||||||
let note_end = note_start + (note_len.saturating_sub(1));
|
|
||||||
let key: u7 = u7::from(note_point as u8);
|
|
||||||
let vel: u7 = 100.into();
|
|
||||||
let length = phrase.length;
|
|
||||||
let note_end = note_end % length;
|
|
||||||
let note_on = MidiMessage::NoteOn { key, vel };
|
|
||||||
if !phrase.notes[note_start].iter().any(|msg|*msg == note_on) {
|
|
||||||
phrase.notes[note_start].push(note_on);
|
|
||||||
}
|
|
||||||
let note_off = MidiMessage::NoteOff { key, vel };
|
|
||||||
if !phrase.notes[note_end].iter().any(|msg|*msg == note_off) {
|
|
||||||
phrase.notes[note_end].push(note_off);
|
|
||||||
}
|
|
||||||
if advance {
|
|
||||||
self.set_time_point(note_end);
|
|
||||||
}
|
|
||||||
redraw = true;
|
|
||||||
}
|
|
||||||
if redraw {
|
|
||||||
self.mode.redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
from!(|phrase: &Arc<RwLock<MidiClip>>|MidiEditor = {
|
|
||||||
let mut model = Self::from(Some(phrase.clone()));
|
|
||||||
model.redraw();
|
|
||||||
model
|
|
||||||
});
|
|
||||||
|
|
||||||
from!(|phrase: Option<Arc<RwLock<MidiClip>>>|MidiEditor = {
|
|
||||||
let mut model = Self::default();
|
|
||||||
*model.phrase_mut() = phrase;
|
|
||||||
model.redraw();
|
|
||||||
model
|
|
||||||
});
|
|
||||||
|
|
||||||
impl std::fmt::Debug for MidiEditor {
|
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
|
||||||
f.debug_struct("MidiEditor")
|
|
||||||
.field("mode", &self.mode)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
23
src/piano.rs
23
src/piano.rs
|
|
@ -26,21 +26,22 @@ pub struct PianoHorizontal {
|
||||||
|
|
||||||
impl PianoHorizontal {
|
impl PianoHorizontal {
|
||||||
pub fn new (phrase: Option<&Arc<RwLock<MidiClip>>>) -> Self {
|
pub fn new (phrase: Option<&Arc<RwLock<MidiClip>>>) -> Self {
|
||||||
let size = Measure::new();
|
let size = Measure::new();
|
||||||
let mut range = MidiRangeModel::from((24, true));
|
let mut range = MidiRangeModel::from((24, true));
|
||||||
range.time_axis = size.x.clone();
|
range.time_axis = size.x.clone();
|
||||||
range.note_axis = size.y.clone();
|
range.note_axis = size.y.clone();
|
||||||
let color = phrase.as_ref()
|
let mut piano = Self {
|
||||||
.map(|p|p.read().unwrap().color)
|
keys_width: 5,
|
||||||
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
|
size,
|
||||||
Self {
|
range,
|
||||||
buffer: Default::default(),
|
buffer: Default::default(),
|
||||||
point: MidiPointModel::default(),
|
point: MidiPointModel::default(),
|
||||||
phrase: phrase.cloned(),
|
phrase: phrase.cloned(),
|
||||||
size,
|
color: phrase.as_ref()
|
||||||
range,
|
.map(|p|p.read().unwrap().color)
|
||||||
color,
|
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64)))),
|
||||||
keys_width: 5
|
};
|
||||||
}
|
piano.redraw();
|
||||||
|
piano
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ impl TimePoint for PianoHorizontal {
|
||||||
fn time_point (&self) -> usize { self.point.time_point() }
|
fn time_point (&self) -> usize { self.point.time_point() }
|
||||||
fn set_time_point (&self, x: usize) { self.point.set_time_point(x) }
|
fn set_time_point (&self, x: usize) { self.point.set_time_point(x) }
|
||||||
}
|
}
|
||||||
impl PhraseViewMode for PianoHorizontal {
|
impl MidiViewMode for PianoHorizontal {
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
||||||
&self.phrase
|
&self.phrase
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue