mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
Phrase -> MidiClip, PhraseEdit -> MidiEdit
This commit is contained in:
parent
63550fabcf
commit
0530e43a2f
14 changed files with 94 additions and 94 deletions
|
|
@ -80,9 +80,9 @@ pub struct MidiPlayer {
|
|||
/// State of clock and playhead
|
||||
pub(crate) clock: ClockModel,
|
||||
/// Start time and phrase being played
|
||||
pub(crate) play_phrase: Option<(Moment, Option<Arc<RwLock<Phrase>>>)>,
|
||||
pub(crate) play_phrase: Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>,
|
||||
/// Start time and next phrase
|
||||
pub(crate) next_phrase: Option<(Moment, Option<Arc<RwLock<Phrase>>>)>,
|
||||
pub(crate) next_phrase: Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>,
|
||||
/// Play input through output.
|
||||
pub(crate) monitoring: bool,
|
||||
/// Write input to sequence.
|
||||
|
|
@ -125,7 +125,7 @@ from!(|clock: &ClockModel| MidiPlayer = Self {
|
|||
notes_in: RwLock::new([false;128]).into(),
|
||||
notes_out: RwLock::new([false;128]).into(),
|
||||
});
|
||||
from!(|state: (&ClockModel, &Arc<RwLock<Phrase>>)|MidiPlayer = {
|
||||
from!(|state: (&ClockModel, &Arc<RwLock<MidiClip>>)|MidiPlayer = {
|
||||
let (clock, phrase) = state;
|
||||
let mut model = Self::from(clock);
|
||||
model.play_phrase = Some((Moment::zero(&clock.timebase), Some(phrase.clone())));
|
||||
|
|
@ -228,16 +228,16 @@ impl HasPlayPhrase for MidiPlayer {
|
|||
fn reset_mut (&mut self) -> &mut bool {
|
||||
&mut self.reset
|
||||
}
|
||||
fn play_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<Phrase>>>)> {
|
||||
fn play_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
|
||||
&self.play_phrase
|
||||
}
|
||||
fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<Phrase>>>)> {
|
||||
fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
|
||||
&mut self.play_phrase
|
||||
}
|
||||
fn next_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<Phrase>>>)> {
|
||||
fn next_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
|
||||
&self.next_phrase
|
||||
}
|
||||
fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<Phrase>>>)> {
|
||||
fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)> {
|
||||
&mut self.next_phrase
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait HasPhrase {
|
||||
fn phrase (&self) -> &Arc<RwLock<Phrase>>;
|
||||
pub trait HasMidiClip {
|
||||
fn phrase (&self) -> &Arc<RwLock<MidiClip>>;
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! has_phrase {
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasPhrase for $Struct $(<$($L),*$($T),*>)? {
|
||||
fn phrase (&$self) -> &Arc<RwLock<Phrase>> { &$cb }
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasMidiClip for $Struct $(<$($L),*$($T),*>)? {
|
||||
fn phrase (&$self) -> &Arc<RwLock<MidiClip>> { &$cb }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A MIDI sequence.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Phrase {
|
||||
pub struct MidiClip {
|
||||
pub uuid: uuid::Uuid,
|
||||
/// Name of phrase
|
||||
pub name: String,
|
||||
|
|
@ -23,7 +23,7 @@ pub struct Phrase {
|
|||
/// Length of phrase in pulses
|
||||
pub length: usize,
|
||||
/// Notes in phrase
|
||||
pub notes: PhraseData,
|
||||
pub notes: MidiData,
|
||||
/// Whether to loop the phrase or play it once
|
||||
pub looped: bool,
|
||||
/// Start of loop
|
||||
|
|
@ -37,14 +37,14 @@ pub struct Phrase {
|
|||
}
|
||||
|
||||
/// MIDI message structural
|
||||
pub type PhraseData = Vec<Vec<MidiMessage>>;
|
||||
pub type MidiData = Vec<Vec<MidiMessage>>;
|
||||
|
||||
impl Phrase {
|
||||
impl MidiClip {
|
||||
pub fn new (
|
||||
name: impl AsRef<str>,
|
||||
looped: bool,
|
||||
length: usize,
|
||||
notes: Option<PhraseData>,
|
||||
notes: Option<MidiData>,
|
||||
color: Option<ItemPalette>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
@ -86,7 +86,7 @@ impl Phrase {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Phrase {
|
||||
impl Default for MidiClip {
|
||||
fn default () -> Self {
|
||||
Self::new(
|
||||
"Stop",
|
||||
|
|
@ -101,10 +101,10 @@ impl Default for Phrase {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Phrase {
|
||||
impl PartialEq for MidiClip {
|
||||
fn eq (&self, other: &Self) -> bool {
|
||||
self.uuid == other.uuid
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Phrase {}
|
||||
impl Eq for MidiClip {}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use crate::*;
|
|||
pub trait HasPlayPhrase: HasClock {
|
||||
fn reset (&self) -> bool;
|
||||
fn reset_mut (&mut self) -> &mut bool;
|
||||
fn play_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<Phrase>>>)>;
|
||||
fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<Phrase>>>)>;
|
||||
fn next_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<Phrase>>>)>;
|
||||
fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<Phrase>>>)>;
|
||||
fn play_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
|
||||
fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
|
||||
fn next_phrase (&self) -> &Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
|
||||
fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option<Arc<RwLock<MidiClip>>>)>;
|
||||
fn pulses_since_start (&self) -> Option<f64> {
|
||||
if let Some((started, Some(_))) = self.play_phrase().as_ref() {
|
||||
let elapsed = self.clock().playhead.pulse.get() - started.pulse.get();
|
||||
|
|
@ -25,7 +25,7 @@ pub trait HasPlayPhrase: HasClock {
|
|||
None
|
||||
}
|
||||
}
|
||||
fn enqueue_next (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) {
|
||||
fn enqueue_next (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||
let start = self.clock().next_launch_pulse() as f64;
|
||||
let instant = Moment::from_pulse(&self.clock().timebase(), start);
|
||||
let phrase = phrase.map(|p|p.clone());
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
|
|||
note_buf: &mut Vec<u8>,
|
||||
out: &mut [Vec<Vec<u8>>],
|
||||
started: &Moment,
|
||||
phrase: &Option<Arc<RwLock<Phrase>>>
|
||||
phrase: &Option<Arc<RwLock<MidiClip>>>
|
||||
) -> bool {
|
||||
// First sample to populate. Greater than 0 means that the first
|
||||
// pulse of the phrase falls somewhere in the middle of the chunk.
|
||||
|
|
@ -97,7 +97,7 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
|
|||
}
|
||||
|
||||
fn play_pulse (
|
||||
phrase: &RwLock<Phrase>,
|
||||
phrase: &RwLock<MidiClip>,
|
||||
pulse: usize,
|
||||
sample: usize,
|
||||
note_buf: &mut Vec<u8>,
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait HasPhrases {
|
||||
fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>>;
|
||||
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<Phrase>>>;
|
||||
fn phrases (&self) -> &Vec<Arc<RwLock<MidiClip>>>;
|
||||
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<MidiClip>>>;
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! has_phrases {
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasPhrases for $Struct $(<$($L),*$($T),*>)? {
|
||||
fn phrases (&$self) -> &Vec<Arc<RwLock<Phrase>>> { &$cb }
|
||||
fn phrases_mut (&mut $self) -> &mut Vec<Arc<RwLock<Phrase>>> { &mut$cb }
|
||||
fn phrases (&$self) -> &Vec<Arc<RwLock<MidiClip>>> { &$cb }
|
||||
fn phrases_mut (&mut $self) -> &mut Vec<Arc<RwLock<MidiClip>>> { &mut$cb }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PhrasePoolCommand {
|
||||
Add(usize, Phrase),
|
||||
Add(usize, MidiClip),
|
||||
Delete(usize),
|
||||
Swap(usize, usize),
|
||||
Import(usize, PathBuf),
|
||||
|
|
@ -62,7 +62,7 @@ impl<T: HasPhrases> Command<T> for PhrasePoolCommand {
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut phrase = Phrase::new("imported", true, t as usize + 1, None, None);
|
||||
let mut phrase = MidiClip::new("imported", true, t as usize + 1, None, None);
|
||||
for event in events.iter() {
|
||||
phrase.notes[event.0 as usize].push(event.2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ pub struct ArrangerTui {
|
|||
pub size: Measure<Tui>,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub editor: PhraseEditorModel,
|
||||
pub editor: MidiEditorModel,
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
impl ArrangerTui {
|
||||
|
|
@ -40,7 +40,7 @@ impl ArrangerTui {
|
|||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn selected_phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
|
||||
pub fn selected_phrase (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||
self.selected_scene()?.clips.get(self.selected.track()?)?.clone()
|
||||
}
|
||||
pub fn toggle_loop (&mut self) {
|
||||
|
|
@ -61,7 +61,7 @@ impl ArrangerTui {
|
|||
}
|
||||
from_jack!(|jack| ArrangerTui {
|
||||
let clock = ClockModel::from(jack);
|
||||
let phrase = Arc::new(RwLock::new(Phrase::new(
|
||||
let phrase = Arc::new(RwLock::new(MidiClip::new(
|
||||
"New", true, 4 * clock.timebase.ppq.get() as usize,
|
||||
None, Some(ItemColor::random().into())
|
||||
)));
|
||||
|
|
@ -97,7 +97,7 @@ render!(<Tui>|self: ArrangerTui|{
|
|||
let pool_size = if self.phrases.visible { self.splits[1] } else { 0 };
|
||||
let with_pool = |x|Split::left(false, pool_size, PoolView(&self.phrases), x);
|
||||
let status = ArrangerStatus::from(self);
|
||||
let with_editbar = |x|Tui::split_n(false, 3, PhraseEditStatus(&self.editor), x);
|
||||
let with_editbar = |x|Tui::split_n(false, 3, MidiEditStatus(&self.editor), x);
|
||||
let with_status = |x|Tui::split_n(false, 2, status, x);
|
||||
let with_size = |x|lay!([&self.size, x]);
|
||||
let arranger = ||lay!(|add|{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub struct SequencerTui {
|
|||
pub(crate) clock: ClockModel,
|
||||
pub(crate) phrases: PoolModel,
|
||||
pub(crate) player: MidiPlayer,
|
||||
pub(crate) editor: PhraseEditorModel,
|
||||
pub(crate) editor: MidiEditorModel,
|
||||
pub(crate) size: Measure<Tui>,
|
||||
pub(crate) status: bool,
|
||||
pub(crate) note_buf: Vec<u8>,
|
||||
|
|
@ -19,14 +19,14 @@ pub struct SequencerTui {
|
|||
}
|
||||
from_jack!(|jack|SequencerTui {
|
||||
let clock = ClockModel::from(jack);
|
||||
let phrase = Arc::new(RwLock::new(Phrase::new(
|
||||
let phrase = Arc::new(RwLock::new(MidiClip::new(
|
||||
"New", true, 4 * clock.timebase.ppq.get() as usize,
|
||||
None, Some(ItemColor::random().into())
|
||||
)));
|
||||
Self {
|
||||
_jack: jack.clone(),
|
||||
phrases: PoolModel::from(&phrase),
|
||||
editor: PhraseEditorModel::from(&phrase),
|
||||
editor: MidiEditorModel::from(&phrase),
|
||||
player: MidiPlayer::from((&clock, &phrase)),
|
||||
size: Measure::new(),
|
||||
midi_buf: vec![vec![];65536],
|
||||
|
|
@ -44,7 +44,7 @@ render!(<Tui>|self: SequencerTui|{
|
|||
let with_pool = move|x|Tui::split_w(false, pool_w, pool, x);
|
||||
let status = SequencerStatus::from(self);
|
||||
let with_status = |x|Tui::split_n(false, if self.status { 2 } else { 0 }, status, x);
|
||||
let with_editbar = |x|Tui::split_n(false, 3, PhraseEditStatus(&self.editor), x);
|
||||
let with_editbar = |x|Tui::split_n(false, 3, MidiEditStatus(&self.editor), x);
|
||||
let with_size = |x|lay!([self.size, x]);
|
||||
let editor = with_editbar(with_pool(Fill::wh(&self.editor)));
|
||||
let color = self.player.play_phrase().as_ref().map(|(_,p)|
|
||||
|
|
@ -85,7 +85,7 @@ handle!(<Tui>|self:SequencerTui,input|SequencerCommand::execute_with_state(self,
|
|||
Clock(ClockCommand),
|
||||
Phrases(PoolCommand),
|
||||
Editor(PhraseCommand),
|
||||
Enqueue(Option<Arc<RwLock<Phrase>>>),
|
||||
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||
}
|
||||
input_to_command!(SequencerCommand: <Tui>|state: SequencerTui, input|match input.event() {
|
||||
// TODO: k: toggle on-screen keyboard
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ pub enum ArrangerSceneCommand {
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum ArrangerClipCommand {
|
||||
Get(usize, usize),
|
||||
Put(usize, usize, Option<Arc<RwLock<Phrase>>>),
|
||||
Put(usize, usize, Option<Arc<RwLock<MidiClip>>>),
|
||||
Enqueue(usize, usize),
|
||||
Edit(Option<Arc<RwLock<Phrase>>>),
|
||||
Edit(Option<Arc<RwLock<MidiClip>>>),
|
||||
SetLoop(usize, usize, bool),
|
||||
SetColor(usize, usize, ItemPalette),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl ArrangerTui {
|
|||
/// Name of scene
|
||||
pub(crate) name: Arc<RwLock<String>>,
|
||||
/// Clips in scene, one per track
|
||||
pub(crate) clips: Vec<Option<Arc<RwLock<Phrase>>>>,
|
||||
pub(crate) clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
|
||||
/// Identifying color of scene
|
||||
pub(crate) color: ItemPalette,
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ impl ArrangerScene {
|
|||
pub fn name (&self) -> &Arc<RwLock<String>> {
|
||||
&self.name
|
||||
}
|
||||
pub fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>> {
|
||||
pub fn clips (&self) -> &Vec<Option<Arc<RwLock<MidiClip>>>> {
|
||||
&self.clips
|
||||
}
|
||||
pub fn color (&self) -> ItemPalette {
|
||||
|
|
@ -85,7 +85,7 @@ impl ArrangerScene {
|
|||
None => true
|
||||
})
|
||||
}
|
||||
pub fn clip (&self, index: usize) -> Option<&Arc<RwLock<Phrase>>> {
|
||||
pub fn clip (&self, index: usize) -> Option<&Arc<RwLock<MidiClip>>> {
|
||||
match self.clips().get(index) { Some(Some(clip)) => Some(clip), _ => None }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ impl<'a> ArrangerVClips<'a> {
|
|||
Fixed::wh(w, h, Layers::new(move |add|{
|
||||
let mut bg = TuiTheme::border_bg();
|
||||
if let Some(Some(phrase)) = scene.clips.get(index) {
|
||||
let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name;
|
||||
let name = &(phrase as &Arc<RwLock<MidiClip>>).read().unwrap().name;
|
||||
let name = format!("{}", name);
|
||||
let max_w = name.len().min((w as usize).saturating_sub(2));
|
||||
let color = phrase.read().unwrap().color;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ use KeyCode::{Char, Up, Down, Left, Right, Enter};
|
|||
use PhraseCommand::*;
|
||||
|
||||
pub trait HasEditor {
|
||||
fn editor (&self) -> &PhraseEditorModel;
|
||||
fn editor (&self) -> &MidiEditorModel;
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! has_editor {
|
||||
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
||||
impl $(<$($L),*$($T $(: $U)?),*>)? HasEditor for $Struct $(<$($L),*$($T),*>)? {
|
||||
fn editor (&$self) -> &PhraseEditorModel { &$cb }
|
||||
fn editor (&$self) -> &MidiEditorModel { &$cb }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,11 +26,11 @@ pub enum PhraseCommand {
|
|||
SetTimeScroll(usize),
|
||||
SetTimeZoom(usize),
|
||||
SetTimeLock(bool),
|
||||
Show(Option<Arc<RwLock<Phrase>>>),
|
||||
Show(Option<Arc<RwLock<MidiClip>>>),
|
||||
}
|
||||
event_map_input_to_command!(Tui: PhraseEditorModel: PhraseCommand: PhraseEditorModel::KEYS);
|
||||
event_map_input_to_command!(Tui: MidiEditorModel: PhraseCommand: MidiEditorModel::KEYS);
|
||||
|
||||
impl PhraseEditorModel {
|
||||
impl MidiEditorModel {
|
||||
const KEYS: [(TuiEvent, &'static dyn Fn(&Self)->PhraseCommand);31] = [
|
||||
(kexp!(Ctrl-Alt-Up), &|s: &Self|SetNoteScroll(s.note_point() + 3)),
|
||||
(kexp!(Ctrl-Alt-Down), &|s: &Self|SetNoteScroll(s.note_point().saturating_sub(3))),
|
||||
|
|
@ -71,8 +71,8 @@ impl PhraseEditorModel {
|
|||
}
|
||||
}
|
||||
|
||||
impl Command<PhraseEditorModel> for PhraseCommand {
|
||||
fn execute (self, state: &mut PhraseEditorModel) -> Perhaps<Self> {
|
||||
impl Command<MidiEditorModel> for PhraseCommand {
|
||||
fn execute (self, state: &mut MidiEditorModel) -> Perhaps<Self> {
|
||||
use PhraseCommand::*;
|
||||
match self {
|
||||
Show(phrase) => { state.set_phrase(phrase.as_ref()); },
|
||||
|
|
@ -92,13 +92,13 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
|||
}
|
||||
|
||||
/// Contains state for viewing and editing a phrase
|
||||
pub struct PhraseEditorModel {
|
||||
pub struct MidiEditorModel {
|
||||
/// Renders the phrase
|
||||
pub mode: Box<dyn PhraseViewMode>,
|
||||
pub size: Measure<Tui>
|
||||
}
|
||||
|
||||
impl Default for PhraseEditorModel {
|
||||
impl Default for MidiEditorModel {
|
||||
fn default () -> Self {
|
||||
let mut mode = Box::new(PianoHorizontal::new(None));
|
||||
mode.redraw();
|
||||
|
|
@ -106,28 +106,28 @@ impl Default for PhraseEditorModel {
|
|||
}
|
||||
}
|
||||
|
||||
has_size!(<Tui>|self:PhraseEditorModel|&self.size);
|
||||
render!(<Tui>|self: PhraseEditorModel|{
|
||||
has_size!(<Tui>|self:MidiEditorModel|&self.size);
|
||||
render!(<Tui>|self: MidiEditorModel|{
|
||||
self.autoscroll();
|
||||
self.autozoom();
|
||||
&self.mode
|
||||
});
|
||||
//render!(<Tui>|self: PhraseEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks
|
||||
//render!(<Tui>|self: MidiEditorModel|lay!(|add|{add(&self.size)?;add(self.mode)}));//bollocks
|
||||
|
||||
pub trait PhraseViewMode: Render<Tui> + HasSize<Tui> + MidiRange + MidiPoint + Debug + Send + Sync {
|
||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize);
|
||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize);
|
||||
fn redraw (&mut self);
|
||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>>;
|
||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<Phrase>>>;
|
||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) {
|
||||
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.map(|p|p.clone());
|
||||
self.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
impl MidiView<Tui> for PhraseEditorModel {}
|
||||
impl MidiView<Tui> for MidiEditorModel {}
|
||||
|
||||
impl MidiRange for PhraseEditorModel {
|
||||
impl MidiRange for MidiEditorModel {
|
||||
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() }
|
||||
|
|
@ -137,7 +137,7 @@ impl MidiRange for PhraseEditorModel {
|
|||
fn time_axis (&self) -> &AtomicUsize { self.mode.time_axis() }
|
||||
}
|
||||
|
||||
impl MidiPoint for PhraseEditorModel {
|
||||
impl MidiPoint for MidiEditorModel {
|
||||
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() }
|
||||
|
|
@ -146,25 +146,25 @@ impl MidiPoint for PhraseEditorModel {
|
|||
fn set_time_point (&self, x: usize) { self.mode.set_time_point(x) }
|
||||
}
|
||||
|
||||
impl PhraseViewMode for PhraseEditorModel {
|
||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
||||
impl PhraseViewMode for MidiEditorModel {
|
||||
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<Phrase>>> {
|
||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
||||
self.mode.phrase()
|
||||
}
|
||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<Phrase>>> {
|
||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> {
|
||||
self.mode.phrase_mut()
|
||||
}
|
||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) {
|
||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||
self.mode.set_phrase(phrase)
|
||||
}
|
||||
}
|
||||
|
||||
impl PhraseEditorModel {
|
||||
impl MidiEditorModel {
|
||||
/// Put note at current position
|
||||
pub fn put_note (&mut self, advance: bool) {
|
||||
let mut redraw = false;
|
||||
|
|
@ -197,22 +197,22 @@ impl PhraseEditorModel {
|
|||
}
|
||||
}
|
||||
|
||||
from!(|phrase: &Arc<RwLock<Phrase>>|PhraseEditorModel = {
|
||||
from!(|phrase: &Arc<RwLock<MidiClip>>|MidiEditorModel = {
|
||||
let mut model = Self::from(Some(phrase.clone()));
|
||||
model.redraw();
|
||||
model
|
||||
});
|
||||
|
||||
from!(|phrase: Option<Arc<RwLock<Phrase>>>|PhraseEditorModel = {
|
||||
from!(|phrase: Option<Arc<RwLock<MidiClip>>>|MidiEditorModel = {
|
||||
let mut model = Self::default();
|
||||
*model.phrase_mut() = phrase;
|
||||
model.redraw();
|
||||
model
|
||||
});
|
||||
|
||||
impl std::fmt::Debug for PhraseEditorModel {
|
||||
impl std::fmt::Debug for MidiEditorModel {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("PhraseEditorModel")
|
||||
f.debug_struct("MidiEditorModel")
|
||||
.field("mode", &self.mode)
|
||||
.finish()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16) -> impl Iter
|
|||
|
||||
/// A phrase, rendered as a horizontal piano roll.
|
||||
pub struct PianoHorizontal {
|
||||
phrase: Option<Arc<RwLock<Phrase>>>,
|
||||
phrase: Option<Arc<RwLock<MidiClip>>>,
|
||||
/// Buffer where the whole phrase is rerendered on change
|
||||
buffer: BigBuffer,
|
||||
/// Size of actual notes area
|
||||
|
|
@ -26,7 +26,7 @@ pub struct PianoHorizontal {
|
|||
}
|
||||
|
||||
impl PianoHorizontal {
|
||||
pub fn new (phrase: Option<&Arc<RwLock<Phrase>>>) -> Self {
|
||||
pub fn new (phrase: Option<&Arc<RwLock<MidiClip>>>) -> Self {
|
||||
let size = Measure::new();
|
||||
let mut range = MidiRangeModel::from((24, true));
|
||||
range.time_axis = size.x.clone();
|
||||
|
|
@ -64,7 +64,7 @@ render!(<Tui>|self: PianoHorizontal|{
|
|||
|
||||
impl PianoHorizontal {
|
||||
/// Draw the piano roll foreground using full blocks on note on and half blocks on legato: █▄ █▄ █▄
|
||||
fn draw_bg (buf: &mut BigBuffer, phrase: &Phrase, zoom: usize, note_len: usize) {
|
||||
fn draw_bg (buf: &mut BigBuffer, phrase: &MidiClip, zoom: usize, note_len: usize) {
|
||||
for (y, note) in (0..127).rev().enumerate() {
|
||||
for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) {
|
||||
let cell = buf.get_mut(x, y).unwrap();
|
||||
|
|
@ -85,7 +85,7 @@ impl PianoHorizontal {
|
|||
}
|
||||
}
|
||||
/// Draw the piano roll background using full blocks on note on and half blocks on legato: █▄ █▄ █▄
|
||||
fn draw_fg (buf: &mut BigBuffer, phrase: &Phrase, zoom: usize) {
|
||||
fn draw_fg (buf: &mut BigBuffer, phrase: &MidiClip, zoom: usize) {
|
||||
let style = Style::default().fg(phrase.color.base.rgb);//.bg(Color::Rgb(0, 0, 0));
|
||||
let mut notes_on = [false;128];
|
||||
for (x, time_start) in (0..phrase.length).step_by(zoom).enumerate() {
|
||||
|
|
@ -142,14 +142,14 @@ impl MidiPoint for PianoHorizontal {
|
|||
fn set_time_point (&self, x: usize) { self.point.set_time_point(x) }
|
||||
}
|
||||
impl PhraseViewMode for PianoHorizontal {
|
||||
fn phrase (&self) -> &Option<Arc<RwLock<Phrase>>> {
|
||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>> {
|
||||
&self.phrase
|
||||
}
|
||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<Phrase>>> {
|
||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>> {
|
||||
&mut self.phrase
|
||||
}
|
||||
/// Determine the required space to render the phrase.
|
||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
||||
fn buffer_size (&self, phrase: &MidiClip) -> (usize, usize) {
|
||||
(phrase.length / self.range.time_zoom().get(), 128)
|
||||
}
|
||||
fn redraw (&mut self) {
|
||||
|
|
@ -168,7 +168,7 @@ impl PhraseViewMode for PianoHorizontal {
|
|||
};
|
||||
self.buffer = buffer
|
||||
}
|
||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) {
|
||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||
*self.phrase_mut() = phrase.map(|p|p.clone());
|
||||
self.color = phrase.map(|p|p.read().unwrap().color.clone())
|
||||
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use FileBrowserCommand as Browse;
|
|||
pub struct PoolModel {
|
||||
pub(crate) visible: bool,
|
||||
/// Collection of phrases
|
||||
pub(crate) phrases: Vec<Arc<RwLock<Phrase>>>,
|
||||
pub(crate) phrases: Vec<Arc<RwLock<MidiClip>>>,
|
||||
/// Selected phrase
|
||||
pub(crate) phrase: AtomicUsize,
|
||||
/// Mode switch
|
||||
|
|
@ -149,10 +149,10 @@ fn to_phrases_command (state: &PoolModel, input: &TuiInput) -> Option<PoolComman
|
|||
} else {
|
||||
return None
|
||||
},
|
||||
key_pat!(Char('a')) | key_pat!(Shift-Char('A')) => Cmd::Phrase(Pool::Add(count, Phrase::new(
|
||||
key_pat!(Char('a')) | key_pat!(Shift-Char('A')) => Cmd::Phrase(Pool::Add(count, MidiClip::new(
|
||||
String::from("(new)"), true, 4 * PPQ, None, Some(ItemPalette::random())
|
||||
))),
|
||||
key_pat!(Char('i')) => Cmd::Phrase(Pool::Add(index + 1, Phrase::new(
|
||||
key_pat!(Char('i')) => Cmd::Phrase(Pool::Add(index + 1, MidiClip::new(
|
||||
String::from("(new)"), true, 4 * PPQ, None, Some(ItemPalette::random())
|
||||
))),
|
||||
key_pat!(Char('d')) | key_pat!(Shift-Char('D')) => {
|
||||
|
|
@ -167,7 +167,7 @@ impl Default for PoolModel {
|
|||
fn default () -> Self {
|
||||
Self {
|
||||
visible: true,
|
||||
phrases: vec![RwLock::new(Phrase::default()).into()],
|
||||
phrases: vec![RwLock::new(MidiClip::default()).into()],
|
||||
phrase: 0.into(),
|
||||
scroll: 0,
|
||||
mode: None,
|
||||
|
|
@ -175,7 +175,7 @@ impl Default for PoolModel {
|
|||
}
|
||||
}
|
||||
}
|
||||
from!(|phrase:&Arc<RwLock<Phrase>>|PoolModel = {
|
||||
from!(|phrase:&Arc<RwLock<MidiClip>>|PoolModel = {
|
||||
let mut model = Self::default();
|
||||
model.phrases.push(phrase.clone());
|
||||
model.phrase.store(1, Relaxed);
|
||||
|
|
@ -214,7 +214,7 @@ render!(<Tui>|self: PoolView<'a>|{
|
|||
Some(PoolMode::Export(_, ref file_picker)) => add(file_picker),
|
||||
_ => Ok(for (i, phrase) in phrases.iter().enumerate() {
|
||||
add(&lay!(|add|{
|
||||
let Phrase { ref name, color, length, .. } = *phrase.read().unwrap();
|
||||
let MidiClip { ref name, color, length, .. } = *phrase.read().unwrap();
|
||||
let mut length = PhraseLength::new(length, None);
|
||||
if let Some(PoolMode::Length(phrase, new_length, focus)) = mode {
|
||||
if i == *phrase {
|
||||
|
|
@ -267,7 +267,7 @@ impl PhraseSelector {
|
|||
// beats elapsed
|
||||
pub fn play_phrase <T: HasPlayPhrase + HasClock> (state: &T) -> Self {
|
||||
let (name, color) = if let Some((_, Some(phrase))) = state.play_phrase() {
|
||||
let Phrase { ref name, color, .. } = *phrase.read().unwrap();
|
||||
let MidiClip { ref name, color, .. } = *phrase.read().unwrap();
|
||||
(name.clone(), color)
|
||||
} else {
|
||||
("".to_string(), ItemPalette::from(TuiTheme::g(64)))
|
||||
|
|
@ -282,7 +282,7 @@ impl PhraseSelector {
|
|||
// beats until switchover
|
||||
pub fn next_phrase <T: HasPlayPhrase> (state: &T) -> Self {
|
||||
let (time, name, color) = if let Some((t, Some(phrase))) = state.next_phrase() {
|
||||
let Phrase { ref name, color, .. } = *phrase.read().unwrap();
|
||||
let MidiClip { ref name, color, .. } = *phrase.read().unwrap();
|
||||
let time = {
|
||||
let target = t.pulse.get();
|
||||
let current = state.clock().playhead.pulse.get();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct PhraseEditStatus<'a>(pub &'a PhraseEditorModel);
|
||||
render!(<Tui>|self:PhraseEditStatus<'a>|{
|
||||
pub struct MidiEditStatus<'a>(pub &'a MidiEditorModel);
|
||||
render!(<Tui>|self:MidiEditStatus<'a>|{
|
||||
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
|
||||
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue