#![warn(missing_docs)]
mod clock;
mod midi_connection;
pub use midi_connection::MidiInParam;
pub use midir::Ignore;
pub use mseq_core::*;
pub use mseq_tracks::*;
use clock::Clock;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use std::time::Instant;
use thiserror::Error;
use crate::midi_connection::*;
#[derive(Error, Debug)]
pub enum MSeqError {
#[error("Midi error [{}: {}]", file!(), line!())]
Midi(#[from] MidiError),
#[error("Failed to parse midi file [{f}: {l}]\n\t{0}", f=file!(), l=line!())]
Track(#[from] TrackError),
}
pub fn run(
conductor: impl Conductor + std::marker::Send + 'static,
out_port: Option<u32>,
midi_in: Vec<MidiInParam>,
) -> Result<(), MSeqError> {
let midi_out = StdMidiOut::new(out_port)?;
let midi_controller = MidiController::new(midi_out);
let ctx = Context::default();
if midi_in.is_empty() {
return run_no_input(ctx, midi_controller, conductor);
}
let slave_idx = midi_in.iter().position(|p| p.slave);
let slave_count = midi_in.iter().filter(|p| p.slave).count();
if slave_count > 1 {
log::warn!(
"{slave_count} MIDI inputs are marked as slave, but a single clock/transport source is \
supported: using input {} as the clock source, the others are treated as message-only inputs.",
slave_idx.unwrap()
);
}
let run = Arc::new(Mutex::new((conductor, midi_controller, ctx)));
let connections = midi_in
.into_iter()
.enumerate()
.map(|(input_id, params)| connect(input_id, params, Some(input_id) == slave_idx))
.collect::<Result<Vec<_>, _>>()?;
for (input_id, conn) in connections.iter().enumerate() {
let run_consumer = run.clone();
let channel = conn.message.clone();
thread::spawn(move || {
loop {
let pending = {
let mut queue = channel.queue.lock().unwrap();
while queue.is_empty() {
queue = channel.condvar.wait(queue).unwrap();
}
core::mem::take(&mut *queue)
};
let mut r = run_consumer.lock().unwrap();
let (ref mut conductor, ref mut controller, ref mut ctx) = *r;
ctx.handle_input(input_id, conductor, controller, pending);
}
});
}
let slave_system = connections.iter().find_map(|c| c.slave_system.clone());
if let Some(slave) = slave_system {
run_slave(run, slave)
} else {
run_master(run)
}
}
fn run_no_input(
mut ctx: Context,
mut controller: MidiController<impl MidiOut>,
mut conductor: impl Conductor,
) -> Result<(), MSeqError> {
ctx.init(&mut conductor, &mut controller);
let mut clock = Clock::new();
while ctx.is_running() {
ctx.process_pre_tick(&mut conductor, &mut controller);
clock.tick(&Duration::from_micros(ctx.get_period_us()));
ctx.process_post_tick(&mut controller);
}
controller.finish();
clock.tick(&Duration::from_micros(ctx.get_period_us()));
Ok(())
}
fn run_master(
run: Arc<Mutex<(impl Conductor, MidiController<impl MidiOut>, Context)>>,
) -> Result<(), MSeqError> {
{
let mut r = run.lock().unwrap();
let (ref mut conductor, ref mut controller, ref mut ctx) = *r;
ctx.init(conductor, controller);
}
let mut clock = Clock::new();
loop {
let period_us = {
let mut r = run.lock().unwrap();
let (ref mut conductor, ref mut controller, ref mut ctx) = *r;
ctx.process_pre_tick(conductor, controller);
ctx.get_period_us()
};
clock.tick(&Duration::from_micros(period_us));
let mut r = run.lock().unwrap();
let (_, ref mut controller, ref mut ctx) = *r;
ctx.process_post_tick(controller);
if !ctx.is_running() {
break;
}
}
let mut r = run.lock().unwrap();
let (_, ref mut controller, ref mut ctx) = *r;
controller.finish();
clock.tick(&Duration::from_micros(ctx.get_period_us()));
Ok(())
}
fn run_slave(
run: Arc<Mutex<(impl Conductor, MidiController<impl MidiOut>, Context)>>,
slave: NotifyQueue,
) -> Result<(), MSeqError> {
{
let mut r = run.lock().unwrap();
let (ref mut conductor, ref mut controller, ref mut ctx) = *r;
ctx.init(conductor, controller);
ctx.pause();
}
let mut bpm_counter = 0;
let mut bmp_time_stamp = Instant::now();
loop {
{
let mut r = run.lock().unwrap();
let (ref mut conductor, ref mut controller, ref mut ctx) = *r;
ctx.process_pre_tick(conductor, controller);
}
enum SysMessage {
Start,
Stop,
Continue,
}
loop {
let mut quit_loop = false;
let mut transport = vec![];
{
let mut mutex = slave.queue.lock().unwrap();
while mutex.is_empty() {
mutex = slave.condvar.wait(mutex).unwrap();
}
while let Some(message) = mutex.pop_front() {
match message {
MidiMessage::Clock => quit_loop = true,
MidiMessage::Start => transport.push(SysMessage::Start),
MidiMessage::Stop => transport.push(SysMessage::Stop),
MidiMessage::Continue => transport.push(SysMessage::Continue),
_ => unreachable!(),
}
}
}
if !transport.is_empty() {
let mut r = run.lock().unwrap();
let (_, ref mut controller, ref mut ctx) = *r;
for message in transport {
match message {
SysMessage::Start => ctx.start(),
SysMessage::Stop => ctx.pause(),
SysMessage::Continue => ctx.resume(),
}
}
ctx.flush_sys_instructions(controller);
}
if quit_loop {
break;
}
}
let mut r = run.lock().unwrap();
let (_, ref mut controller, ref mut ctx) = *r;
bpm_counter += 1;
if bpm_counter == 24 {
bpm_counter = 0;
let duration = bmp_time_stamp.elapsed().as_millis();
if let Some(bpm) = 60000_u128.checked_div(duration)
&& (1..=255).contains(&bpm)
{
ctx.set_bpm(bpm as u8);
}
bmp_time_stamp = Instant::now();
}
ctx.process_post_tick(controller);
if !ctx.is_running() {
break;
}
}
let mut r = run.lock().unwrap();
let (_, ref mut controller, ref mut ctx) = *r;
controller.finish();
let mut clock = Clock::new();
clock.tick(&Duration::from_micros(ctx.get_period_us()));
Ok(())
}