mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
draw ports; fix process callback
This commit is contained in:
parent
72ead536be
commit
9e550a73ae
8 changed files with 268 additions and 175 deletions
|
|
@ -19,6 +19,11 @@ pub use self::plugin::Plugin;
|
|||
pub use self::launcher::Launcher;
|
||||
|
||||
use crossterm::event;
|
||||
use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client};
|
||||
|
||||
pub trait Device: Render + Handle + DevicePorts + Send + Sync {}
|
||||
|
||||
impl<T: Render + Handle + DevicePorts + Send + Sync> Device for T {}
|
||||
|
||||
pub trait Handle {
|
||||
// Returns Ok(true) if the device handled the event.
|
||||
|
|
@ -36,15 +41,59 @@ pub trait Render {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait DevicePorts {
|
||||
fn audio_ins (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn audio_outs (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn midi_outs (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn connect (&mut self, connect: bool, source: &str, target: &str)
|
||||
-> Usually<()>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
fn connect_all (&mut self, connections: &[(bool, &str, &str)])
|
||||
-> Usually<()>
|
||||
{
|
||||
for (connect, source, target) in connections.iter() {
|
||||
self.connect(*connect, source, target)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Box<dyn Device> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
(**self).render(b, a)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Device: Render + Handle + Send + Sync {}
|
||||
pub struct DevicePort<T: PortSpec> {
|
||||
pub name: String,
|
||||
pub port: Port<T>,
|
||||
pub connect: Vec<String>,
|
||||
}
|
||||
|
||||
impl<T: Render + Handle + Send + Sync> Device for T {}
|
||||
impl<T: PortSpec + Default> DevicePort<T> {
|
||||
pub fn new (client: &Client, name: &str, connect: &[&str]) -> Usually<Self> {
|
||||
let mut connects = vec![];
|
||||
for port in connect.iter() {
|
||||
connects.push(port.to_string());
|
||||
}
|
||||
Ok(Self {
|
||||
name: name.to_string(),
|
||||
port: client.register_port(name, T::default())?,
|
||||
connect: connects,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for &dyn Render {
|
||||
fn render_ref (&self, area: Rect, buf: &mut Buffer) {
|
||||
|
|
@ -147,6 +196,21 @@ impl<T> Render for DynamicDevice<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: DevicePorts + Send + Sync + 'static> DevicePorts for DynamicDevice<T> {
|
||||
fn audio_ins (&self) -> Usually<Vec<String>> {
|
||||
self.state().audio_ins()
|
||||
}
|
||||
fn audio_outs (&self) -> Usually<Vec<String>> {
|
||||
self.state().audio_outs()
|
||||
}
|
||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||
self.state().midi_ins()
|
||||
}
|
||||
fn midi_outs (&self) -> Usually<Vec<String>> {
|
||||
self.state().midi_outs()
|
||||
}
|
||||
}
|
||||
|
||||
type DynamicAsyncClient = AsyncClient<DynamicNotifications, DynamicProcessHandler>;
|
||||
type DynamicNotifications = Notifications<Box<dyn Fn(AppEvent) + Send + Sync>>;
|
||||
type DynamicProcessHandler = ClosureProcessHandler<BoxedProcessHandler>;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub fn process (_: &mut Chain, _: &Client, _: &ProcessScope) -> Control {
|
|||
pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
let Rect { x, y, .. } = area;
|
||||
let Rect { mut x, mut y, width, height } = area;
|
||||
let selected = Some(if state.focused {
|
||||
Style::default().green().not_dim()
|
||||
} else {
|
||||
|
|
@ -52,8 +52,57 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
|
|||
draw_box_styled(buf, area, selected)
|
||||
},
|
||||
ChainView::Column => {
|
||||
let (area, areas) = Column::draw(buf, area, &state.items, 0)?;
|
||||
draw_box_styled(buf, areas[state.focus], selected);
|
||||
//let (area, areas) = Column::draw(buf, area, &state.items, 0)?;
|
||||
let mut w = 0u16;
|
||||
let mut y = area.y;
|
||||
let mut frames = vec![];
|
||||
for (i, device) in state.items.iter().enumerate() {
|
||||
|
||||
let midi_ins = device.midi_ins()?;
|
||||
let midi_outs = device.midi_outs()?;
|
||||
let audio_ins = device.audio_ins()?;
|
||||
let audio_outs = device.audio_outs()?;
|
||||
|
||||
y = y + midi_ins.len() as u16;
|
||||
let frame = device.render(buf, Rect { x, y, width, height: height - y })?;
|
||||
frames.push(frame);
|
||||
w = w.max(frame.width);
|
||||
|
||||
y = y - midi_ins.len() as u16;
|
||||
for port in midi_ins.iter() {
|
||||
buf.set_string(x + frame.width - 10, y,
|
||||
&format!(" <i> MIDI {port} "),
|
||||
Style::default().black().bold().on_green());
|
||||
y = y + 1;
|
||||
}
|
||||
|
||||
y = y - audio_ins.len() as u16;
|
||||
for port in audio_ins.iter() {
|
||||
buf.set_string(x + frame.width - 10, y,
|
||||
&format!(" <i> MIDI {port} "),
|
||||
Style::default().black().bold().on_red());
|
||||
y = y + 1;
|
||||
}
|
||||
|
||||
y = y + frame.height - 1;
|
||||
|
||||
y = y + midi_outs.len() as u16;
|
||||
for port in midi_outs.iter() {
|
||||
buf.set_string(x + 2, y,
|
||||
&format!(" <o> MIDI {port} "),
|
||||
Style::default().black().bold().on_green());
|
||||
y = y + 1;
|
||||
}
|
||||
|
||||
y = y + audio_outs.len() as u16;
|
||||
for port in audio_outs.iter() {
|
||||
buf.set_string(x + 2, y,
|
||||
&format!(" <o> Audio {port} "),
|
||||
Style::default().black().bold().on_red());
|
||||
y = y + 1;
|
||||
}
|
||||
}
|
||||
draw_box_styled(buf, frames[state.focus], selected);
|
||||
area
|
||||
},
|
||||
})
|
||||
|
|
@ -159,3 +208,5 @@ pub fn handle (state: &mut Chain, event: &AppEvent) -> Usually<bool> {
|
|||
|s: &mut Chain|s.handle_focus(&FocusEvent::Outward)]
|
||||
}))
|
||||
}
|
||||
|
||||
impl DevicePorts for Chain {}
|
||||
|
|
|
|||
|
|
@ -119,3 +119,12 @@ pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually<bool> {
|
|||
}]
|
||||
}))
|
||||
}
|
||||
|
||||
impl DevicePorts for Plugin {
|
||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec!["in".into()])
|
||||
}
|
||||
fn audio_outs (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec!["out L".into(), "out R".into()])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,14 @@ mod vertical;
|
|||
use vertical::*;
|
||||
|
||||
pub struct Sequencer {
|
||||
name: String,
|
||||
name: String,
|
||||
/// JACK transport handle.
|
||||
transport: ::jack::Transport,
|
||||
transport: ::jack::Transport,
|
||||
/// JACK MIDI input port that will be created.
|
||||
input_port: Port<MidiIn>,
|
||||
/// JACK MIDI output port that will be created.
|
||||
output_port: Port<MidiOut>,
|
||||
|
||||
/// Holds info about tempo
|
||||
timebase: Arc<Timebase>,
|
||||
/// Sequencer resolution, e.g. 16 steps per beat.
|
||||
|
|
@ -24,29 +29,21 @@ pub struct Sequencer {
|
|||
/// Steps in sequence, e.g. 64 16ths = 4 beat loop.
|
||||
/// FIXME: play start / end / loop in ppm
|
||||
steps: usize,
|
||||
|
||||
/// JACK MIDI input port that will be created.
|
||||
input_port: Port<MidiIn>,
|
||||
/// Port name patterns to connect to.
|
||||
input_connect: Vec<String>,
|
||||
/// Play input through output.
|
||||
monitoring: bool,
|
||||
/// Red keys on piano roll.
|
||||
notes_on: Vec<bool>,
|
||||
/// Write input to sequence.
|
||||
recording: bool,
|
||||
/// Sequence selector
|
||||
sequence: usize,
|
||||
sequence: usize,
|
||||
/// Map: tick -> MIDI events at tick
|
||||
sequences: Vec<Sequence>,
|
||||
/// Don't delete when recording.
|
||||
overdub: bool,
|
||||
sequences: Vec<Sequence>,
|
||||
/// Red keys on piano roll.
|
||||
notes_on: Vec<bool>,
|
||||
|
||||
/// Play sequence to output.
|
||||
playing: bool,
|
||||
/// JACK MIDI output port that will be created.
|
||||
output_port: Port<MidiOut>,
|
||||
/// Port name patterns to connect to.
|
||||
output_connect: Vec<String>,
|
||||
playing: TransportState,
|
||||
/// Play input through output.
|
||||
monitoring: bool,
|
||||
/// Write input to sequence.
|
||||
recording: bool,
|
||||
/// Don't delete when recording.
|
||||
overdub: bool,
|
||||
|
||||
/// Display mode
|
||||
mode: SequencerView,
|
||||
|
|
@ -71,130 +68,101 @@ enum SequencerView {
|
|||
impl Sequencer {
|
||||
pub fn new (name: &str, timebase: &Arc<Timebase>) -> Usually<DynamicDevice<Self>> {
|
||||
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||
let transport = client.transport();
|
||||
let state = transport.query_state()?;
|
||||
DynamicDevice::new(render, handle, Self::process, Self {
|
||||
name: name.into(),
|
||||
transport: client.transport(),
|
||||
timebase: timebase.clone(),
|
||||
steps: 64,
|
||||
resolution: 8,
|
||||
name: name.into(),
|
||||
input_port: client.register_port("in", MidiIn::default())?,
|
||||
output_port: client.register_port("out", MidiOut::default())?,
|
||||
|
||||
input_port: client.register_port("in", MidiIn::default())?,
|
||||
input_connect: vec!["nanoKEY Studio * (capture): *".into()],
|
||||
monitoring: true,
|
||||
notes_on: vec![false;128],
|
||||
recording: false,
|
||||
overdub: true,
|
||||
sequence: 0,
|
||||
sequences: vec![std::collections::BTreeMap::new();8],
|
||||
playing: true,
|
||||
output_port: client.register_port("out", MidiOut::default())?,
|
||||
output_connect: vec![],
|
||||
timebase: timebase.clone(),
|
||||
steps: 64,
|
||||
resolution: 8,
|
||||
sequence: 0,
|
||||
sequences: vec![std::collections::BTreeMap::new();8],
|
||||
notes_on: vec![false;128],
|
||||
|
||||
mode: SequencerView::Horizontal,
|
||||
note_axis: (36, 68),
|
||||
note_cursor: 0,
|
||||
time_axis: (0, 64),
|
||||
time_cursor: 0,
|
||||
playing: TransportState::Starting,
|
||||
monitoring: true,
|
||||
recording: true,
|
||||
overdub: true,
|
||||
transport,
|
||||
|
||||
mode: SequencerView::Horizontal,
|
||||
note_axis: (36, 68),
|
||||
note_cursor: 0,
|
||||
time_axis: (0, 64),
|
||||
time_cursor: 0,
|
||||
}).activate(client)
|
||||
}
|
||||
|
||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
let transport = self.transport.query().unwrap();
|
||||
process_out(self, scope, &transport);
|
||||
process_in(self, scope, &transport);
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
||||
impl Ports for Sequencer {
|
||||
fn audio_ins (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn audio_outs (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![self.input_port.short_name()?])
|
||||
}
|
||||
fn midi_outs (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec![self.output_port.short_name()?])
|
||||
}
|
||||
fn connect (&mut self, connect: bool, source: &str, target: &str) {}
|
||||
}
|
||||
|
||||
fn process_in (s: &mut Sequencer, scope: &ProcessScope, transport: &::jack::TransportStatePosition) {
|
||||
if !s.recording {
|
||||
return
|
||||
}
|
||||
let pos = &transport.pos;
|
||||
let usecs = s.timebase.frame_to_usec(pos.frame() as usize);
|
||||
let steps = usecs / s.timebase.usec_per_step(s.resolution as usize);
|
||||
let step = steps % s.steps;
|
||||
let tick = step * s.timebase.ppq() / s.resolution;
|
||||
let mut sequence = &mut s.sequences[s.sequence];
|
||||
let mut writer = s.output_port.writer(scope);
|
||||
for event in s.input_port.iter(scope) {
|
||||
if s.monitoring {
|
||||
writer.write(&event).expect(&format!("{event:?}"))
|
||||
self.playing = transport.state;
|
||||
let pos = &transport.pos;
|
||||
let usecs = self.timebase.frame_to_usec(pos.frame() as usize);
|
||||
let steps = usecs / self.timebase.usec_per_step(self.resolution as usize);
|
||||
let step = steps % self.steps;
|
||||
let tick = step * self.timebase.ppq() / self.resolution;
|
||||
let mut sequence = &mut self.sequences[self.sequence];
|
||||
let mut writer = self.output_port.writer(scope);
|
||||
if self.playing == TransportState::Rolling {
|
||||
let frame = transport.pos.frame() as usize;
|
||||
let ticks = self.timebase.frames_to_ticks(
|
||||
frame,
|
||||
frame + scope.n_frames() as usize,
|
||||
self.timebase.fpb() as usize * self.steps / self.resolution
|
||||
);
|
||||
for (time, tick) in ticks.iter() {
|
||||
if let Some(events) = sequence.get(&(*tick as u32)) {
|
||||
for message in events.iter() {
|
||||
let mut buf = vec![];
|
||||
::midly::live::LiveEvent::Midi {
|
||||
channel: 0.into(),
|
||||
message: *message,
|
||||
}.write(&mut buf).unwrap();
|
||||
let midi = ::jack::RawMidi {
|
||||
time: *time as u32,
|
||||
bytes: &buf
|
||||
};
|
||||
writer.write(&midi).expect(&format!("{midi:?}"));
|
||||
//panic!("{midi:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.recording {
|
||||
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
||||
midly::live::LiveEvent::Midi { channel: _, message } => match message {
|
||||
midly::MidiMessage::NoteOn { key, vel: _ } => {
|
||||
s.notes_on[key.as_int() as usize] = true;
|
||||
let contains = sequence.contains_key(&(tick as u32));
|
||||
if contains {
|
||||
sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
},
|
||||
midly::MidiMessage::NoteOff { key, vel: _ } => {
|
||||
s.notes_on[key.as_int() as usize] = false;
|
||||
let contains = sequence.contains_key(&(tick as u32));
|
||||
if contains {
|
||||
sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
for event in self.input_port.iter(scope) {
|
||||
if self.monitoring {
|
||||
writer.write(&event).expect(&format!("{event:?}"))
|
||||
}
|
||||
if self.recording {
|
||||
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
||||
midly::live::LiveEvent::Midi { channel: _, message } => match message {
|
||||
midly::MidiMessage::NoteOn { key, vel: _ } => {
|
||||
self.notes_on[key.as_int() as usize] = true;
|
||||
let contains = sequence.contains_key(&(tick as u32));
|
||||
if contains {
|
||||
sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
},
|
||||
midly::MidiMessage::NoteOff { key, vel: _ } => {
|
||||
self.notes_on[key.as_int() as usize] = false;
|
||||
let contains = sequence.contains_key(&(tick as u32));
|
||||
if contains {
|
||||
sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_out (s: &mut Sequencer, scope: &ProcessScope, transport: &::jack::TransportStatePosition) {
|
||||
if !s.playing {
|
||||
return
|
||||
}
|
||||
if transport.state != ::jack::TransportState::Rolling {
|
||||
return
|
||||
}
|
||||
let frame = transport.pos.frame() as usize;
|
||||
let ticks = s.timebase.frames_to_ticks(
|
||||
frame,
|
||||
frame + scope.n_frames() as usize,
|
||||
s.timebase.fpb() as usize * s.steps / s.resolution
|
||||
);
|
||||
let mut writer = s.output_port.writer(scope);
|
||||
for (time, tick) in ticks.iter() {
|
||||
if let Some(events) = s.sequences[s.sequence].get(&(*tick as u32)) {
|
||||
for message in events.iter() {
|
||||
let mut buf = vec![];
|
||||
::midly::live::LiveEvent::Midi {
|
||||
channel: 1.into(),
|
||||
message: *message,
|
||||
}.write(&mut buf).unwrap();
|
||||
let midi = ::jack::RawMidi {
|
||||
time: *time as u32,
|
||||
bytes: &buf
|
||||
};
|
||||
writer.write(&midi).expect(&format!("{midi:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -243,11 +211,15 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
|
|||
let style = Style::default().gray();
|
||||
let timer = format!("{rep}.{step:02} / {reps}.{steps}");
|
||||
buf.set_string(x + width - 2 - timer.len() as u16, y + 1, &timer, style.bold().not_dim());
|
||||
if s.playing {
|
||||
buf.set_string(x + 2, y + 1, &format!("▶ PLAYING"), style.not_dim().white().bold());
|
||||
} else {
|
||||
buf.set_string(x + 2, y + 1, &format!("⏹ STOPPED"), style.dim().bold());
|
||||
}
|
||||
buf.set_string(x + 2, y + 1, &match s.playing {
|
||||
TransportState::Rolling => format!("▶ PLAYING"),
|
||||
TransportState::Starting => format!("READY ..."),
|
||||
TransportState::Stopped => format!("⏹ STOPPED")
|
||||
}, match s.playing {
|
||||
TransportState::Rolling => style.dim().bold(),
|
||||
TransportState::Starting => style.not_dim().bold(),
|
||||
TransportState::Stopped => style.not_dim().white().bold()
|
||||
});
|
||||
buf.set_string(x, y + 2, format!("├{}┤", "-".repeat((area.width - 2).into())), style.dim());
|
||||
//buf.set_string(x + 2, y + 2,
|
||||
//&format!("▶ PLAY"), if s.playing {
|
||||
|
|
@ -282,10 +254,9 @@ fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|||
let style = Style::default().gray();
|
||||
for i in 0..8 {
|
||||
buf.set_string(x + 2, y + 3 + i*2, &format!("▶ {}", &s.name), if i as usize == s.sequence {
|
||||
if s.playing {
|
||||
style.white().bold()
|
||||
} else {
|
||||
style.not_dim().bold()
|
||||
match s.playing {
|
||||
TransportState::Rolling => style.white().bold(),
|
||||
_ => style.not_dim().bold()
|
||||
}
|
||||
} else {
|
||||
style.dim()
|
||||
|
|
@ -458,17 +429,23 @@ impl SequencerView {
|
|||
}
|
||||
}
|
||||
fn stop_and_rewind (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.playing = false;
|
||||
s.transport.stop()?;
|
||||
s.transport.locate(0)?;
|
||||
s.playing = TransportState::Stopped;
|
||||
Ok(true)
|
||||
}
|
||||
fn toggle_play (s: &mut Sequencer) -> Usually<bool> {
|
||||
s.playing = !s.playing;
|
||||
if s.playing {
|
||||
s.transport.start()?;
|
||||
} else {
|
||||
s.transport.stop()?;
|
||||
s.transport.locate(0)?;
|
||||
}
|
||||
s.playing = match s.playing {
|
||||
TransportState::Stopped => {
|
||||
s.transport.start()?;
|
||||
TransportState::Starting
|
||||
},
|
||||
_ => {
|
||||
s.transport.stop()?;
|
||||
s.transport.locate(0)?;
|
||||
TransportState::Stopped
|
||||
},
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
fn toggle_record (s: &mut Sequencer) -> Usually<bool> {
|
||||
|
|
@ -525,3 +502,12 @@ fn quantize_prev (s: &mut Sequencer) -> Usually<bool> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DevicePorts for Sequencer {
|
||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec!["in".into()])
|
||||
}
|
||||
fn midi_outs (&self) -> Usually<Vec<String>> {
|
||||
Ok(vec!["out".into()])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub fn draw_vertical (
|
|||
let height = (s.time_axis.1-s.time_axis.0)/2;
|
||||
footer(s, buf, x, y, height);
|
||||
playhead(s, buf, x, y);
|
||||
Ok(Rect { x, y, width: area.width, height: height + 1 })
|
||||
Ok(Rect { x, y, width: area.width, height: height + 3 })
|
||||
}
|
||||
|
||||
fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
|
||||
|
|
|
|||
|
|
@ -12,16 +12,14 @@ pub mod draw;
|
|||
pub mod config;
|
||||
pub mod layout;
|
||||
pub mod time;
|
||||
pub mod port;
|
||||
|
||||
use crate::device::*;
|
||||
use crate::layout::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
fn main () -> Result<(), Box<dyn Error>> {
|
||||
let _cli = cli::Cli::parse();
|
||||
let xdg = microxdg::XdgApp::new("dawdle")?;
|
||||
crate::config::create_dirs(&xdg)?;
|
||||
let transport = Transport::new("Transport")?;
|
||||
let transport = crate::device::Transport::new("Transport")?;
|
||||
let timebase = transport.state.lock().unwrap().timebase();
|
||||
crate::device::run(Chain::new("Chain#0000", vec![
|
||||
Box::new(Sequencer::new("Phrase#000", &timebase)?),
|
||||
|
|
|
|||
14
src/port.rs
14
src/port.rs
|
|
@ -1,14 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
pub trait Ports {
|
||||
fn audio_ins (&self) -> Usually<Vec<String>>;
|
||||
fn audio_outs (&self) -> Usually<Vec<String>>;
|
||||
fn midi_ins (&self) -> Usually<Vec<String>>;
|
||||
fn midi_outs (&self) -> Usually<Vec<String>>;
|
||||
fn connect (&mut self, connect: bool, source: &str, target: &str);
|
||||
fn connect_all (&mut self, connections: &[(bool, &str, &str)]) {
|
||||
for (connect, source, target) in connections.iter() {
|
||||
self.connect(*connect, source, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ pub type Usually<T> = Result<T, Box<dyn Error>>;
|
|||
pub use crate::draw::*;
|
||||
pub use crate::device::*;
|
||||
pub use crate::time::*;
|
||||
pub use crate::port::*;
|
||||
pub use crate::layout::*;
|
||||
|
||||
pub use std::error::Error;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue