mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
i dont know why that worked
This commit is contained in:
parent
4ae62c5bc2
commit
b73aa8a0dc
17 changed files with 552 additions and 481 deletions
|
|
@ -7,17 +7,14 @@ pub struct Chain {
|
|||
|
||||
impl Chain {
|
||||
pub fn new (name: &str, devices: Vec<Box<dyn Device>>) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
devices
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process (
|
||||
client: &Client,
|
||||
scope: &ProcessScope
|
||||
) -> Control {
|
||||
pub fn process (state: &mut Chain, client: &Client, scope: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +51,6 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
|
|||
Ok(Rect { x: area.x, y: area.y, width: x, height: y })
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Chain, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (state: &mut Chain, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub struct Launcher {
|
|||
|
||||
impl Launcher {
|
||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
Ok(DynamicDevice::new(render, handle, Self {
|
||||
name: name.into(),
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ pub struct Looper {
|
|||
|
||||
impl Looper {
|
||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process (
|
||||
state: &mut Looper,
|
||||
client: &Client,
|
||||
scope: &ProcessScope
|
||||
) -> Control {
|
||||
|
|
@ -32,7 +33,7 @@ pub fn render (state: &Looper, buf: &mut Buffer, area: Rect)
|
|||
Ok(Rect::default())
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Looper, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (state: &mut Looper, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use crate::prelude::*;
|
|||
|
||||
pub struct Mixer {
|
||||
name: String,
|
||||
jack: Jack<MixerJack>,
|
||||
tracks: Vec<Track>,
|
||||
selected_track: usize,
|
||||
selected_column: usize,
|
||||
|
|
@ -10,33 +9,27 @@ pub struct Mixer {
|
|||
|
||||
impl Mixer {
|
||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
let (client, status) = Client::new(
|
||||
name,
|
||||
ClientOptions::NO_START_SERVER
|
||||
)?;
|
||||
let jack = MixerJack.activate(client, ClosureProcessHandler::new(Box::new(
|
||||
move|client: &Client, scope: &ProcessScope|process(client, scope)
|
||||
) as BoxedProcessHandler))?;
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
selected_column: 0,
|
||||
selected_track: 1,
|
||||
tracks: vec![
|
||||
Track::new(&jack.as_client(), 1, "Mono 1")?,
|
||||
Track::new(&jack.as_client(), 1, "Mono 2")?,
|
||||
Track::new(&jack.as_client(), 2, "Stereo 1")?,
|
||||
Track::new(&jack.as_client(), 2, "Stereo 2")?,
|
||||
Track::new(&jack.as_client(), 2, "Stereo 3")?,
|
||||
Track::new(&jack.as_client(), 2, "Bus 1")?,
|
||||
Track::new(&jack.as_client(), 2, "Bus 2")?,
|
||||
Track::new(&jack.as_client(), 2, "Mix")?,
|
||||
Track::new(&client, 1, "Mono 1")?,
|
||||
Track::new(&client, 1, "Mono 2")?,
|
||||
Track::new(&client, 2, "Stereo 1")?,
|
||||
Track::new(&client, 2, "Stereo 2")?,
|
||||
Track::new(&client, 2, "Stereo 3")?,
|
||||
Track::new(&client, 2, "Bus 1")?,
|
||||
Track::new(&client, 2, "Bus 2")?,
|
||||
Track::new(&client, 2, "Mix")?,
|
||||
],
|
||||
jack,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process (
|
||||
mixer: &mut Mixer,
|
||||
client: &Client,
|
||||
scope: &ProcessScope
|
||||
) -> Control {
|
||||
|
|
@ -107,8 +100,8 @@ pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
|
|||
Ok(area)
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Mixer, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
||||
if let EngineEvent::Input(crossterm::event::Event::Key(event)) = event {
|
||||
pub fn handle (state: &mut Mixer, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
if let AppEvent::Input(crossterm::event::Event::Key(event)) = event {
|
||||
|
||||
match event.code {
|
||||
//KeyCode::Char('c') => {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,16 @@ pub struct Plugin {
|
|||
|
||||
impl Plugin {
|
||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process (state: &mut Plugin, client: &Client, scope: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, width, height }: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
|
|
@ -27,6 +31,6 @@ pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, width, height }: R
|
|||
Ok(Rect { x, y, width: 40, height: 7 })
|
||||
}
|
||||
|
||||
pub fn handle (state: &mut Plugin, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (state: &mut Plugin, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub const ACTIONS: [(&'static str, &'static str);2] = [
|
|||
|
||||
pub struct Sampler {
|
||||
name: String,
|
||||
jack: Jack<SamplerJack>,
|
||||
input: ::jack::Port<::jack::MidiIn>,
|
||||
samples: Arc<Mutex<Vec<Sample>>>,
|
||||
selected_sample: usize,
|
||||
selected_column: usize,
|
||||
|
|
@ -23,36 +23,23 @@ impl Sampler {
|
|||
];
|
||||
let samples = Arc::new(Mutex::new(samples));
|
||||
let input = client.register_port("trigger", ::jack::MidiIn::default())?;
|
||||
let jack = SamplerJack.activate(client, ClosureProcessHandler::new({
|
||||
let exited = exited.clone();
|
||||
let samples = samples.clone();
|
||||
Box::new(move |client: &Client, scope: &ProcessScope|{
|
||||
process(client, scope, &exited, &samples, &input);
|
||||
Control::Continue
|
||||
}) as BoxedProcessHandler
|
||||
}))?;
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
input,
|
||||
selected_sample: 0,
|
||||
selected_column: 0,
|
||||
samples,
|
||||
jack,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process (
|
||||
client: &Client,
|
||||
scope: &ProcessScope,
|
||||
exited: &Arc<AtomicBool>,
|
||||
samples: &Arc<Mutex<Vec<Sample>>>,
|
||||
input: &Port<MidiIn>
|
||||
state: &mut Sampler,
|
||||
client: &Client,
|
||||
scope: &ProcessScope,
|
||||
) -> Control {
|
||||
if exited.fetch_and(true, Ordering::Relaxed) {
|
||||
return Control::Quit
|
||||
}
|
||||
let mut samples = samples.lock().unwrap();
|
||||
for event in input.iter(scope) {
|
||||
let mut samples = state.samples.lock().unwrap();
|
||||
for event in state.input.iter(scope) {
|
||||
let len = 3.min(event.bytes.len());
|
||||
let mut data = [0; 3];
|
||||
data[..len].copy_from_slice(&event.bytes[..len]);
|
||||
|
|
@ -183,7 +170,7 @@ pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, width, height }:
|
|||
//Ok(())
|
||||
//}
|
||||
|
||||
pub fn handle (state: &mut Sampler, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (state: &mut Sampler, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
//if let Event::Input(crossterm::event::Event::Key(event)) = event {
|
||||
//match event.code {
|
||||
//KeyCode::Char('c') => {
|
||||
|
|
|
|||
|
|
@ -2,18 +2,16 @@ use crate::prelude::*;
|
|||
use ratatui::style::Stylize;
|
||||
|
||||
pub struct Sequencer {
|
||||
name: String,
|
||||
jack: Jack<SequencerJack>,
|
||||
playing: Arc<AtomicBool>,
|
||||
recording: Arc<AtomicBool>,
|
||||
overdub: Arc<AtomicBool>,
|
||||
inputs_open: Arc<AtomicBool>,
|
||||
outputs_open: Arc<AtomicBool>,
|
||||
cursor: (u16, u16, u16),
|
||||
timesig: (f32, f32),
|
||||
sequence: Arc<Mutex<Vec<Vec<Option<SequencerEvent>>>>>,
|
||||
sequences: Arc<Mutex<Vec<MIDISequence>>>,
|
||||
mode: SequencerMode,
|
||||
name: String,
|
||||
mode: SequencerMode,
|
||||
cursor: (u16, u16, u16),
|
||||
rate: Hz,
|
||||
tempo: Tempo,
|
||||
sequences: Vec<Sequence>,
|
||||
input_port: ::jack::Port<::jack::MidiIn>,
|
||||
input_connect: Vec<String>,
|
||||
output_port: ::jack::Port<::jack::MidiOut>,
|
||||
output_connect: Vec<String>,
|
||||
}
|
||||
|
||||
pub enum SequencerMode {
|
||||
|
|
@ -24,120 +22,111 @@ pub enum SequencerMode {
|
|||
}
|
||||
|
||||
impl Sequencer {
|
||||
|
||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
let beats = 4;
|
||||
let steps = 16;
|
||||
let bpm = 120.0;
|
||||
let rate = 44100; // Hz
|
||||
let frame = 1f64 / rate as f64; // msec
|
||||
let buf = 512; // frames
|
||||
let t_beat = 60.0 / bpm; // msec
|
||||
let t_loop = t_beat * beats as f64; // msec
|
||||
let t_step = t_beat / steps as f64; // msec
|
||||
let exited = Arc::new(AtomicBool::new(false));
|
||||
let playing = Arc::new(AtomicBool::new(true));
|
||||
let recording = Arc::new(AtomicBool::new(true));
|
||||
let overdub = Arc::new(AtomicBool::new(false));
|
||||
let sequence: Arc<Mutex<Vec<Vec<Option<SequencerEvent>>>>> = Arc::new(
|
||||
Mutex::new(vec![vec![None;64];128])
|
||||
);
|
||||
let mut step_frames = vec![];
|
||||
for step in 0..beats*steps {
|
||||
let step_index = (step as f64 * t_step / frame) as usize;
|
||||
step_frames.push(step_index);
|
||||
}
|
||||
let loop_frames = (t_loop*rate as f64) as usize;
|
||||
let mut frame_steps: Vec<Option<usize>> = vec![None;loop_frames];
|
||||
for (index, frame) in step_frames.iter().enumerate() {
|
||||
frame_steps[*frame] = Some(index);
|
||||
}
|
||||
let (client, _status) = Client::new(
|
||||
name, ClientOptions::NO_START_SERVER
|
||||
)?;
|
||||
let mut input = client.register_port(
|
||||
"input", ::jack::MidiIn::default()
|
||||
)?;
|
||||
let mut output = client.register_port(
|
||||
"output", ::jack::MidiOut::default()
|
||||
)?;
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
mode: SequencerMode::Vertical,
|
||||
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||
let mut input = client.register_port("input", ::jack::MidiIn::default())?;
|
||||
let mut output = client.register_port("output", ::jack::MidiOut::default())?;
|
||||
let device = DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
playing: playing.clone(),
|
||||
recording: recording.clone(),
|
||||
overdub: overdub.clone(),
|
||||
sequence: sequence.clone(),
|
||||
inputs_open: Arc::new(AtomicBool::new(false)),
|
||||
outputs_open: Arc::new(AtomicBool::new(false)),
|
||||
sequences: Arc::new(Mutex::new(vec![
|
||||
])),
|
||||
mode: SequencerMode::Vertical,
|
||||
cursor: (11, 0, 0),
|
||||
timesig: (4.0, 4.0),
|
||||
jack: SequencerJack.activate(client, ClosureProcessHandler::new(
|
||||
Box::new(move |client: &Client, scope: &ProcessScope| process(client, scope)) as BoxedProcessHandler
|
||||
))?
|
||||
}))
|
||||
rate: Hz(client.sample_rate() as u32),
|
||||
tempo: Tempo(120000),
|
||||
sequences: vec![
|
||||
Sequence::new(&client, "Rhythm#000",)?,
|
||||
Sequence::new(&client, "Rhythm#001",)?,
|
||||
Sequence::new(&client, "Rhythm#002",)?,
|
||||
Sequence::new(&client, "Rhythm#003",)?,
|
||||
],
|
||||
input_port: client.register_port("in", ::jack::MidiIn::default())?,
|
||||
input_connect: vec![],
|
||||
output_port: client.register_port("out", ::jack::MidiOut::default())?,
|
||||
output_connect: vec![],
|
||||
});
|
||||
device.activate(client)?;
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn process (
|
||||
client: &Client,
|
||||
scope: &ProcessScope
|
||||
) -> Control {
|
||||
//if exited.fetch_and(true, Ordering::Relaxed) {
|
||||
//return Control::Quit
|
||||
//}
|
||||
//if client.transport().query_state().unwrap() == TransportState::Rolling {
|
||||
//let chunk_start = scope.last_frame_time();
|
||||
//let chunk_size = scope.n_frames();
|
||||
//let chunk_end = chunk_start + chunk_size;
|
||||
//let start_looped = chunk_start as usize % loop_frames;
|
||||
//let end_looped = chunk_end as usize % loop_frames;
|
||||
|
||||
//// Write MIDI notes from input to sequence
|
||||
//if recording.fetch_and(true, Ordering::Relaxed) {
|
||||
////let overdub = overdub.fetch_and(true, Ordering::Relaxed);
|
||||
//for event in input.iter(scope) {
|
||||
//let ::jack::RawMidi { time, bytes } = event;
|
||||
//println!("\n{time} {bytes:?}");
|
||||
//}
|
||||
//}
|
||||
|
||||
//// Write MIDI notes from sequence to output
|
||||
//if playing.fetch_and(true, Ordering::Relaxed) {
|
||||
//let mut writer = output.writer(scope);
|
||||
//let sequence = sequence.lock().unwrap();
|
||||
//for frame in 0..chunk_size {
|
||||
//let value = frame_steps[(start_looped + frame as usize) % loop_frames];
|
||||
//if let Some(step) = value {
|
||||
//for track in sequence.iter() {
|
||||
//for event in track[step].iter() {
|
||||
//writer.write(&::jack::RawMidi {
|
||||
//time: frame as u32,
|
||||
//bytes: &match event {
|
||||
//SequencerEvent::NoteOn(pitch, velocity) => [
|
||||
//0b10010000,
|
||||
//*pitch,
|
||||
//*velocity
|
||||
//],
|
||||
//SequencerEvent::NoteOff(pitch) => [
|
||||
//0b10000000,
|
||||
//*pitch,
|
||||
//0b00000000
|
||||
//],
|
||||
//}
|
||||
//}).unwrap()
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
pub fn process (state: &mut Sequencer, client: &Client, scope: &ProcessScope) -> Control {
|
||||
for seq in state.sequences.iter() {
|
||||
seq.chunk_out(scope, &mut state.output_port);
|
||||
seq.chunk_in(scope, &state.input_port);
|
||||
}
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
pub struct Sequence {
|
||||
is_recording: bool,
|
||||
overdub: bool,
|
||||
ppq: u32,
|
||||
buffer: std::collections::BTreeMap<u32, Option<Vec<Vec<u8>>>>,
|
||||
loop_points: Option<(u32, u32)>,
|
||||
is_playing: bool,
|
||||
}
|
||||
|
||||
impl Sequence {
|
||||
fn new (client: &Client, name: &str) -> Usually<Self> {
|
||||
Ok(Self {
|
||||
is_recording: false,
|
||||
overdub: false,
|
||||
ppq: 96,
|
||||
buffer: std::collections::BTreeMap::new(),
|
||||
loop_points: None,
|
||||
is_playing: false
|
||||
})
|
||||
}
|
||||
fn chunk_in (
|
||||
&self,
|
||||
scope: &ProcessScope,
|
||||
port: &::jack::Port<::jack::MidiIn>,
|
||||
) {
|
||||
if self.is_recording {
|
||||
for event in port.iter(scope) {
|
||||
let ::jack::RawMidi { time, bytes } = event;
|
||||
println!("\n{time} {bytes:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
fn chunk_out (
|
||||
&self,
|
||||
scope: &ProcessScope,
|
||||
port: &mut ::jack::Port<::jack::MidiOut>,
|
||||
) {
|
||||
if self.is_playing {
|
||||
let size = scope.n_frames();
|
||||
let start = scope.last_frame_time();
|
||||
let end = start + size;
|
||||
let mut writer = port.writer(scope);
|
||||
for time in 0..size {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl NotificationHandler for Sequencer {
|
||||
fn thread_init (&self, _: &Client) {}
|
||||
fn shutdown (&mut self, status: ClientStatus, reason: &str) {}
|
||||
fn freewheel (&mut self, _: &Client, is_enabled: bool) {}
|
||||
fn sample_rate (&mut self, _: &Client, _: Frames) -> Control {
|
||||
Control::Quit
|
||||
}
|
||||
fn client_registration (&mut self, _: &Client, name: &str, is_reg: bool) {}
|
||||
fn port_registration (&mut self, _: &Client, port_id: PortId, is_reg: bool) {}
|
||||
fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
fn ports_connected (&mut self, _: &Client, id_a: PortId, id_b: PortId, are: bool) {}
|
||||
fn graph_reorder (&mut self, _: &Client) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
fn xrun (&mut self, _: &Client) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
||||
pub const ACTIONS: [(&'static str, &'static str);4] = [
|
||||
("+/-", "Zoom"),
|
||||
("A/D", "Add/delete note"),
|
||||
|
|
@ -145,12 +134,12 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
|
|||
("CapsLock", "Auto advance"),
|
||||
];
|
||||
|
||||
pub fn handle (state: &mut Sequencer, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (state: &mut Sequencer, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
|
||||
if let EngineEvent::Focus = event {
|
||||
if let AppEvent::Focus = event {
|
||||
}
|
||||
|
||||
if let EngineEvent::Input(Event::Key(event)) = event {
|
||||
if let AppEvent::Input(Event::Key(event)) = event {
|
||||
match event.code {
|
||||
KeyCode::Down => {
|
||||
state.cursor.0 = if state.cursor.0 >= 23 {
|
||||
|
|
@ -189,20 +178,20 @@ pub fn handle (state: &mut Sequencer, event: &EngineEvent) -> Result<(), Box<dyn
|
|||
state.cursor.2 = state.cursor.2 + 1
|
||||
},
|
||||
KeyCode::Char('i') => {
|
||||
state.inputs_open.fetch_xor(true, Ordering::Relaxed);
|
||||
//state.inputs_open.fetch_xor(true, Ordering::Relaxed);
|
||||
},
|
||||
KeyCode::Char('o') => {
|
||||
state.outputs_open.fetch_xor(true, Ordering::Relaxed);
|
||||
//state.outputs_open.fetch_xor(true, Ordering::Relaxed);
|
||||
},
|
||||
KeyCode::Char('a') => {
|
||||
let row = state.cursor.0 as usize;
|
||||
let step = state.cursor.1 as usize;
|
||||
let duration = state.cursor.2 as usize;
|
||||
let mut sequence = state.sequence.lock().unwrap();
|
||||
sequence[row][step] = Some(SequencerEvent::NoteOn(48 - row as u8, 128));
|
||||
if state.cursor.2 > 0 {
|
||||
sequence[row][step + duration] = Some(SequencerEvent::NoteOff(35));
|
||||
}
|
||||
//let mut sequence = state.sequence.lock().unwrap();
|
||||
//sequence[row][step] = Some(SequencerEvent::NoteOn(48 - row as u8, 128));
|
||||
//if state.cursor.2 > 0 {
|
||||
//sequence[row][step + duration] = Some(SequencerEvent::NoteOff(35));
|
||||
//}
|
||||
},
|
||||
_ => {
|
||||
println!("{event:?}");
|
||||
|
|
@ -518,186 +507,83 @@ fn draw_sequence_cursor (
|
|||
buf.set_string(area.x + cursor.1 + 3, area.y + 1 + cursor_y, cursor_text, style);
|
||||
}
|
||||
|
||||
pub struct SequencerJack;
|
||||
//pub type MIDISequenceVoice = std::collections::BTreeMap<u32, NoteEvent>;
|
||||
|
||||
impl SequencerJack {
|
||||
fn activate (self, client: Client, handler: ClosureProcessHandler<BoxedProcessHandler>)
|
||||
-> Usually<AsyncClient<Self, ClosureProcessHandler<BoxedProcessHandler>>>
|
||||
{
|
||||
Ok(client.activate_async(self, handler)?)
|
||||
}
|
||||
}
|
||||
//#[derive(Clone)]
|
||||
//pub enum NoteEvent {
|
||||
//On(u8),
|
||||
//Off(u8),
|
||||
//}
|
||||
|
||||
impl NotificationHandler for SequencerJack {
|
||||
fn thread_init (&self, _: &Client) {
|
||||
}
|
||||
//const VOICE_EMPTY: MIDISequenceVoice = MIDISequenceVoice::new();
|
||||
|
||||
fn shutdown (&mut self, status: ClientStatus, reason: &str) {
|
||||
}
|
||||
//impl MIDISequence {
|
||||
//fn new () -> Self {
|
||||
//Self {
|
||||
//channels: [
|
||||
//MIDISequenceChannel::new(1),
|
||||
//MIDISequenceChannel::new(2),
|
||||
//MIDISequenceChannel::new(3),
|
||||
//MIDISequenceChannel::new(4),
|
||||
//MIDISequenceChannel::new(5),
|
||||
//MIDISequenceChannel::new(6),
|
||||
//MIDISequenceChannel::new(7),
|
||||
//MIDISequenceChannel::new(8),
|
||||
//MIDISequenceChannel::new(9),
|
||||
//MIDISequenceChannel::new(10),
|
||||
//MIDISequenceChannel::new(11),
|
||||
//MIDISequenceChannel::new(12),
|
||||
//MIDISequenceChannel::new(13),
|
||||
//MIDISequenceChannel::new(14),
|
||||
//MIDISequenceChannel::new(15),
|
||||
//MIDISequenceChannel::new(16),
|
||||
//]
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
fn freewheel (&mut self, _: &Client, is_enabled: bool) {
|
||||
}
|
||||
//pub struct MIDISequence {
|
||||
//channels: [MIDISequenceChannel;16],
|
||||
//}
|
||||
//pub struct MIDISequenceChannel {
|
||||
//number: u8,
|
||||
//notes: [MIDISequenceVoice;128],
|
||||
//}
|
||||
//impl MIDISequenceChannel {
|
||||
//fn new (number: u8) -> Self {
|
||||
//Self {
|
||||
//number,
|
||||
//notes: [VOICE_EMPTY;128]
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
fn sample_rate (&mut self, _: &Client, _: Frames) -> Control {
|
||||
Control::Quit
|
||||
}
|
||||
//#[derive(Clone)]
|
||||
//pub enum SequencerEvent {
|
||||
//NoteOn(u8, u8),
|
||||
//NoteOff(u8)
|
||||
//}
|
||||
|
||||
fn client_registration (&mut self, _: &Client, name: &str, is_reg: bool) {
|
||||
}
|
||||
|
||||
fn port_registration (&mut self, _: &Client, port_id: PortId, is_reg: bool) {
|
||||
}
|
||||
|
||||
fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
fn ports_connected (&mut self, _: &Client, id_a: PortId, id_b: PortId, are: bool) {
|
||||
}
|
||||
|
||||
fn graph_reorder (&mut self, _: &Client) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
fn xrun (&mut self, _: &Client) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MIDISequence {
|
||||
channels: [MIDISequenceChannel;16],
|
||||
}
|
||||
|
||||
pub struct MIDISequenceChannel {
|
||||
number: u8,
|
||||
notes: [MIDISequenceVoice;128],
|
||||
}
|
||||
|
||||
pub type MIDISequenceVoice = std::collections::BTreeMap<u32, NoteEvent>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum NoteEvent {
|
||||
On(u8),
|
||||
Off(u8),
|
||||
}
|
||||
|
||||
const VOICE_EMPTY: MIDISequenceVoice = MIDISequenceVoice::new();
|
||||
|
||||
impl MIDISequence {
|
||||
fn new () -> Self {
|
||||
Self {
|
||||
channels: [
|
||||
MIDISequenceChannel::new(1),
|
||||
MIDISequenceChannel::new(2),
|
||||
MIDISequenceChannel::new(3),
|
||||
MIDISequenceChannel::new(4),
|
||||
MIDISequenceChannel::new(5),
|
||||
MIDISequenceChannel::new(6),
|
||||
MIDISequenceChannel::new(7),
|
||||
MIDISequenceChannel::new(8),
|
||||
MIDISequenceChannel::new(9),
|
||||
MIDISequenceChannel::new(10),
|
||||
MIDISequenceChannel::new(11),
|
||||
MIDISequenceChannel::new(12),
|
||||
MIDISequenceChannel::new(13),
|
||||
MIDISequenceChannel::new(14),
|
||||
MIDISequenceChannel::new(15),
|
||||
MIDISequenceChannel::new(16),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MIDISequenceChannel {
|
||||
fn new (number: u8) -> Self {
|
||||
Self {
|
||||
number,
|
||||
notes: [VOICE_EMPTY;128]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SequencerEvent {
|
||||
NoteOn(u8, u8),
|
||||
NoteOff(u8)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_midi_frames () {
|
||||
let beats = 4;
|
||||
let steps = 16;
|
||||
let bpm = 120;
|
||||
let rate = 44100; // Hz
|
||||
let frame = 1f64 / rate as f64; // msec
|
||||
let buf = 512; // frames
|
||||
let t_beat = 60.0 / bpm as f64; // msec
|
||||
let t_loop = t_beat * beats as f64; // msec
|
||||
let t_step = t_beat / steps as f64; // msec
|
||||
|
||||
let assign = |chunk: usize| {
|
||||
let start = chunk * buf; // frames
|
||||
let end = (chunk + 1) * buf; // frames
|
||||
println!("{chunk}: {start} .. {end}");
|
||||
let mut steps: Vec<(usize, usize, f64)> = vec![];
|
||||
for frame_index in start..end {
|
||||
let frame_msec = frame_index as f64 * frame;
|
||||
let offset = (frame_msec * 1000.0) % (t_step * 1000.0);
|
||||
if offset < 0.1 {
|
||||
let time = frame_index - start;
|
||||
let step_index = (frame_msec % t_loop / t_step) as usize;
|
||||
println!("{chunk}: {frame_index} ({time}) -> {step_index} ({frame_msec} % {t_step} = {offset})");
|
||||
steps.push((time, step_index, offset));
|
||||
}
|
||||
}
|
||||
steps
|
||||
};
|
||||
|
||||
for chunk in 0..10 {
|
||||
let chunk = assign(chunk);
|
||||
//println!("{chunk} {:#?}", assign(chunk));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_midi_frames_2 () {
|
||||
let beats = 4;
|
||||
let steps = 16;
|
||||
let bpm = 120;
|
||||
let rate = 44100; // Hz
|
||||
let frame = 1f64 / rate as f64; // msec
|
||||
let buf = 512; // frames
|
||||
let t_beat = 60.0 / bpm as f64; // msec
|
||||
let t_loop = t_beat * beats as f64; // msec
|
||||
let t_step = t_beat / steps as f64; // msec
|
||||
let mut step_frames = vec![];
|
||||
for step in 0..beats*steps {
|
||||
let step_index = (step as f64 * t_step / frame) as usize;
|
||||
step_frames.push(step_index);
|
||||
}
|
||||
let loop_frames = (t_loop*rate as f64) as usize;
|
||||
let mut frame_steps: Vec<Option<usize>> = vec![None;loop_frames];
|
||||
for (index, frame) in step_frames.iter().enumerate() {
|
||||
println!("{index} {frame}");
|
||||
frame_steps[*frame] = Some(index);
|
||||
}
|
||||
let assign = |chunk: usize| {
|
||||
let (start, end) = (chunk * buf, (chunk + 1) * buf); // frames
|
||||
let (start_looped, end_looped) = (start % loop_frames, end % loop_frames);
|
||||
println!("{chunk}: {start} .. {end} ({start_looped} .. {end_looped})");
|
||||
let mut steps: Vec<Option<usize>> = vec![None;buf];
|
||||
for frame in 0..buf {
|
||||
let value = frame_steps[(start_looped + frame) as usize % loop_frames];
|
||||
if value.is_some() { println!("{frame:03} = {value:?}, ") };
|
||||
steps[frame as usize] = value;
|
||||
}
|
||||
steps
|
||||
};
|
||||
for chunk in 0..1000 {
|
||||
let chunk = assign(chunk);
|
||||
//println!("{chunk} {:#?}", assign(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
//let buffer_index = self.to_buffer_index(chunk_start + frame, scope, bpm);
|
||||
//let value = frame_steps[(start_looped + frame as usize) % loop_frames];
|
||||
//if let Some(step) = value {
|
||||
//for track in sequence.iter() {
|
||||
//for event in track[step].iter() {
|
||||
//writer.write(&::jack::RawMidi {
|
||||
//time: frame as u32,
|
||||
//bytes: &match event {
|
||||
//SequencerEvent::NoteOn(pitch, velocity) => [
|
||||
//0b10010000,
|
||||
//*pitch,
|
||||
//*velocity
|
||||
//],
|
||||
//SequencerEvent::NoteOff(pitch) => [
|
||||
//0b10000000,
|
||||
//*pitch,
|
||||
//0b00000000
|
||||
//],
|
||||
//}
|
||||
//}).unwrap()
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
|
|
|||
77
src/device/sequencer_test.rs
Normal file
77
src/device/sequencer_test.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#![cfg(test)]
|
||||
|
||||
#[test]
|
||||
fn test_midi_frames () {
|
||||
let beats = 4;
|
||||
let steps = 16;
|
||||
let bpm = 120;
|
||||
let rate = 44100; // Hz
|
||||
let frame = 1f64 / rate as f64; // msec
|
||||
let buf = 512; // frames
|
||||
let t_beat = 60.0 / bpm as f64; // msec
|
||||
let t_loop = t_beat * beats as f64; // msec
|
||||
let t_step = t_beat / steps as f64; // msec
|
||||
|
||||
let assign = |chunk: usize| {
|
||||
let start = chunk * buf; // frames
|
||||
let end = (chunk + 1) * buf; // frames
|
||||
println!("{chunk}: {start} .. {end}");
|
||||
let mut steps: Vec<(usize, usize, f64)> = vec![];
|
||||
for frame_index in start..end {
|
||||
let frame_msec = frame_index as f64 * frame;
|
||||
let offset = (frame_msec * 1000.0) % (t_step * 1000.0);
|
||||
if offset < 0.1 {
|
||||
let time = frame_index - start;
|
||||
let step_index = (frame_msec % t_loop / t_step) as usize;
|
||||
println!("{chunk}: {frame_index} ({time}) -> {step_index} ({frame_msec} % {t_step} = {offset})");
|
||||
steps.push((time, step_index, offset));
|
||||
}
|
||||
}
|
||||
steps
|
||||
};
|
||||
|
||||
for chunk in 0..10 {
|
||||
let chunk = assign(chunk);
|
||||
//println!("{chunk} {:#?}", assign(chunk));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_midi_frames_2 () {
|
||||
let beats = 4;
|
||||
let steps = 16;
|
||||
let bpm = 120;
|
||||
let rate = 44100; // Hz
|
||||
let frame = 1f64 / rate as f64; // msec
|
||||
let buf = 512; // frames
|
||||
let t_beat = 60.0 / bpm as f64; // msec
|
||||
let t_loop = t_beat * beats as f64; // msec
|
||||
let t_step = t_beat / steps as f64; // msec
|
||||
let mut step_frames = vec![];
|
||||
for step in 0..beats*steps {
|
||||
let step_index = (step as f64 * t_step / frame) as usize;
|
||||
step_frames.push(step_index);
|
||||
}
|
||||
let loop_frames = (t_loop*rate as f64) as usize;
|
||||
let mut frame_steps: Vec<Option<usize>> = vec![None;loop_frames];
|
||||
for (index, frame) in step_frames.iter().enumerate() {
|
||||
println!("{index} {frame}");
|
||||
frame_steps[*frame] = Some(index);
|
||||
}
|
||||
let assign = |chunk: usize| {
|
||||
let (start, end) = (chunk * buf, (chunk + 1) * buf); // frames
|
||||
let (start_looped, end_looped) = (start % loop_frames, end % loop_frames);
|
||||
println!("{chunk}: {start} .. {end} ({start_looped} .. {end_looped})");
|
||||
let mut steps: Vec<Option<usize>> = vec![None;buf];
|
||||
for frame in 0..buf {
|
||||
let value = frame_steps[(start_looped + frame) as usize % loop_frames];
|
||||
if value.is_some() { println!("{frame:03} = {value:?}, ") };
|
||||
steps[frame as usize] = value;
|
||||
}
|
||||
steps
|
||||
};
|
||||
for chunk in 0..1000 {
|
||||
let chunk = assign(chunk);
|
||||
//println!("{chunk} {:#?}", assign(chunk));
|
||||
}
|
||||
}
|
||||
|
|
@ -2,21 +2,19 @@ use crate::prelude::*;
|
|||
|
||||
pub struct Transport {
|
||||
name: String,
|
||||
transport: Option<::jack::Transport>,
|
||||
bpm: f64,
|
||||
transport: ::jack::Transport,
|
||||
timesig: (f32, f32),
|
||||
bpm: f64,
|
||||
}
|
||||
|
||||
impl Transport {
|
||||
pub fn new ()
|
||||
-> Result<DynamicDevice<Self>, Box<dyn Error>>
|
||||
{
|
||||
//let transport = client.transport();
|
||||
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
|
||||
name: "Transport".into(),
|
||||
bpm: 113.0,
|
||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||
Ok(DynamicDevice::new(render, handle, process, Self {
|
||||
name: name.into(),
|
||||
transport: client.transport(),
|
||||
timesig: (4.0, 4.0),
|
||||
transport: None,
|
||||
bpm: 113.0,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +22,7 @@ impl Transport {
|
|||
}
|
||||
|
||||
pub fn play_or_pause (&mut self) -> Result<(), Box<dyn Error>> {
|
||||
match self.transport.as_ref().unwrap().query_state()? {
|
||||
match self.transport.query_state()? {
|
||||
TransportState::Stopped => self.play(),
|
||||
TransportState::Rolling => self.stop(),
|
||||
_ => Ok(())
|
||||
|
|
@ -32,14 +30,18 @@ impl Transport {
|
|||
}
|
||||
|
||||
pub fn play (&mut self) -> Result<(), Box<dyn Error>> {
|
||||
Ok(self.transport.as_ref().unwrap().start()?)
|
||||
Ok(self.transport.start()?)
|
||||
}
|
||||
|
||||
pub fn stop (&mut self) -> Result<(), Box<dyn Error>> {
|
||||
Ok(self.transport.as_ref().unwrap().stop()?)
|
||||
Ok(self.transport.stop()?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process (state: &mut Transport, client: &Client, scope: &ProcessScope) -> Control {
|
||||
Control::Continue
|
||||
}
|
||||
|
||||
pub fn render (state: &Transport, buf: &mut Buffer, mut area: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
|
|
@ -220,7 +222,7 @@ pub fn render (state: &Transport, buf: &mut Buffer, mut area: Rect)
|
|||
//Ok(())
|
||||
//}
|
||||
|
||||
pub fn handle (state: &mut Transport, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
|
||||
pub fn handle (state: &mut Transport, event: &AppEvent) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -231,9 +233,9 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
|
|||
("(Shift-)Space", "⯈ Play/pause"),
|
||||
];
|
||||
|
||||
struct Notifications;
|
||||
struct TransportJack;
|
||||
|
||||
impl NotificationHandler for Notifications {
|
||||
impl NotificationHandler for TransportJack {
|
||||
fn thread_init (&self, _: &Client) {
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue