Compare commits

...

7 commits

10 changed files with 85 additions and 52 deletions

View file

@ -6,7 +6,7 @@ midi-in := "-i 'Midi-Bridge:.*nanoKEY.*:.*capture.*'"
midi-out := "-o 'Midi-Bridge:.*playback.*'"
audio-in := "-l 'Komplete Audio 6 Pro:capture_AUX1' -r 'Komplete Audio 6 Pro:capture_AUX1'"
audio-out := "-L 'Komplete Audio 6 Pro:playback_AUX1' -R 'Komplete Audio 6 Pro:playback_AUX1'"
firefox-in := "-l 'Firefox:output_FL' -r 'Firefox:output_FR'"
firefox-in := "-l 'Firefox:output_FL*' -r 'Firefox:output_FR*'"
covfig := "CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cov/cargo-test-%p-%m.profraw'"
grcov-binary := "--binary-path ./target/coverage/deps/"
grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'"
@ -71,6 +71,8 @@ groovebox:
groovebox-ext:
reset
{{debug}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} {{audio-in}} {{audio-out}} groovebox
groovebox-browser:
{{debug}} {{name}} {{bpm}} {{audio-in}} groovebox
groovebox-release:
{{release}} {{name}} {{bpm}} groovebox
groovebox-release-ext:

View file

@ -1,5 +1,7 @@
(@left editor set-time-pos :time-pos-prev)
(@shift-left editor set-time-pos :time-pos-prev-fine)
(@right editor set-time-pos :time-pos-next)
(@shift-right editor set-time-pos :time-pos-next-fine)
(@equal editor set-time-zoom :time-zoom-prev)
(@minus editor set-time-zoom :time-zoom-next)

View file

@ -4,3 +4,5 @@
(@right sampler select :sample-to-right)
(@r sampler record-toggle :sample-selected)
(@shift-R sampler record-cancel)
(@p sampler play-sample :sample-selected)
(@P sampler stop-sample :sample-selected)

View file

@ -2,19 +2,15 @@ use crate::*;
#[derive(Debug, Default)] pub struct Track {
/// Name of track
pub name: Arc<str>,
/// Preferred width of track column
pub width: usize,
pub name: Arc<str>,
/// Identifying color of track
pub color: ItemTheme,
pub color: ItemTheme,
/// Preferred width of track column
pub width: usize,
/// MIDI sequencer state
pub sequencer: Sequencer,
pub sequencer: Sequencer,
/// Device chain
pub devices: Vec<Device>,
/// Inputs of 1st device
pub audio_ins: Vec<JackAudioIn>,
/// Outputs of last device
pub audio_outs: Vec<JackAudioOut>,
pub devices: Vec<Device>,
}
has_clock!(|self: Track|self.sequencer.clock);
@ -58,9 +54,7 @@ impl Track {
audio_from: &[&[PortConnect];2],
audio_to: &[&[PortConnect];2],
) -> Usually<Self> {
let mut track = Self::new(
name, color, jack, clock, clip, midi_from, midi_to
)?;
let mut track = Self::new(name, color, jack, clock, clip, midi_from, midi_to)?;
track.devices.push(Device::Sampler(Sampler::new(
jack,
&format!("{}/sampler", name.as_ref()),

View file

@ -571,9 +571,13 @@ impl<'a> ArrangerView<'a> {
}
trait ScenesColors<'a> = Iterator<Item=SceneWithColor<'a>>;
/// Iterator over scenes with their sizes and colors.
pub(crate) trait ScenesColors<'a> =
Iterator<Item=SceneWithColor<'a>>;
type SceneWithColor<'a> = (usize, &'a Scene, usize, usize, Option<ItemTheme>);
/// A scene with size and color.
pub(crate) type SceneWithColor<'a> =
(usize, &'a Scene, usize, usize, Option<ItemTheme>);
/// Define a type alias for iterators of sized items (columns).
macro_rules! def_sizes_iter {

View file

@ -128,11 +128,13 @@ impl Cli {
midi_ins,
midi_outs,
midi_buf: match mode {
LaunchMode::Clock
| LaunchMode::Sampler => vec![],
LaunchMode::Sequencer
| LaunchMode::Groovebox
| LaunchMode::Arranger {..} => vec![vec![];65536],
LaunchMode::Clock |
LaunchMode::Sampler =>
vec![],
LaunchMode::Sequencer |
LaunchMode::Groovebox |
LaunchMode::Arranger {..} =>
vec![vec![];65536],
_ => todo!("{mode:?}"),
},
tracks: match mode {
@ -155,7 +157,7 @@ impl Cli {
Some(&clock),
clip.as_ref(),
midi_froms.as_slice(),
midi_froms.as_slice(),
midi_tos.as_slice(),
audio_froms,
audio_tos,
)?

View file

@ -5,6 +5,10 @@ use crate::*;
todo!()
}
fn clip_length (&self) -> usize {
self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1)
}
fn note_length (&self) -> usize {
self.get_note_len()
}
@ -49,10 +53,19 @@ use crate::*;
self.get_time_pos()
}
fn time_pos_next (&self) -> usize {
self.get_time_pos() + self.get_note_len()
(self.get_time_pos() + self.get_note_len()) % self.clip_length()
}
fn time_pos_next_fine (&self) -> usize {
(self.get_time_pos() + 1) % self.clip_length()
}
fn time_pos_prev (&self) -> usize {
self.get_time_pos().saturating_sub(self.get_note_len())
let step = self.get_note_len();
self.get_time_pos().overflowing_sub(step)
.0.min(self.clip_length().saturating_sub(step))
}
fn time_pos_prev_fine (&self) -> usize {
self.get_time_pos().overflowing_sub(1)
.0.min(self.clip_length().saturating_sub(1))
}
fn time_zoom (&self) -> usize {

View file

@ -61,7 +61,7 @@ impl MidiEditor {
clip.notes[note_end].push(note_off);
}
if advance {
self.set_time_pos(note_end + 1);
self.set_time_pos((note_end + 1) % clip.length);
}
redraw = true;
}
@ -69,33 +69,6 @@ impl MidiEditor {
self.mode.redraw();
}
}
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else { (ItemTheme::G[64], String::new().into(), 0, false) };
Bsp::e(
FieldH(color, "Edit", format!("{name} ({length})")),
FieldH(color, "Loop", looped.to_string())
)
}
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length)
} else { (ItemTheme::G[64], 0) };
let time_pos = self.get_time_pos();
let time_zoom = self.get_time_zoom();
let time_lock = if self.get_time_lock() { "[lock]" } else { " " };
let note_pos = self.get_note_pos();
let note_name = format!("{:4}", Note::pitch_to_name(note_pos));
let note_pos = format!("{:>3}", note_pos);
let note_len = format!("{:>4}", self.get_note_len());
Bsp::e(
FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")),
FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")),
)
}
}
impl TimeRange for MidiEditor {

View file

@ -7,3 +7,34 @@ content!(TuiOut: |self: MidiEditor| {
//self.autozoom();
self.size.of(&self.mode)
});
impl MidiEditor {
pub fn clip_status (&self) -> impl Content<TuiOut> + '_ {
let (color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.name.clone(), clip.length, clip.looped)
} else { (ItemTheme::G[64], String::new().into(), 0, false) };
Bsp::e(
FieldH(color, "Edit", format!("{name} ({length})")),
FieldH(color, "Loop", looped.to_string())
)
}
pub fn edit_status (&self) -> impl Content<TuiOut> + '_ {
let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length)
} else { (ItemTheme::G[64], 0) };
let time_pos = self.get_time_pos();
let time_zoom = self.get_time_zoom();
let time_lock = if self.get_time_lock() { "[lock]" } else { " " };
let note_pos = self.get_note_pos();
let note_name = format!("{:4}", Note::pitch_to_name(note_pos));
let note_pos = format!("{:>3}", note_pos);
let note_len = format!("{:>4}", self.get_note_len());
Bsp::e(
FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")),
FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")),
)
}
}

View file

@ -81,6 +81,16 @@ impl SamplerCommand {
sampler.recording = None;
Ok(None)
}
fn play_sample (sampler: &mut Sampler, pitch: usize) -> Perhaps<Self> {
if let Some(ref sample) = sampler.mapped[pitch] {
sampler.voices.write().unwrap().push(Sample::play(sample, 0, &u7::from(128)));
}
Ok(None)
}
fn stop_sample (sampler: &mut Sampler, pitch: usize) -> Perhaps<Self> {
todo!();
Ok(None)
}
//fn select (&self, state: &mut Sampler, i: usize) -> Option<Self> {
//Self::Select(state.set_note_pos(i))
//}