1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4use std::{collections::VecDeque, sync::mpsc};
5
6use midir::{MidiInput, MidiInputConnection};
7use tinyaudio::{OutputDevice, OutputDeviceParameters, run_output_device};
8
9pub struct AudioMidiShell {
11 pub midi_connections: MidiConnections,
13
14 pub output_device: OutputDevice,
16}
17
18impl AudioMidiShell {
19 pub fn spawn(
26 sample_rate: u32,
27 buffer_size: usize,
28 process_chunk_size: usize,
29 mut generator: impl AudioGenerator + Send + 'static,
30 ) -> Self {
31 let (midi_sender, midi_receiver) = mpsc::channel();
32 let midi_connections = init_midi(midi_sender);
33
34 generator.init(process_chunk_size);
35
36 let params = OutputDeviceParameters {
37 channels_count: 2,
38 sample_rate: sample_rate as usize,
39 channel_sample_count: buffer_size,
40 };
41
42 let mut out_samples = VecDeque::with_capacity(process_chunk_size);
43
44 let output_device = run_output_device(params, move |data| {
45 for samples in data.chunks_mut(params.channels_count) {
46 if out_samples.is_empty() {
47 while let Ok(message) = midi_receiver.try_recv() {
48 generator.process_midi(message.1.as_ref(), message.0);
49 }
50
51 let mut frames = vec![[0.0; 2]; process_chunk_size];
52 generator.process(&mut frames);
53
54 for frame in frames.iter().take(process_chunk_size) {
55 out_samples.push_back(*frame);
56 }
57 }
58
59 if let Some(s) = out_samples.pop_front() {
60 samples[0] = s[0];
61 samples[1] = s[1];
62 }
63 }
64 })
65 .unwrap();
66
67 Self {
68 midi_connections,
69 output_device,
70 }
71 }
72
73 pub fn run_forever(
79 sample_rate: u32,
80 buffer_size: usize,
81 process_chunk_size: usize,
82 generator: impl AudioGenerator + Send + 'static,
83 ) -> ! {
84 let _shell = Self::spawn(sample_rate, buffer_size, process_chunk_size, generator);
85
86 loop {
87 std::thread::sleep(std::time::Duration::from_millis(100));
88 }
89 }
90}
91
92pub trait AudioGenerator {
94 fn init(&mut self, _process_chunk_size: usize) {}
97
98 fn process(&mut self, frames: &mut [[f32; 2]]);
103
104 fn process_midi(&mut self, _message: &[u8], _timestamp: u64) {}
106}
107
108type MidiConnections = Vec<MidiInputConnection<mpsc::Sender<(u64, Vec<u8>)>>>;
110
111fn init_midi(sender: mpsc::Sender<(u64, Vec<u8>)>) -> MidiConnections {
113 let mut connections = MidiConnections::new();
114
115 let input = MidiInput::new(&(env!("CARGO_PKG_NAME").to_owned() + " scan input"))
116 .expect("MIDI Input error");
117
118 for port in input.ports().iter() {
119 let input = MidiInput::new(&(env!("CARGO_PKG_NAME").to_owned() + " input"))
120 .expect("MIDI Input error");
121 let port_name = input.port_name(port).unwrap();
122 log::info!("Connecting to MIDI input {}", port_name);
123 let conn = input
124 .connect(
125 port,
126 port_name.as_str(),
127 |timestamp, message, sender| {
128 sender.send((timestamp, Vec::from(message))).ok();
129 },
130 sender.clone(),
131 )
132 .ok();
133 connections.push(conn.unwrap());
134 }
135
136 connections
137}