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