mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: assigning steps to frames
This commit is contained in:
parent
7c1dc9ce9b
commit
faac61180b
6 changed files with 389 additions and 13 deletions
|
|
@ -72,7 +72,7 @@ fn run_one (command: &cli::Command) -> Result<(), Box<dyn Error>> {
|
|||
),
|
||||
|
||||
cli::Command::Sequencer => engine.run(
|
||||
&mut sequencer::Sequencer::new(engine.jack_client.as_client())?,
|
||||
&mut sequencer::Sequencer::new()?,
|
||||
|state, stdout, mut offset| {
|
||||
let (w, h) = render::render_toolbar_vertical(stdout, offset, &sequencer::ACTIONS)?;
|
||||
offset.0 = offset.0 + w + 2;
|
||||
|
|
@ -122,7 +122,7 @@ fn run_all () -> Result<(), Box<dyn Error>> {
|
|||
exited: false,
|
||||
mode: Mode::Sequencer,
|
||||
transport: transport::Transport::new(engine.jack_client.as_client())?,
|
||||
sequencer: sequencer::Sequencer::new(engine.jack_client.as_client())?,
|
||||
sequencer: sequencer::Sequencer::new()?,
|
||||
mixer: mixer::Mixer::new()?,
|
||||
looper: looper::Looper::new()?,
|
||||
sampler: sampler::Sampler::new()?,
|
||||
|
|
|
|||
|
|
@ -51,9 +51,10 @@ pub fn handle (state: &mut Sequencer, event: &Event) -> Result<(), Box<dyn Error
|
|||
let row = state.cursor.0 as usize;
|
||||
let step = state.cursor.1 as usize;
|
||||
let duration = state.cursor.2 as usize;
|
||||
state.sequence[row][step] = Some(super::Event::NoteOn(35, 128));
|
||||
let mut sequence = state.sequence.lock().unwrap();
|
||||
sequence[row][step] = Some(super::Event::NoteOn(35, 128));
|
||||
if state.cursor.2 > 0 {
|
||||
state.sequence[row][step + duration] = Some(super::Event::NoteOff(35));
|
||||
sequence[row][step + duration] = Some(super::Event::NoteOff(35));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -14,12 +14,13 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
|
|||
];
|
||||
|
||||
pub struct Sequencer {
|
||||
exited: bool,
|
||||
exited: Arc<AtomicBool>,
|
||||
cursor: (u16, u16, u16),
|
||||
sequence: Vec<Vec<Option<Event>>>,
|
||||
sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>>,
|
||||
transport: ::jack::Transport,
|
||||
bpm: f64,
|
||||
timesig: (f32, f32),
|
||||
pub jack_client: Jack<self::jack::Notifications>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -29,24 +30,179 @@ pub enum Event {
|
|||
}
|
||||
|
||||
impl Sequencer {
|
||||
pub fn new (client: &Client) -> Result<Self, Box<dyn Error>> {
|
||||
|
||||
pub fn new () -> Result<Self, Box<dyn Error>> {
|
||||
let exited = Arc::new(AtomicBool::new(false));
|
||||
let sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>> = Arc::new(Mutex::new(vec![vec![None;64];4]));
|
||||
let (client, _status) = Client::new(
|
||||
"blinkenlive-sequencer",
|
||||
ClientOptions::NO_START_SERVER
|
||||
)?;
|
||||
let transport = client.transport();
|
||||
let mut port = client.register_port("sequence", ::jack::MidiOut::default())?;
|
||||
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() {
|
||||
frame_steps[*frame] = Some(index);
|
||||
}
|
||||
let handler: BoxedProcessHandler = Box::new({
|
||||
let transport = client.transport();
|
||||
let exited = exited.clone();
|
||||
let sequence = sequence.clone();
|
||||
move |client: &Client, scope: &ProcessScope| -> Control {
|
||||
if exited.fetch_and(true, Ordering::Relaxed) {
|
||||
return Control::Quit
|
||||
}
|
||||
if 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;
|
||||
let mut writer = port.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 {
|
||||
Event::NoteOn(pitch, velocity) => [
|
||||
0b10010000,
|
||||
*pitch,
|
||||
*velocity
|
||||
],
|
||||
Event::NoteOff(pitch) => [
|
||||
0b10000000,
|
||||
*pitch,
|
||||
0b00000000
|
||||
],
|
||||
}
|
||||
}).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Control::Continue
|
||||
}
|
||||
});
|
||||
Ok(Self {
|
||||
exited: false,
|
||||
exited,
|
||||
cursor: (0, 0, 0),
|
||||
transport,
|
||||
sequence: vec![vec![None;64];4],
|
||||
sequence,
|
||||
bpm: 120.0,
|
||||
timesig: (4.0, 4.0)
|
||||
timesig: (4.0, 4.0),
|
||||
jack_client: client.activate_async(
|
||||
self::jack::Notifications,
|
||||
ClosureProcessHandler::new(handler)
|
||||
)?
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Exitable for Sequencer {
|
||||
fn exit (&mut self) {
|
||||
self.exited = true
|
||||
self.exited.store(true, Ordering::Relaxed)
|
||||
}
|
||||
fn exited (&self) -> bool {
|
||||
self.exited
|
||||
self.exited.fetch_and(true, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
#[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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ fn render_grid (
|
|||
.queue(move_to(17, 3))?.queue(Print("1.2"))?
|
||||
.queue(move_to(33, 3))?.queue(Print("1.3"))?
|
||||
.queue(move_to(49, 3))?.queue(Print("1.4"))?;
|
||||
for (index, row) in state.sequence.iter().enumerate() {
|
||||
let sequence = state.sequence.lock().unwrap();
|
||||
for (index, row) in sequence.iter().enumerate() {
|
||||
let y = index as u16 + 4;
|
||||
for x in 0u16..64 {
|
||||
let bg = if x as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue