mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
groovebox: looks surprisingly well
This commit is contained in:
parent
c66a006120
commit
03024f8a14
11 changed files with 130 additions and 91 deletions
|
|
@ -14,5 +14,7 @@
|
|||
(view (bsp/a :view-dialog (bsp/w :view-meters-output (bsp/e :view-meters-input
|
||||
(bsp/n (fixed/y 6 (bsp/e :view-sample-status :view-sample-viewer))
|
||||
(bsp/e
|
||||
(fill/y (align/n (bsp/s :view-status-v (bsp/s :view-ports-status (bsp/s :view-editor-status :view-pool)))))
|
||||
(fill/y (align/n (bsp/s :view-status-v (bsp/s
|
||||
(bsp/s :view-midi-ports-status :view-audio-ports-status)
|
||||
(bsp/n :view-editor-status :view-pool)))))
|
||||
(bsp/e :view-samples-keys :view-editor)))))))
|
||||
|
|
|
|||
|
|
@ -244,22 +244,22 @@ impl App {
|
|||
Some(Dialog::Menu(0))
|
||||
}
|
||||
fn dialog_save (&self) -> Option<Dialog> {
|
||||
Some(Dialog::Browser(BrowserTarget::SaveProject, Default::default()))
|
||||
Some(Dialog::Browser(BrowserTarget::SaveProject, Browser::new(None).unwrap()))
|
||||
}
|
||||
fn dialog_load (&self) -> Option<Dialog> {
|
||||
Some(Dialog::Browser(BrowserTarget::LoadProject, Default::default()))
|
||||
Some(Dialog::Browser(BrowserTarget::LoadProject, Browser::new(None).unwrap()))
|
||||
}
|
||||
fn dialog_import_clip (&self) -> Option<Dialog> {
|
||||
Some(Dialog::Browser(BrowserTarget::ImportClip(Default::default()), Default::default()))
|
||||
Some(Dialog::Browser(BrowserTarget::ImportClip(Default::default()), Browser::new(None).unwrap()))
|
||||
}
|
||||
fn dialog_export_clip (&self) -> Option<Dialog> {
|
||||
Some(Dialog::Browser(BrowserTarget::ExportClip(Default::default()), Default::default()))
|
||||
Some(Dialog::Browser(BrowserTarget::ExportClip(Default::default()), Browser::new(None).unwrap()))
|
||||
}
|
||||
fn dialog_import_sample (&self) -> Option<Dialog> {
|
||||
Some(Dialog::Browser(BrowserTarget::ImportSample(Default::default()), Default::default()))
|
||||
Some(Dialog::Browser(BrowserTarget::ImportSample(Default::default()), Browser::new(None).unwrap()))
|
||||
}
|
||||
fn dialog_export_sample (&self) -> Option<Dialog> {
|
||||
Some(Dialog::Browser(BrowserTarget::ExportSample(Default::default()), Default::default()))
|
||||
Some(Dialog::Browser(BrowserTarget::ExportSample(Default::default()), Browser::new(None).unwrap()))
|
||||
}
|
||||
fn dialog_options (&self) -> Option<Dialog> {
|
||||
Some(Dialog::Options)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ impl App {
|
|||
let cache = self.view_cache.read().unwrap();
|
||||
let theme = self.color;
|
||||
let playing = self.clock().is_rolling();
|
||||
Fixed::xy(20, 7, col!(
|
||||
Fixed::xy(20, 6, col!(
|
||||
Fill::x(Align::w(Bsp::e(
|
||||
Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
|
||||
Either::new(false, // TODO
|
||||
|
|
@ -60,22 +60,33 @@ impl App {
|
|||
self.editor()
|
||||
}
|
||||
pub fn view_editor_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
Fixed::y(5, self.editor().map(|e|Bsp::s(e.clip_status(), e.edit_status())))
|
||||
self.editor().map(|e|Bsp::s(e.clip_status(), e.edit_status()))
|
||||
}
|
||||
pub fn view_ports_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
pub fn view_midi_ports_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.project.get_track().map(|track|Bsp::s(
|
||||
Fixed::y(2.max(track.sequencer.midi_ins.len() as u16), Align::n(Map::south(1,
|
||||
||track.sequencer.midi_ins.iter(),
|
||||
|port, index|Fixed::x(20, FieldV(
|
||||
Fixed::xy(20, 1 + track.sequencer.midi_ins.len() as u16, FieldV(self.color,
|
||||
format!("MIDI ins: "),
|
||||
Map::south(1, ||track.sequencer.midi_ins.iter(),
|
||||
|port, index|Fill::x(Align::w(format!(" {index} {}", port.name())))))),
|
||||
Fixed::xy(20, 1 + track.sequencer.midi_outs.len() as u16, FieldV(
|
||||
self.color,
|
||||
format!("MIDI in {index}: "),
|
||||
format!("{}", port.name())))))),
|
||||
Fixed::y(2.max(track.sequencer.midi_outs.len() as u16), Align::n(Map::south(1,
|
||||
||track.sequencer.midi_outs.iter(),
|
||||
|port, index|Fixed::x(20, FieldV(
|
||||
self.color,
|
||||
format!("MIDI out {index}:"),
|
||||
format!("{}", port.name()))))))
|
||||
format!("MIDI outs: "),
|
||||
Map::south(1, ||track.sequencer.midi_outs.iter(),
|
||||
|port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))
|
||||
))
|
||||
}
|
||||
pub fn view_audio_ports_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.project.get_track().map(|track|Bsp::s(
|
||||
track.devices.get(0).map(|device|
|
||||
Fixed::xy(20, 1 + device.audio_ins().len() as u16, FieldV(self.color,
|
||||
format!("Audio ins: "),
|
||||
Map::south(1, ||device.audio_ins().iter(),
|
||||
|port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))),
|
||||
track.devices.last().map(|device|
|
||||
Fixed::xy(20, 1 + device.audio_outs().len() as u16, FieldV(self.color,
|
||||
format!("Audio outs:"),
|
||||
Map::south(1, ||device.audio_outs().iter(),
|
||||
|port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))),
|
||||
))
|
||||
}
|
||||
pub fn view_arranger (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
|
|
@ -83,8 +94,8 @@ impl App {
|
|||
}
|
||||
pub fn view_pool (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
Fixed::x(20, Bsp::s(
|
||||
Fill::x(Align::w(Bsp::s("", FieldH(self.color, "Pool", "")))),
|
||||
Fill::y(Align::n(Tui::bg(Black, PoolView(&self.project.pool)))),
|
||||
Fill::x(Align::w(FieldH(self.color, "MIDI clip pool:", ""))),
|
||||
Fill::y(Align::n(Tui::bg(Rgb(0, 0, 0), PoolView(&self.project.pool)))),
|
||||
))
|
||||
}
|
||||
pub fn view_samples_keys (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,33 @@
|
|||
use crate::*;
|
||||
|
||||
content!(TuiOut: |self: Browser| /*Stack::down(|add|{
|
||||
let mut i = 0;
|
||||
for (_, name) in self.dirs.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
content!(TuiOut: |self: Browser|Map::south(1, ||EntriesIterator {
|
||||
offset: 0,
|
||||
index: 0,
|
||||
length: self.dirs.len() + self.files.len(),
|
||||
browser: self,
|
||||
}, |entry, index|Fill::x(Align::w(entry))));
|
||||
|
||||
struct EntriesIterator<'a> {
|
||||
browser: &'a Browser,
|
||||
offset: usize,
|
||||
length: usize,
|
||||
index: usize,
|
||||
}
|
||||
i += 1;
|
||||
|
||||
impl<'a> Iterator for EntriesIterator<'a> {
|
||||
type Item = Modify<&'a str>;
|
||||
fn next (&mut self) -> Option<Self::Item> {
|
||||
let dirs = self.browser.dirs.len();
|
||||
let files = self.browser.files.len();
|
||||
let index = self.index;
|
||||
if self.index < dirs {
|
||||
self.index += 1;
|
||||
Some(Tui::bold(true, self.browser.dirs[index].1.as_str()))
|
||||
} else if self.index < dirs + files {
|
||||
self.index += 1;
|
||||
Some(Tui::bold(false, self.browser.files[index - dirs].1.as_str()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
for (_, name) in self.files.iter() {
|
||||
if i >= self.scroll {
|
||||
add(&Tui::bold(i == self.index, name.as_str()))?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
add(&format!("{}/{i}", self.index))?;
|
||||
Ok(())
|
||||
})*/"todo");
|
||||
|
|
|
|||
|
|
@ -37,6 +37,30 @@ impl Device {
|
|||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
pub fn midi_ins (&self) -> &[JackMidiIn] {
|
||||
match self {
|
||||
//Self::Sampler(Sampler { midi_in, .. }) => &[midi_in],
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
pub fn midi_outs (&self) -> &[JackMidiOut] {
|
||||
match self {
|
||||
Self::Sampler(_) => &[],
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
pub fn audio_ins (&self) -> &[JackAudioIn] {
|
||||
match self {
|
||||
Self::Sampler(Sampler { audio_ins, .. }) => audio_ins.as_slice(),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
pub fn audio_outs (&self) -> &[JackAudioOut] {
|
||||
match self {
|
||||
Self::Sampler(Sampler { audio_outs, .. }) => audio_outs.as_slice(),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeviceAudio<'a>(pub &'a mut Device);
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@ impl MidiEditor {
|
|||
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());
|
||||
Fixed::x(20, Bsp::s(
|
||||
Fill::x(Align::w(FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")))),
|
||||
Fixed::x(20, col!(
|
||||
Fill::x(Align::w(FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos}")))),
|
||||
Fill::x(Align::w(FieldH(color, "Lock", format!("{time_lock}")))),
|
||||
Fill::x(Align::w(FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")))),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(let_chains)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
|
||||
pub(crate) use std::cmp::Ord;
|
||||
pub(crate) use std::fmt::{Debug, Formatter};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ impl Sampler {
|
|||
/// Create [Voice]s from [Sample]s in response to MIDI input.
|
||||
pub fn process_midi_in (&mut self, scope: &ProcessScope) {
|
||||
let Sampler { midi_in, mapped, voices, .. } = self;
|
||||
if let Some(ref midi_in) = midi_in {
|
||||
for RawMidi { time, bytes } in midi_in.port().iter(scope) {
|
||||
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
|
||||
match message {
|
||||
|
|
@ -24,7 +23,6 @@ impl Sampler {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub struct Sampler {
|
|||
/// Sample currently being edited.
|
||||
pub editing: Option<Arc<RwLock<Sample>>>,
|
||||
/// MIDI input port. Triggers sample playback.
|
||||
pub midi_in: Option<JackMidiIn>,
|
||||
pub midi_in: JackMidiIn,
|
||||
/// Collection of currently playing instances of samples.
|
||||
pub voices: Arc<RwLock<Vec<Voice>>>,
|
||||
/// Audio output ports. Voices get played here.
|
||||
|
|
@ -47,15 +47,28 @@ pub struct Sampler {
|
|||
pub cursor: (AtomicUsize, AtomicUsize),
|
||||
}
|
||||
|
||||
impl Default for Sampler {
|
||||
fn default () -> Self {
|
||||
Self {
|
||||
midi_in: None,
|
||||
audio_ins: vec![],
|
||||
impl Sampler {
|
||||
pub fn new (
|
||||
jack: &Jack,
|
||||
name: impl AsRef<str>,
|
||||
midi_from: &[PortConnect],
|
||||
audio_from: &[&[PortConnect];2],
|
||||
audio_to: &[&[PortConnect];2],
|
||||
) -> Usually<Self> {
|
||||
let name = name.as_ref();
|
||||
Ok(Self {
|
||||
name: name.into(),
|
||||
midi_in: JackMidiIn::new(jack, format!("M/{name}"), midi_from)?,
|
||||
audio_ins: vec![
|
||||
JackAudioIn::new(jack, &format!("L/{name}"), audio_from[0])?,
|
||||
JackAudioIn::new(jack, &format!("R/{name}"), audio_from[1])?,
|
||||
],
|
||||
audio_outs: vec![
|
||||
JackAudioOut::new(jack, &format!("{name}/L"), audio_to[0])?,
|
||||
JackAudioOut::new(jack, &format!("{name}/R"), audio_to[1])?,
|
||||
],
|
||||
input_meters: vec![0.0;2],
|
||||
output_meters: vec![0.0;2],
|
||||
audio_outs: vec![],
|
||||
name: "tek_sampler".into(),
|
||||
mapped: [const { None };128],
|
||||
unmapped: vec![],
|
||||
voices: Arc::new(RwLock::new(vec![])),
|
||||
|
|
@ -71,31 +84,6 @@ impl Default for Sampler {
|
|||
color: Default::default(),
|
||||
mixing_mode: Default::default(),
|
||||
metering_mode: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler {
|
||||
pub fn new (
|
||||
jack: &Jack,
|
||||
name: impl AsRef<str>,
|
||||
midi_from: &[PortConnect],
|
||||
audio_from: &[&[PortConnect];2],
|
||||
audio_to: &[&[PortConnect];2],
|
||||
) -> Usually<Self> {
|
||||
let name = name.as_ref();
|
||||
Ok(Self {
|
||||
name: name.into(),
|
||||
midi_in: Some(JackMidiIn::new(jack, format!("M/{name}"), midi_from)?),
|
||||
audio_ins: vec![
|
||||
JackAudioIn::new(jack, &format!("L/{name}"), audio_from[0])?,
|
||||
JackAudioIn::new(jack, &format!("R/{name}"), audio_from[1])?,
|
||||
],
|
||||
audio_outs: vec![
|
||||
JackAudioOut::new(jack, &format!("{name}/L"), audio_to[0])?,
|
||||
JackAudioOut::new(jack, &format!("{name}/R"), audio_to[1])?,
|
||||
],
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
/// Value of cursor
|
||||
|
|
|
|||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 8bfd1a23a1f880a1d2fb104a158fc51f244acd6e
|
||||
Subproject commit f7306de55faea42bb1531955a92220fd48d9aadb
|
||||
Loading…
Add table
Add a link
Reference in a new issue