mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
tons more lint fixes
This commit is contained in:
parent
e96faeb6d3
commit
96f360791b
23 changed files with 121 additions and 122 deletions
|
|
@ -6,7 +6,7 @@ pub use clojure_reader::edn::Edn;
|
||||||
|
|
||||||
pub trait FromEdn<C>: Sized {
|
pub trait FromEdn<C>: Sized {
|
||||||
const ID: &'static str;
|
const ID: &'static str;
|
||||||
fn from_edn <'e> (context: C, expr: &[Edn<'e>]) -> Usually<Self>;
|
fn from_edn (context: C, expr: &[Edn<'_>]) -> Usually<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements the [FromEdn] trait.
|
/// Implements the [FromEdn] trait.
|
||||||
|
|
@ -46,7 +46,7 @@ from_edn!(|jack = &Arc<RwLock<JackClient>>, "sampler", args| -> crate::Sampler {
|
||||||
dir = String::from(*n);
|
dir = String::from(*n);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Edn::List(args) => match args.get(0) {
|
Edn::List(args) => match args.first() {
|
||||||
Some(Edn::Symbol("sample")) => {
|
Some(Edn::Symbol("sample")) => {
|
||||||
let (midi, sample) = MidiSample::from_edn((jack, &dir), &args[1..])?;
|
let (midi, sample) = MidiSample::from_edn((jack, &dir), &args[1..])?;
|
||||||
if let Some(midi) = midi {
|
if let Some(midi) = midi {
|
||||||
|
|
@ -62,7 +62,6 @@ from_edn!(|jack = &Arc<RwLock<JackClient>>, "sampler", args| -> crate::Sampler {
|
||||||
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
|
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
name: name.into(),
|
|
||||||
mapped: samples,
|
mapped: samples,
|
||||||
unmapped: Default::default(),
|
unmapped: Default::default(),
|
||||||
voices: Default::default(),
|
voices: Default::default(),
|
||||||
|
|
@ -70,6 +69,7 @@ from_edn!(|jack = &Arc<RwLock<JackClient>>, "sampler", args| -> crate::Sampler {
|
||||||
audio_outs: vec![],
|
audio_outs: vec![],
|
||||||
output_gain: 0.,
|
output_gain: 0.,
|
||||||
midi_in,
|
midi_in,
|
||||||
|
name,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,9 @@ pub trait MidiView<E: Engine>: MidiRange + MidiPoint + HasSize<E> {
|
||||||
}
|
}
|
||||||
/// Make sure range is within display
|
/// Make sure range is within display
|
||||||
fn autozoom (&self) {
|
fn autozoom (&self) {
|
||||||
let time_len = self.time_len().get();
|
let time_len = self.time_len().get();
|
||||||
let time_axis = self.time_axis().get();
|
let time_axis = self.time_axis().get();
|
||||||
let mut time_zoom = self.time_zoom().get();
|
let time_zoom = self.time_zoom().get();
|
||||||
//while time_len.div_ceil(time_zoom) > time_axis {
|
//while time_len.div_ceil(time_zoom) > time_axis {
|
||||||
//println!("\r{time_len} {time_zoom} {time_axis}");
|
//println!("\r{time_len} {time_zoom} {time_axis}");
|
||||||
//time_zoom = Note::next(time_zoom);
|
//time_zoom = Note::next(time_zoom);
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
|
||||||
// If it's time to switch to the next phrase:
|
// If it's time to switch to the next phrase:
|
||||||
if start <= sample0.saturating_sub(sample) {
|
if start <= sample0.saturating_sub(sample) {
|
||||||
// Samples elapsed since phrase was supposed to start
|
// Samples elapsed since phrase was supposed to start
|
||||||
let skipped = sample0 - start;
|
let _skipped = sample0 - start;
|
||||||
// Switch over to enqueued phrase
|
// Switch over to enqueued phrase
|
||||||
let started = Moment::from_sample(self.clock().timebase(), start as f64);
|
let started = Moment::from_sample(self.clock().timebase(), start as f64);
|
||||||
// Launch enqueued phrase
|
// Launch enqueued phrase
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ pub trait MidiRecordApi: HasClock + HasPlayPhrase + HasMidiIns {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((start_at, phrase)) = &self.next_phrase() {
|
if let Some((start_at, _clip)) = &self.next_phrase() {
|
||||||
// TODO switch to next phrase and record into it
|
// TODO switch to next phrase and record into it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ mod piano_h; pub(crate) use self::piano_h::*;
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub fn render <F: Fn(&mut TuiOutput)->Usually<()>+Send+Sync> (render: F) -> impl Render<Tui> {
|
pub fn render <F: Fn(&mut TuiOutput)->Usually<()>+Send+Sync> (render: F) -> impl Render<Tui> {
|
||||||
Widget::new(|_|Ok(Some([0u16,0u16].into())), render)
|
Widget::new(|_|Ok(Some([0u16,0u16])), render)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -37,13 +37,11 @@ pub enum GrooveboxCommand {
|
||||||
Sampler(SamplerCommand),
|
Sampler(SamplerCommand),
|
||||||
}
|
}
|
||||||
handle!(<Tui>|self:GrooveboxTui,input|GrooveboxCommand::execute_with_state(self, input));
|
handle!(<Tui>|self:GrooveboxTui,input|GrooveboxCommand::execute_with_state(self, input));
|
||||||
input_to_command!(GrooveboxCommand: <Tui>|state:GrooveboxTui,input|match input.event() {
|
input_to_command!(GrooveboxCommand: <Tui>|state:GrooveboxTui,input|match state.focus {
|
||||||
_ => match state.focus {
|
GrooveboxFocus::Sequencer => GrooveboxCommand::Sequencer(
|
||||||
GrooveboxFocus::Sequencer => GrooveboxCommand::Sequencer(
|
SequencerCommand::input_to_command(&state.sequencer, input)?),
|
||||||
SequencerCommand::input_to_command(&state.sequencer, input)?),
|
GrooveboxFocus::Sampler => GrooveboxCommand::Sampler(
|
||||||
GrooveboxFocus::Sampler => GrooveboxCommand::Sampler(
|
SamplerCommand::input_to_command(&state.sampler, input)?),
|
||||||
SamplerCommand::input_to_command(&state.sampler, input)?),
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
command!(|self:GrooveboxCommand,state:GrooveboxTui|match self {
|
command!(|self:GrooveboxCommand,state:GrooveboxTui|match self {
|
||||||
GrooveboxCommand::Sequencer(command) =>
|
GrooveboxCommand::Sequencer(command) =>
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,17 @@ use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use KeyCode::Char;
|
use KeyCode::Char;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use symphonia::core::codecs::CODEC_TYPE_NULL;
|
use symphonia::{
|
||||||
use symphonia::core::errors::Error;
|
core::{
|
||||||
use symphonia::core::io::MediaSourceStream;
|
formats::Packet,
|
||||||
use symphonia::core::probe::Hint;
|
codecs::{Decoder, CODEC_TYPE_NULL},
|
||||||
use symphonia::core::audio::SampleBuffer;
|
errors::Error,
|
||||||
use symphonia::default::get_codecs;
|
io::MediaSourceStream,
|
||||||
|
probe::Hint,
|
||||||
|
audio::SampleBuffer,
|
||||||
|
},
|
||||||
|
default::get_codecs,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct SamplerTui {
|
pub struct SamplerTui {
|
||||||
pub state: Sampler,
|
pub state: Sampler,
|
||||||
|
|
@ -305,8 +310,8 @@ fn scan (dir: &PathBuf) -> Usually<(Vec<OsString>, Vec<OsString>)> {
|
||||||
|
|
||||||
impl Sample {
|
impl Sample {
|
||||||
fn from_file (path: &PathBuf) -> Usually<Self> {
|
fn from_file (path: &PathBuf) -> Usually<Self> {
|
||||||
let mut sample = Self::default();
|
let name = path.file_name().unwrap().to_string_lossy().into();
|
||||||
sample.name = path.file_name().unwrap().to_string_lossy().into();
|
let mut sample = Self { name, ..Default::default() };
|
||||||
// Use file extension if present
|
// Use file extension if present
|
||||||
let mut hint = Hint::new();
|
let mut hint = Hint::new();
|
||||||
if let Some(ext) = path.extension() {
|
if let Some(ext) = path.extension() {
|
||||||
|
|
@ -322,48 +327,14 @@ impl Sample {
|
||||||
&Default::default()
|
&Default::default()
|
||||||
)?;
|
)?;
|
||||||
let mut format = probed.format;
|
let mut format = probed.format;
|
||||||
let mut decoder = get_codecs().make(
|
let params = &format.tracks().iter()
|
||||||
&format.tracks().iter()
|
.find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
|
||||||
.find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
|
.expect("no tracks found")
|
||||||
.expect("no tracks found")
|
.codec_params;
|
||||||
.codec_params,
|
let mut decoder = get_codecs().make(params, &Default::default())?;
|
||||||
&Default::default()
|
|
||||||
)?;
|
|
||||||
loop {
|
loop {
|
||||||
match format.next_packet() {
|
match format.next_packet() {
|
||||||
Ok(packet) => {
|
Ok(packet) => sample.decode_packet(&mut decoder, packet)?,
|
||||||
// Decode a packet
|
|
||||||
let decoded = match decoder.decode(&packet) {
|
|
||||||
Ok(decoded) => decoded,
|
|
||||||
Err(err) => { return Err(err.into()); }
|
|
||||||
};
|
|
||||||
// Determine sample rate
|
|
||||||
let spec = *decoded.spec();
|
|
||||||
if let Some(rate) = sample.rate {
|
|
||||||
if rate != spec.rate as usize {
|
|
||||||
panic!("sample rate changed");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sample.rate = Some(spec.rate as usize);
|
|
||||||
}
|
|
||||||
// Determine channel count
|
|
||||||
while sample.channels.len() < spec.channels.count() {
|
|
||||||
sample.channels.push(vec![]);
|
|
||||||
}
|
|
||||||
// Load sample
|
|
||||||
let mut samples = SampleBuffer::new(
|
|
||||||
decoded.frames() as u64,
|
|
||||||
spec
|
|
||||||
);
|
|
||||||
if samples.capacity() > 0 {
|
|
||||||
samples.copy_interleaved_ref(decoded);
|
|
||||||
for frame in samples.samples().chunks(spec.channels.count()) {
|
|
||||||
for (chan, frame) in frame.iter().enumerate() {
|
|
||||||
sample.channels[chan].push(*frame)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(Error::IoError(_)) => break decoder.last_decoded(),
|
Err(Error::IoError(_)) => break decoder.last_decoded(),
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|
@ -371,6 +342,41 @@ impl Sample {
|
||||||
sample.end = sample.channels.iter().fold(0, |l, c|l + c.len());
|
sample.end = sample.channels.iter().fold(0, |l, c|l + c.len());
|
||||||
Ok(sample)
|
Ok(sample)
|
||||||
}
|
}
|
||||||
|
fn decode_packet (
|
||||||
|
&mut self, decoder: &mut Box<dyn Decoder>, packet: Packet
|
||||||
|
) -> Usually<()> {
|
||||||
|
// Decode a packet
|
||||||
|
let decoded = decoder
|
||||||
|
.decode(&packet)
|
||||||
|
.map_err(|e|Box::<dyn crate::Error>::from(e))?;
|
||||||
|
// Determine sample rate
|
||||||
|
let spec = *decoded.spec();
|
||||||
|
if let Some(rate) = self.rate {
|
||||||
|
if rate != spec.rate as usize {
|
||||||
|
panic!("sample rate changed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.rate = Some(spec.rate as usize);
|
||||||
|
}
|
||||||
|
// Determine channel count
|
||||||
|
while self.channels.len() < spec.channels.count() {
|
||||||
|
self.channels.push(vec![]);
|
||||||
|
}
|
||||||
|
// Load sample
|
||||||
|
let mut samples = SampleBuffer::new(
|
||||||
|
decoded.frames() as u64,
|
||||||
|
spec
|
||||||
|
);
|
||||||
|
if samples.capacity() > 0 {
|
||||||
|
samples.copy_interleaved_ref(decoded);
|
||||||
|
for frame in samples.samples().chunks(spec.channels.count()) {
|
||||||
|
for (chan, frame) in frame.iter().enumerate() {
|
||||||
|
self.channels[chan].push(*frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_sample (
|
fn draw_sample (
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ fn to_arrangement_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arra
|
||||||
key_pat!(Down) => Some(
|
key_pat!(Down) => Some(
|
||||||
Cmd::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1))))),
|
Cmd::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1))))),
|
||||||
key_pat!(Left) =>
|
key_pat!(Left) =>
|
||||||
return None,
|
None,
|
||||||
key_pat!(Right) => Some(
|
key_pat!(Right) => Some(
|
||||||
Cmd::Select(Selected::Clip(0, s))),
|
Cmd::Select(Selected::Clip(0, s))),
|
||||||
|
|
||||||
|
|
@ -221,7 +221,7 @@ fn to_arrangement_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arra
|
||||||
key_pat!(Char('c')) => Some(Cmd::Track(Track::SetColor(t, ItemPalette::random()))),
|
key_pat!(Char('c')) => Some(Cmd::Track(Track::SetColor(t, ItemPalette::random()))),
|
||||||
|
|
||||||
key_pat!(Up) =>
|
key_pat!(Up) =>
|
||||||
return None,
|
None,
|
||||||
key_pat!(Down) => Some(
|
key_pat!(Down) => Some(
|
||||||
Cmd::Select(Selected::Clip(t, 0))),
|
Cmd::Select(Selected::Clip(t, 0))),
|
||||||
key_pat!(Left) => Some(
|
key_pat!(Left) => Some(
|
||||||
|
|
@ -237,11 +237,11 @@ fn to_arrangement_command (state: &ArrangerTui, input: &TuiInput) -> Option<Arra
|
||||||
key_pat!(Char('c')) => Some(Cmd::Color(ItemPalette::random())),
|
key_pat!(Char('c')) => Some(Cmd::Color(ItemPalette::random())),
|
||||||
|
|
||||||
key_pat!(Up) =>
|
key_pat!(Up) =>
|
||||||
return None,
|
None,
|
||||||
key_pat!(Down) => Some(
|
key_pat!(Down) => Some(
|
||||||
Cmd::Select(Selected::Scene(0))),
|
Cmd::Select(Selected::Scene(0))),
|
||||||
key_pat!(Left) =>
|
key_pat!(Left) =>
|
||||||
return None,
|
None,
|
||||||
key_pat!(Right) => Some(
|
key_pat!(Right) => Some(
|
||||||
Cmd::Select(Selected::Track(0))),
|
Cmd::Select(Selected::Track(0))),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ render!(<Tui>|self: ArrangerMode|{});
|
||||||
/// Arranger display mode can be cycled
|
/// Arranger display mode can be cycled
|
||||||
impl ArrangerMode {
|
impl ArrangerMode {
|
||||||
/// Cycle arranger display mode
|
/// Cycle arranger display mode
|
||||||
pub fn to_next (&mut self) {
|
pub fn next (&mut self) {
|
||||||
*self = match self {
|
*self = match self {
|
||||||
Self::H => Self::V(1),
|
Self::H => Self::V(1),
|
||||||
Self::V(1) => Self::V(2),
|
Self::V(1) => Self::V(2),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ impl ArrangerTui {
|
||||||
let scene = ArrangerScene {
|
let scene = ArrangerScene {
|
||||||
name: Arc::new(name.into()),
|
name: Arc::new(name.into()),
|
||||||
clips: vec![None;self.tracks.len()],
|
clips: vec![None;self.tracks.len()],
|
||||||
color: color.unwrap_or_else(||ItemPalette::random()),
|
color: color.unwrap_or_else(ItemPalette::random),
|
||||||
};
|
};
|
||||||
self.scenes.push(scene);
|
self.scenes.push(scene);
|
||||||
let index = self.scenes.len() - 1;
|
let index = self.scenes.len() - 1;
|
||||||
|
|
@ -20,10 +20,10 @@ impl ArrangerTui {
|
||||||
format!("S{:3>}", self.scenes.len() + 1)
|
format!("S{:3>}", self.scenes.len() + 1)
|
||||||
}
|
}
|
||||||
pub fn selected_scene (&self) -> Option<&ArrangerScene> {
|
pub fn selected_scene (&self) -> Option<&ArrangerScene> {
|
||||||
self.selected.scene().map(|s|self.scenes.get(s)).flatten()
|
self.selected.scene().and_then(|s|self.scenes.get(s))
|
||||||
}
|
}
|
||||||
pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> {
|
pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> {
|
||||||
self.selected.scene().map(|s|self.scenes.get_mut(s)).flatten()
|
self.selected.scene().and_then(|s|self.scenes.get_mut(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Default, Debug, Clone)] pub struct ArrangerScene {
|
#[derive(Default, Debug, Clone)] pub struct ArrangerScene {
|
||||||
|
|
@ -49,7 +49,7 @@ impl ArrangerScene {
|
||||||
if factor == 0 {
|
if factor == 0 {
|
||||||
scenes.iter().map(|scene|{
|
scenes.iter().map(|scene|{
|
||||||
let pulses = scene.pulses().max(PPQ);
|
let pulses = scene.pulses().max(PPQ);
|
||||||
total = total + pulses;
|
total += pulses;
|
||||||
(pulses, total - pulses)
|
(pulses, total - pulses)
|
||||||
}).collect()
|
}).collect()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,17 @@ pub enum ArrangerSelection {
|
||||||
}
|
}
|
||||||
/// Focus identification methods
|
/// Focus identification methods
|
||||||
impl ArrangerSelection {
|
impl ArrangerSelection {
|
||||||
pub fn description <E: Engine> (
|
pub fn is_mix (&self) -> bool { matches!(self, Self::Mix) }
|
||||||
|
pub fn is_track (&self) -> bool { matches!(self, Self::Track(_)) }
|
||||||
|
pub fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) }
|
||||||
|
pub fn is_clip (&self) -> bool { matches!(self, Self::Clip(_, _)) }
|
||||||
|
pub fn description (
|
||||||
&self,
|
&self,
|
||||||
tracks: &Vec<ArrangerTrack>,
|
tracks: &[ArrangerTrack],
|
||||||
scenes: &Vec<ArrangerScene>,
|
scenes: &[ArrangerScene],
|
||||||
) -> String {
|
) -> String {
|
||||||
format!("Selected: {}", match self {
|
format!("Selected: {}", match self {
|
||||||
Self::Mix => format!("Everything"),
|
Self::Mix => "Everything".to_string(),
|
||||||
Self::Track(t) => match tracks.get(*t) {
|
Self::Track(t) => match tracks.get(*t) {
|
||||||
Some(track) => format!("T{t}: {}", &track.name.read().unwrap()),
|
Some(track) => format!("T{t}: {}", &track.name.read().unwrap()),
|
||||||
None => "T??".into(),
|
None => "T??".into(),
|
||||||
|
|
@ -37,18 +41,6 @@ impl ArrangerSelection {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn is_mix (&self) -> bool {
|
|
||||||
match self { Self::Mix => true, _ => false }
|
|
||||||
}
|
|
||||||
pub fn is_track (&self) -> bool {
|
|
||||||
match self { Self::Track(_) => true, _ => false }
|
|
||||||
}
|
|
||||||
pub fn is_scene (&self) -> bool {
|
|
||||||
match self { Self::Scene(_) => true, _ => false }
|
|
||||||
}
|
|
||||||
pub fn is_clip (&self) -> bool {
|
|
||||||
match self { Self::Clip(_, _) => true, _ => false }
|
|
||||||
}
|
|
||||||
pub fn track (&self) -> Option<usize> {
|
pub fn track (&self) -> Option<usize> {
|
||||||
use ArrangerSelection::*;
|
use ArrangerSelection::*;
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ impl ArrangerTui {
|
||||||
let track = ArrangerTrack {
|
let track = ArrangerTrack {
|
||||||
width: name.len() + 2,
|
width: name.len() + 2,
|
||||||
name: Arc::new(name.into()),
|
name: Arc::new(name.into()),
|
||||||
color: color.unwrap_or_else(||ItemPalette::random()),
|
color: color.unwrap_or_else(ItemPalette::random),
|
||||||
player: MidiPlayer::from(&self.clock),
|
player: MidiPlayer::from(&self.clock),
|
||||||
};
|
};
|
||||||
self.tracks.push(track);
|
self.tracks.push(track);
|
||||||
|
|
@ -96,7 +96,7 @@ pub struct TracksAudio<'a>(
|
||||||
/// Note chunk buffer
|
/// Note chunk buffer
|
||||||
pub &'a mut Vec<Vec<Vec<u8>>>,
|
pub &'a mut Vec<Vec<Vec<u8>>>,
|
||||||
);
|
);
|
||||||
impl<'a> Audio for TracksAudio<'a> {
|
impl Audio for TracksAudio<'_> {
|
||||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||||
let model = &mut self.0;
|
let model = &mut self.0;
|
||||||
let note_buffer = &mut self.1;
|
let note_buffer = &mut self.1;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ from!(<'a>|args:(&'a ArrangerTui, usize)|ArrangerVClips<'a> = Self {
|
||||||
|
|
||||||
render!(<Tui>|self: ArrangerVClips<'a>|Fill::wh(
|
render!(<Tui>|self: ArrangerVClips<'a>|Fill::wh(
|
||||||
col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => {
|
col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => {
|
||||||
Self::format_scene(&self.tracks, scene, pulses)
|
Self::format_scene(self.tracks, scene, pulses)
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -28,8 +28,10 @@ impl<'a> ArrangerVClips<'a> {
|
||||||
let height = 1.max((pulses / PPQ) as u16);
|
let height = 1.max((pulses / PPQ) as u16);
|
||||||
let playing = scene.is_playing(tracks);
|
let playing = scene.is_playing(tracks);
|
||||||
Fixed::h(height, row!([
|
Fixed::h(height, row!([
|
||||||
Tui::bg(scene.color.base.rgb, if playing { "▶ " } else { " " }),
|
Tui::bg(scene.color.base.rgb,
|
||||||
Tui::fg_bg(scene.color.lightest.rgb, scene.color.base.rgb, Tui::grow_x(1, Tui::bold(true, scene.name.read().unwrap().as_str()))),
|
if playing { "▶ " } else { " " }),
|
||||||
|
Tui::fg_bg(scene.color.lightest.rgb, scene.color.base.rgb,
|
||||||
|
Tui::grow_x(1, Tui::bold(true, scene.name.read().unwrap().as_str()))),
|
||||||
row!((index, track, x1, x2) in ArrangerTrack::with_widths(tracks) => {
|
row!((index, track, x1, x2) in ArrangerTrack::with_widths(tracks) => {
|
||||||
Self::format_clip(scene, index, track, (x2 - x1) as u16, height)
|
Self::format_clip(scene, index, track, (x2 - x1) as u16, height)
|
||||||
})])
|
})])
|
||||||
|
|
@ -41,8 +43,7 @@ impl<'a> ArrangerVClips<'a> {
|
||||||
Fixed::wh(w, h, Layers::new(move |add|{
|
Fixed::wh(w, h, Layers::new(move |add|{
|
||||||
if let Some(Some(phrase)) = scene.clips.get(index) {
|
if let Some(Some(phrase)) = scene.clips.get(index) {
|
||||||
let mut bg = TuiTheme::border_bg();
|
let mut bg = TuiTheme::border_bg();
|
||||||
let name = &(phrase as &Arc<RwLock<MidiClip>>).read().unwrap().name;
|
let name = &(phrase as &Arc<RwLock<MidiClip>>).read().unwrap().name.to_string();
|
||||||
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;
|
||||||
bg = color.dark.rgb;
|
bg = color.dark.rgb;
|
||||||
|
|
@ -52,7 +53,7 @@ impl<'a> ArrangerVClips<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
add(&Tui::bg(bg,
|
add(&Tui::bg(bg,
|
||||||
Tui::push_x(1, Fixed::w(w as u16, &name.as_str()[0..max_w])))
|
Tui::push_x(1, Fixed::w(w, &name.as_str()[0..max_w])))
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,13 @@ render!(<Tui>|self: ArrangerVHead<'a>|Tui::push_x(self.scenes_w, row!(
|
||||||
row(color, &Self::format_name(track, w)),
|
row(color, &Self::format_name(track, w)),
|
||||||
row(color, &Self::format_input(track)?),
|
row(color, &Self::format_input(track)?),
|
||||||
row(color, &Self::format_output(track)?),
|
row(color, &Self::format_output(track)?),
|
||||||
row(color, &Self::format_elapsed(track, &self.timebase)),
|
row(color, &Self::format_elapsed(track, self.timebase)),
|
||||||
row(color, &Self::format_until_next(track, &self.current)),
|
row(color, &Self::format_until_next(track, self.current)),
|
||||||
]))))
|
]))))
|
||||||
}
|
}
|
||||||
)));
|
)));
|
||||||
|
|
||||||
impl<'a> ArrangerVHead<'a> {
|
impl ArrangerVHead<'_> {
|
||||||
/// name and width of track
|
/// name and width of track
|
||||||
fn format_name (track: &ArrangerTrack, _w: usize) -> impl Render<Tui> {
|
fn format_name (track: &ArrangerTrack, _w: usize) -> impl Render<Tui> {
|
||||||
let name = track.name().read().unwrap().clone();
|
let name = track.name().read().unwrap().clone();
|
||||||
|
|
@ -43,12 +43,12 @@ impl<'a> ArrangerVHead<'a> {
|
||||||
}
|
}
|
||||||
/// input port
|
/// input port
|
||||||
fn format_input (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
fn format_input (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||||
Ok(format!(">{}", track.player.midi_ins().get(0).map(|port|port.short_name())
|
Ok(format!(">{}", track.player.midi_ins().first().map(|port|port.short_name())
|
||||||
.transpose()?.unwrap_or("?".into())))
|
.transpose()?.unwrap_or("?".into())))
|
||||||
}
|
}
|
||||||
/// output port
|
/// output port
|
||||||
fn format_output (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
fn format_output (track: &ArrangerTrack) -> Usually<impl Render<Tui>> {
|
||||||
Ok(format!("<{}", track.player.midi_outs().get(0).map(|port|port.short_name())
|
Ok(format!("<{}", track.player.midi_outs().first().map(|port|port.short_name())
|
||||||
.transpose()?.unwrap_or("?".into())))
|
.transpose()?.unwrap_or("?".into())))
|
||||||
}
|
}
|
||||||
/// beats elapsed
|
/// beats elapsed
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ pub enum PhraseCommand {
|
||||||
SetTimeLock(bool),
|
SetTimeLock(bool),
|
||||||
Show(Option<Arc<RwLock<MidiClip>>>),
|
Show(Option<Arc<RwLock<MidiClip>>>),
|
||||||
}
|
}
|
||||||
event_map_input_to_command!(Tui: MidiEditorModel: PhraseCommand: MidiEditorModel::KEYS);
|
|
||||||
|
|
||||||
|
event_map_input_to_command!(Tui: MidiEditorModel: PhraseCommand: MidiEditorModel::KEYS);
|
||||||
impl MidiEditorModel {
|
impl MidiEditorModel {
|
||||||
const KEYS: [(TuiEvent, &'static dyn Fn(&Self)->PhraseCommand);31] = [
|
const KEYS: KeyMapping<31, Self> = [
|
||||||
(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))),
|
||||||
(kexp!(Ctrl-Alt-Left), &|s: &Self|SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get()))),
|
(kexp!(Ctrl-Alt-Left), &|s: &Self|SetTimeScroll(s.time_point().saturating_sub(s.time_zoom().get()))),
|
||||||
|
|
@ -120,7 +120,7 @@ pub trait PhraseViewMode: Render<Tui> + HasSize<Tui> + MidiRange + MidiPoint + D
|
||||||
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
fn phrase (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
||||||
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>>;
|
fn phrase_mut (&mut self) -> &mut Option<Arc<RwLock<MidiClip>>>;
|
||||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
*self.phrase_mut() = phrase.map(|p|p.clone());
|
*self.phrase_mut() = phrase.cloned();
|
||||||
self.redraw();
|
self.redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,19 +37,19 @@ impl PianoHorizontal {
|
||||||
Self {
|
Self {
|
||||||
buffer: Default::default(),
|
buffer: Default::default(),
|
||||||
point: MidiPointModel::default(),
|
point: MidiPointModel::default(),
|
||||||
|
phrase: phrase.cloned(),
|
||||||
size,
|
size,
|
||||||
range,
|
range,
|
||||||
phrase: phrase.map(|p|p.clone()),
|
|
||||||
color
|
color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(<Tui>|self: PianoHorizontal|{
|
render!(<Tui>|self: PianoHorizontal|{
|
||||||
let keys = move||PianoHorizontalKeys(&self);
|
let keys = move||PianoHorizontalKeys(self);
|
||||||
let timeline = move||PianoHorizontalTimeline(&self);
|
let timeline = move||PianoHorizontalTimeline(self);
|
||||||
let notes = move||PianoHorizontalNotes(&self);
|
let notes = move||PianoHorizontalNotes(self);
|
||||||
let cursor = move||PianoHorizontalCursor(&self);
|
let cursor = move||PianoHorizontalCursor(self);
|
||||||
let keys_width = 5;
|
let keys_width = 5;
|
||||||
let border = Fill::wh(Outer(Style::default().fg(self.color.dark.rgb).bg(self.color.darkest.rgb)));
|
let border = Fill::wh(Outer(Style::default().fg(self.color.dark.rgb).bg(self.color.darkest.rgb)));
|
||||||
let with_border = |x|lay!([border, Tui::inset_xy(1, 1, &x)]);
|
let with_border = |x|lay!([border, Tui::inset_xy(1, 1, &x)]);
|
||||||
|
|
@ -169,8 +169,8 @@ impl PhraseViewMode for PianoHorizontal {
|
||||||
self.buffer = buffer
|
self.buffer = buffer
|
||||||
}
|
}
|
||||||
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
fn set_phrase (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
*self.phrase_mut() = phrase.map(|p|p.clone());
|
*self.phrase_mut() = phrase.cloned();
|
||||||
self.color = phrase.map(|p|p.read().unwrap().color.clone())
|
self.color = phrase.map(|p|p.read().unwrap().color)
|
||||||
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
|
.unwrap_or(ItemPalette::from(ItemColor::from(TuiTheme::g(64))));
|
||||||
self.redraw();
|
self.redraw();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ render!(<Tui>|self: PianoHorizontalCursor<'a>|render(|to|Ok({
|
||||||
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
for (area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||||
if note == note_point {
|
if note == note_point {
|
||||||
for x in 0..w {
|
for x in 0..w {
|
||||||
let screen_x = x0 + x as u16;
|
let screen_x = x0 + x;
|
||||||
let time_1 = time_start + x as usize * time_zoom;
|
let time_1 = time_start + x as usize * time_zoom;
|
||||||
let time_2 = time_1 + time_zoom;
|
let time_2 = time_1 + time_zoom;
|
||||||
if time_1 <= time_point && time_point < time_2 {
|
if time_1 <= time_point && time_point < time_2 {
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,7 @@ impl PhraseSelector {
|
||||||
} else if let Some((_, Some(phrase))) = state.play_phrase() {
|
} else if let Some((_, Some(phrase))) = state.play_phrase() {
|
||||||
let phrase = phrase.read().unwrap();
|
let phrase = phrase.read().unwrap();
|
||||||
if phrase.looped {
|
if phrase.looped {
|
||||||
(" ".into(), phrase.name.clone(), phrase.color.clone())
|
(" ".into(), phrase.name.clone(), phrase.color)
|
||||||
} else {
|
} else {
|
||||||
(" ".into(), " ".into(), TuiTheme::g(64).into())
|
(" ".into(), " ".into(), TuiTheme::g(64).into())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ impl Command<PoolModel> for PhraseRenameCommand {
|
||||||
match state.phrases_mode_mut().clone() {
|
match state.phrases_mode_mut().clone() {
|
||||||
Some(PoolMode::Rename(phrase, ref mut old_name)) => match self {
|
Some(PoolMode::Rename(phrase, ref mut old_name)) => match self {
|
||||||
Set(s) => {
|
Set(s) => {
|
||||||
state.phrases()[phrase].write().unwrap().name = s.into();
|
state.phrases()[phrase].write().unwrap().name = s;
|
||||||
return Ok(Some(Self::Set(old_name.clone())))
|
return Ok(Some(Self::Set(old_name.clone())))
|
||||||
},
|
},
|
||||||
Confirm => {
|
Confirm => {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub struct ArrangerStatus {
|
||||||
}
|
}
|
||||||
from!(|state:&ArrangerTui|ArrangerStatus = {
|
from!(|state:&ArrangerTui|ArrangerStatus = {
|
||||||
let samples = state.clock.chunk.load(Relaxed);
|
let samples = state.clock.chunk.load(Relaxed);
|
||||||
let rate = state.clock.timebase.sr.get() as f64;
|
let rate = state.clock.timebase.sr.get();
|
||||||
let buffer = samples as f64 / rate;
|
let buffer = samples as f64 / rate;
|
||||||
let width = state.size.w();
|
let width = state.size.w();
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -48,7 +48,7 @@ impl ArrangerStatus {
|
||||||
double(("[]", "phrase"), ("{}", "order"),),
|
double(("[]", "phrase"), ("{}", "order"),),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
fn stats <'a> (&'a self) -> impl Render<Tui> + use<'a> {
|
fn stats (&self) -> impl Render<Tui> + use<'_> {
|
||||||
row!([&self.cpu, &self.res, &self.size])
|
row!([&self.cpu, &self.res, &self.size])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ render!(<Tui>|self:MidiEditStatus<'a>|{
|
||||||
]);
|
]);
|
||||||
Fill::w(Tui::fg_bg(fg, bg, row!([
|
Fill::w(Tui::fg_bg(fg, bg, row!([
|
||||||
Fixed::wh(26, 3, col!(![
|
Fixed::wh(26, 3, col!(![
|
||||||
field(" Edit", format!("{name}")),
|
field(" Edit", name.to_string()),
|
||||||
field(" Length", format!("{length}")),
|
field(" Length", length.to_string()),
|
||||||
field(" Loop", format!("{looped}"))])),
|
field(" Loop", looped.to_string())])),
|
||||||
Fixed::wh(30, 3, col!(![
|
Fixed::wh(30, 3, col!(![
|
||||||
field(" Time", format!("{}/{}-{} ({}*{}) {}",
|
field(" Time", format!("{}/{}-{} ({}*{}) {}",
|
||||||
self.0.time_point(), self.0.time_start().get(), self.0.time_end(),
|
self.0.time_point(), self.0.time_start().get(), self.0.time_end(),
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub struct SequencerStatus {
|
||||||
}
|
}
|
||||||
from!(|state:&SequencerTui|SequencerStatus = {
|
from!(|state:&SequencerTui|SequencerStatus = {
|
||||||
let samples = state.clock.chunk.load(Relaxed);
|
let samples = state.clock.chunk.load(Relaxed);
|
||||||
let rate = state.clock.timebase.sr.get() as f64;
|
let rate = state.clock.timebase.sr.get();
|
||||||
let buffer = samples as f64 / rate;
|
let buffer = samples as f64 / rate;
|
||||||
let width = state.size.w();
|
let width = state.size.w();
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -46,7 +46,7 @@ impl SequencerStatus {
|
||||||
double(("c", "color"), ("", ""),),
|
double(("c", "color"), ("", ""),),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
fn stats <'a> (&'a self) -> impl Render<Tui> + use<'a> {
|
fn stats (&self) -> impl Render<Tui> + use<'_> {
|
||||||
row!([&self.cpu, &self.res, &self.size])
|
row!([&self.cpu, &self.res, &self.size])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,8 @@ impl<'a, const N: usize, E: PartialEq, T, U> EventMap<'a, N, E, T, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type KeyMapping<const N: usize, T> = [(TuiEvent, &'static dyn Fn(&T)->PhraseCommand);N];
|
||||||
|
|
||||||
#[macro_export] macro_rules! kexp {
|
#[macro_export] macro_rules! kexp {
|
||||||
(Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::from_bits(0b0000_0110).unwrap()) };
|
(Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::from_bits(0b0000_0110).unwrap()) };
|
||||||
(Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) };
|
(Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) };
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue