Looper controlled by buttons
This commit is contained in:
parent
d1b88ff921
commit
72115f3783
1
firmware/Cargo.lock
generated
1
firmware/Cargo.lock
generated
@ -280,6 +280,7 @@ dependencies = [
|
|||||||
"defmt-rtt",
|
"defmt-rtt",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"embedded-midi",
|
"embedded-midi",
|
||||||
|
"midi-types",
|
||||||
"nb 1.1.0",
|
"nb 1.1.0",
|
||||||
"panic-halt",
|
"panic-halt",
|
||||||
"stm32f0xx-hal",
|
"stm32f0xx-hal",
|
||||||
|
|||||||
@ -2,6 +2,15 @@
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "stm32f042c4-blinky"
|
name = "stm32f042c4-blinky"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
default-run = "main"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "main"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mapper"
|
||||||
|
path = "src/mapper.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["inline-asm", "critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["inline-asm", "critical-section-single-core"] }
|
||||||
@ -9,6 +18,7 @@ cortex-m-rt = "0.7"
|
|||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
embedded-hal = "0.2"
|
embedded-hal = "0.2"
|
||||||
embedded-midi = "0.1.2"
|
embedded-midi = "0.1.2"
|
||||||
|
midi-types = "0.1.7"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
panic-halt = "0.2"
|
panic-halt = "0.2"
|
||||||
stm32f0xx-hal = { version = "0.18", features = ["stm32f042", "rt"] }
|
stm32f0xx-hal = { version = "0.18", features = ["stm32f042", "rt"] }
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::warn;
|
||||||
use hal::prelude::*;
|
use hal::prelude::*;
|
||||||
use hal::stm32;
|
use hal::stm32;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
@ -10,7 +11,6 @@ use stm32f0xx_hal as hal;
|
|||||||
use defmt::info;
|
use defmt::info;
|
||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
|
|
||||||
use embedded_midi::Channel;
|
|
||||||
use hal::{delay::Delay, serial::Serial};
|
use hal::{delay::Delay, serial::Serial};
|
||||||
|
|
||||||
defmt::timestamp!("{=u32}", {
|
defmt::timestamp!("{=u32}", {
|
||||||
@ -22,27 +22,16 @@ defmt::timestamp!("{=u32}", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
struct Bus {
|
struct Bus {
|
||||||
latch_en:
|
latch_en: hal::gpio::gpioa::PA0<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
stm32f0xx_hal::gpio::gpioa::PA0<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
select_en: hal::gpio::gpiob::PB8<hal::gpio::Output<hal::gpio::OpenDrain>>,
|
||||||
select_en: stm32f0xx_hal::gpio::gpiob::PB8<
|
pb0: hal::gpio::gpiob::PB0<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::OpenDrain>,
|
pb1: hal::gpio::gpiob::PB1<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
>,
|
pb2: hal::gpio::gpiob::PB2<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
pb0:
|
pb3: hal::gpio::gpiob::PB3<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
stm32f0xx_hal::gpio::gpiob::PB0<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
pb4: hal::gpio::gpiob::PB4<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
pb1:
|
pb5: hal::gpio::gpiob::PB5<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
stm32f0xx_hal::gpio::gpiob::PB1<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
pb6: hal::gpio::gpiob::PB6<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
pb2:
|
pb7: hal::gpio::gpiob::PB7<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
stm32f0xx_hal::gpio::gpiob::PB2<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
|
||||||
pb3:
|
|
||||||
stm32f0xx_hal::gpio::gpiob::PB3<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
|
||||||
pb4:
|
|
||||||
stm32f0xx_hal::gpio::gpiob::PB4<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
|
||||||
pb5:
|
|
||||||
stm32f0xx_hal::gpio::gpiob::PB5<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
|
||||||
pb6:
|
|
||||||
stm32f0xx_hal::gpio::gpiob::PB6<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
|
||||||
pb7:
|
|
||||||
stm32f0xx_hal::gpio::gpiob::PB7<stm32f0xx_hal::gpio::Output<stm32f0xx_hal::gpio::PushPull>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
@ -68,9 +57,8 @@ fn main() -> ! {
|
|||||||
let gpioa = dp.GPIOA.split(&mut rcc);
|
let gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
let gpiob = dp.GPIOB.split(&mut rcc);
|
let gpiob = dp.GPIOB.split(&mut rcc);
|
||||||
|
|
||||||
let button_1_5 = cortex_m::interrupt::free(|cs| gpiob.pb11.into_floating_input(cs));
|
let button_1_5 = cortex_m::interrupt::free(|cs| gpiob.pb13.into_floating_input(cs));
|
||||||
|
let button_6_10 = cortex_m::interrupt::free(|cs| gpiob.pb11.into_floating_input(cs));
|
||||||
let button_6_10 = cortex_m::interrupt::free(|cs| gpiob.pb13.into_floating_input(cs));
|
|
||||||
|
|
||||||
let mut bus = cortex_m::interrupt::free(|cs| Bus {
|
let mut bus = cortex_m::interrupt::free(|cs| Bus {
|
||||||
latch_en: gpioa.pa0.into_push_pull_output(cs),
|
latch_en: gpioa.pa0.into_push_pull_output(cs),
|
||||||
@ -92,79 +80,105 @@ fn main() -> ! {
|
|||||||
Serial::usart1(dp.USART1, (tx, rx), 31_250.bps(), &mut rcc)
|
Serial::usart1(dp.USART1, (tx, rx), 31_250.bps(), &mut rcc)
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_tx, rx) = serial.split();
|
let (tx, _rx) = serial.split();
|
||||||
let mut midi = embedded_midi::MidiIn::new(rx);
|
let mut midi = embedded_midi::MidiOut::new(tx);
|
||||||
|
|
||||||
|
bus.output(3, 0xFF, &mut delay);
|
||||||
|
bus.output(10, 0xFF, &mut delay);
|
||||||
|
bus.output(11, 0xFF, &mut delay);
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
let mut ic_03: u8 = 0;
|
let mut button_states = [false; 12];
|
||||||
let mut ic_10: u8 = 0;
|
|
||||||
let mut ic_11: u8 = 0;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let message = midi.read();
|
for i in 0_u8..6_u8 {
|
||||||
match message {
|
bus.output(11, 4_u8 << i, &mut delay);
|
||||||
Ok(embedded_midi::MidiMessage::ControlChange(channel, control, value))
|
delay.delay_us(1u16);
|
||||||
if channel == Channel::new(0) =>
|
let previous_state = &mut button_states[i as usize];
|
||||||
{
|
let new_state = button_1_5.is_high().unwrap_or(false);
|
||||||
bus.led_button_1(&mut delay);
|
if new_state != *previous_state {
|
||||||
if control == embedded_midi::Control::new(20) {
|
*previous_state = new_state;
|
||||||
bus.led_button_2(&mut delay);
|
let control = control_from_button(i);
|
||||||
ic_03 = value.into();
|
if new_state {
|
||||||
} else if control == embedded_midi::Control::new(21) {
|
info!("Button {} pressed", i);
|
||||||
bus.led_button_2(&mut delay);
|
if midi
|
||||||
let value: u8 = value.into();
|
.write(&embedded_midi::MidiMessage::ControlChange(
|
||||||
ic_03 = 128_u8 + value;
|
embedded_midi::Channel::C1,
|
||||||
} else if control == embedded_midi::Control::new(22) {
|
control,
|
||||||
bus.led_button_2(&mut delay);
|
midi_types::Value7::new(127),
|
||||||
ic_10 = value.into();
|
))
|
||||||
} else if control == embedded_midi::Control::new(23) {
|
.is_err()
|
||||||
bus.led_button_2(&mut delay);
|
{
|
||||||
let value: u8 = value.into();
|
warn!("Failed to send MIDI message");
|
||||||
ic_10 = 128_u8 + value;
|
}
|
||||||
} else if control == embedded_midi::Control::new(24) {
|
} else {
|
||||||
bus.led_button_2(&mut delay);
|
info!("Button {} released", i);
|
||||||
ic_11 = value.into();
|
if midi
|
||||||
} else if control == embedded_midi::Control::new(25) {
|
.write(&embedded_midi::MidiMessage::ControlChange(
|
||||||
bus.led_button_2(&mut delay);
|
embedded_midi::Channel::C1,
|
||||||
let value: u8 = value.into();
|
control,
|
||||||
ic_11 = 128_u8 + value;
|
midi_types::Value7::new(0),
|
||||||
|
))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
warn!("Failed to send MIDI message");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bus.output(03, ic_03, &mut delay);
|
|
||||||
bus.output(10, ic_10, &mut delay);
|
|
||||||
bus.output(11, ic_11, &mut delay);
|
|
||||||
}
|
}
|
||||||
Ok(_) => {
|
|
||||||
info!("unhandled midi message");
|
|
||||||
}
|
|
||||||
Err(nb::Error::WouldBlock) => {}
|
|
||||||
Err(nb::Error::Other(hal::serial::Error::Framing)) => {
|
|
||||||
info!("Error::Framing");
|
|
||||||
}
|
|
||||||
Err(nb::Error::Other(hal::serial::Error::Noise)) => {
|
|
||||||
info!("Error::Noise");
|
|
||||||
}
|
|
||||||
Err(nb::Error::Other(hal::serial::Error::Overrun)) => {
|
|
||||||
info!("Error::Overrun");
|
|
||||||
}
|
|
||||||
Err(nb::Error::Other(hal::serial::Error::Parity)) => {
|
|
||||||
info!("Error::Parity");
|
|
||||||
}
|
|
||||||
Err(nb::Error::Other(_)) => {
|
|
||||||
info!("Error::Other");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if button_1_5.is_high().unwrap_or(false) {
|
|
||||||
info!("Button 1-5 pressed");
|
|
||||||
delay.delay_ms(500u16);
|
|
||||||
}
|
|
||||||
|
|
||||||
if button_6_10.is_high().unwrap_or(false) {
|
let i = i + 6;
|
||||||
info!("Button 6-10 pressed");
|
let previous_state = &mut button_states[i as usize];
|
||||||
delay.delay_ms(500u16);
|
let new_state = button_6_10.is_high().unwrap_or(false);
|
||||||
|
if new_state != *previous_state {
|
||||||
|
*previous_state = new_state;
|
||||||
|
let control = control_from_button(i);
|
||||||
|
if new_state {
|
||||||
|
info!("Button {} pressed", i);
|
||||||
|
if midi
|
||||||
|
.write(&embedded_midi::MidiMessage::ControlChange(
|
||||||
|
embedded_midi::Channel::C1,
|
||||||
|
control,
|
||||||
|
midi_types::Value7::new(127),
|
||||||
|
))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
warn!("Failed to send MIDI message");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("Button {} released", i);
|
||||||
|
if midi
|
||||||
|
.write(&embedded_midi::MidiMessage::ControlChange(
|
||||||
|
embedded_midi::Channel::C1,
|
||||||
|
control,
|
||||||
|
midi_types::Value7::new(0),
|
||||||
|
))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
warn!("Failed to send MIDI message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn control_from_button(button: u8) -> embedded_midi::Control {
|
||||||
|
match button {
|
||||||
|
0 => embedded_midi::Control::new(20),
|
||||||
|
1 => embedded_midi::Control::new(21),
|
||||||
|
2 => embedded_midi::Control::new(22),
|
||||||
|
3 => embedded_midi::Control::new(23),
|
||||||
|
4 => embedded_midi::Control::new(24),
|
||||||
|
5 => embedded_midi::Control::new(31),
|
||||||
|
6 => embedded_midi::Control::new(25),
|
||||||
|
7 => embedded_midi::Control::new(26),
|
||||||
|
8 => embedded_midi::Control::new(27),
|
||||||
|
9 => embedded_midi::Control::new(28),
|
||||||
|
10 => embedded_midi::Control::new(29),
|
||||||
|
11 => embedded_midi::Control::new(30),
|
||||||
|
_ => embedded_midi::Control::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
fn output(&mut self, ic: u8, data: u8, delay: &mut Delay) {
|
fn output(&mut self, ic: u8, data: u8, delay: &mut Delay) {
|
||||||
match ic {
|
match ic {
|
||||||
@ -198,126 +212,10 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn select(&mut self, line: u8, delay: &mut Delay) {
|
fn select(&mut self, line: u8, delay: &mut Delay) {
|
||||||
self.select_en.set_low().ok();
|
self.write(line);
|
||||||
delay.delay_us(1u16);
|
|
||||||
match line {
|
|
||||||
0 => {
|
|
||||||
self.pb0.set_low().ok();
|
|
||||||
self.pb1.set_low().ok();
|
|
||||||
self.pb2.set_low().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
self.pb0.set_high().ok();
|
|
||||||
self.pb1.set_low().ok();
|
|
||||||
self.pb2.set_low().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
self.pb0.set_low().ok();
|
|
||||||
self.pb1.set_high().ok();
|
|
||||||
self.pb2.set_low().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
3 => {
|
|
||||||
self.pb0.set_high().ok();
|
|
||||||
self.pb1.set_high().ok();
|
|
||||||
self.pb2.set_low().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
4 => {
|
|
||||||
self.pb0.set_low().ok();
|
|
||||||
self.pb1.set_low().ok();
|
|
||||||
self.pb2.set_high().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
5 => {
|
|
||||||
self.pb0.set_high().ok();
|
|
||||||
self.pb1.set_low().ok();
|
|
||||||
self.pb2.set_high().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
6 => {
|
|
||||||
self.pb0.set_low().ok();
|
|
||||||
self.pb1.set_high().ok();
|
|
||||||
self.pb2.set_high().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
7 => {
|
|
||||||
self.pb0.set_high().ok();
|
|
||||||
self.pb1.set_high().ok();
|
|
||||||
self.pb2.set_high().ok();
|
|
||||||
self.pb3.set_low().ok();
|
|
||||||
self.pb4.set_low().ok();
|
|
||||||
self.pb5.set_low().ok();
|
|
||||||
self.pb6.set_low().ok();
|
|
||||||
self.pb7.set_low().ok();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
delay.delay_us(1u16);
|
delay.delay_us(1u16);
|
||||||
self.latch_en.set_high().ok();
|
self.latch_en.set_high().ok();
|
||||||
delay.delay_us(1u16);
|
delay.delay_us(1u16);
|
||||||
self.latch_en.set_low().ok();
|
self.latch_en.set_low().ok();
|
||||||
delay.delay_us(1u16);
|
|
||||||
self.select_en.set_high().ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn led_button_1(&mut self, delay: &mut Delay) {
|
|
||||||
self.output(03, 0b1111_1011, delay);
|
|
||||||
self.output(10, 0b0001_0000, delay);
|
|
||||||
self.output(11, 0b0000_0000, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn led_button_2(&mut self, delay: &mut Delay) {
|
|
||||||
self.output(03, 0b1111_1101, delay);
|
|
||||||
self.output(10, 0b0001_0000, delay);
|
|
||||||
self.output(11, 0b0000_0000, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn led_button_3(&mut self, delay: &mut Delay) {
|
|
||||||
self.output(03, 0b1111_1110, delay);
|
|
||||||
self.output(10, 0b0001_0000, delay);
|
|
||||||
self.output(11, 0b0000_0000, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn led_button_9(&mut self, delay: &mut Delay) {
|
|
||||||
self.output(03, 0b1110_1111, delay);
|
|
||||||
self.output(10, 0b0001_0000, delay);
|
|
||||||
self.output(11, 0b0000_0000, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn led_button_10(&mut self, delay: &mut Delay) {
|
|
||||||
self.output(03, 0b1111_0111, delay);
|
|
||||||
self.output(10, 0b0001_0000, delay);
|
|
||||||
self.output(11, 0b0000_0000, delay);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
188
firmware/src/mapper.rs
Executable file
188
firmware/src/mapper.rs
Executable file
@ -0,0 +1,188 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use hal::prelude::*;
|
||||||
|
use hal::stm32;
|
||||||
|
use panic_halt as _;
|
||||||
|
use stm32f0xx_hal as hal;
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use defmt_rtt as _;
|
||||||
|
|
||||||
|
use embedded_midi::Channel;
|
||||||
|
use hal::{delay::Delay, serial::Serial};
|
||||||
|
|
||||||
|
defmt::timestamp!("{=u32}", {
|
||||||
|
static mut COUNTER: u32 = 0;
|
||||||
|
unsafe {
|
||||||
|
COUNTER = COUNTER.wrapping_add(1);
|
||||||
|
COUNTER
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
struct Bus {
|
||||||
|
latch_en: hal::gpio::gpioa::PA0<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
select_en: hal::gpio::gpiob::PB8<hal::gpio::Output<hal::gpio::OpenDrain>>,
|
||||||
|
pb0: hal::gpio::gpiob::PB0<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
pb1: hal::gpio::gpiob::PB1<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
pb2: hal::gpio::gpiob::PB2<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
pb3: hal::gpio::gpiob::PB3<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
pb4: hal::gpio::gpiob::PB4<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
pb5: hal::gpio::gpiob::PB5<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
pb6: hal::gpio::gpiob::PB6<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
pb7: hal::gpio::gpiob::PB7<hal::gpio::Output<hal::gpio::PushPull>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
// Get device peripherals
|
||||||
|
let mut dp = stm32::Peripherals::take().unwrap();
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Configure the clock to 48 MHz
|
||||||
|
let mut rcc = dp
|
||||||
|
.RCC
|
||||||
|
.configure()
|
||||||
|
.hsi48()
|
||||||
|
.enable_crs(dp.CRS)
|
||||||
|
.sysclk(48.mhz())
|
||||||
|
.pclk(24.mhz())
|
||||||
|
.freeze(&mut dp.FLASH);
|
||||||
|
|
||||||
|
// Set up delay provider
|
||||||
|
let mut delay = Delay::new(cp.SYST, &rcc);
|
||||||
|
|
||||||
|
// Configure GPIO
|
||||||
|
let gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
|
let gpiob = dp.GPIOB.split(&mut rcc);
|
||||||
|
|
||||||
|
let button_1_5 = cortex_m::interrupt::free(|cs| gpiob.pb13.into_floating_input(cs));
|
||||||
|
let button_6_10 = cortex_m::interrupt::free(|cs| gpiob.pb11.into_floating_input(cs));
|
||||||
|
|
||||||
|
let mut bus = cortex_m::interrupt::free(|cs| Bus {
|
||||||
|
latch_en: gpioa.pa0.into_push_pull_output(cs),
|
||||||
|
select_en: gpiob.pb8.into_open_drain_output(cs),
|
||||||
|
pb0: gpiob.pb0.into_push_pull_output(cs),
|
||||||
|
pb1: gpiob.pb1.into_push_pull_output(cs),
|
||||||
|
pb2: gpiob.pb2.into_push_pull_output(cs),
|
||||||
|
pb3: gpiob.pb3.into_push_pull_output(cs),
|
||||||
|
pb4: gpiob.pb4.into_push_pull_output(cs),
|
||||||
|
pb5: gpiob.pb5.into_push_pull_output(cs),
|
||||||
|
pb6: gpiob.pb6.into_push_pull_output(cs),
|
||||||
|
pb7: gpiob.pb7.into_push_pull_output(cs),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure serial port
|
||||||
|
let serial = cortex_m::interrupt::free(|cs| {
|
||||||
|
let tx = gpioa.pa9.into_alternate_af1(cs);
|
||||||
|
let rx = gpioa.pa10.into_alternate_af1(cs);
|
||||||
|
Serial::usart1(dp.USART1, (tx, rx), 31_250.bps(), &mut rcc)
|
||||||
|
});
|
||||||
|
|
||||||
|
let (_tx, rx) = serial.split();
|
||||||
|
let mut midi = embedded_midi::MidiIn::new(rx);
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
let mut ic_03: u8 = 0;
|
||||||
|
let mut ic_10: u8 = 0;
|
||||||
|
let mut ic_11: u8 = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let message = midi.read();
|
||||||
|
match message {
|
||||||
|
Ok(embedded_midi::MidiMessage::ControlChange(channel, control, value))
|
||||||
|
if channel == Channel::new(0) =>
|
||||||
|
{
|
||||||
|
if control == embedded_midi::Control::new(20) {
|
||||||
|
ic_03 = value.into();
|
||||||
|
} else if control == embedded_midi::Control::new(21) {
|
||||||
|
let value: u8 = value.into();
|
||||||
|
ic_03 = 128_u8 + value;
|
||||||
|
} else if control == embedded_midi::Control::new(22) {
|
||||||
|
ic_10 = value.into();
|
||||||
|
} else if control == embedded_midi::Control::new(23) {
|
||||||
|
let value: u8 = value.into();
|
||||||
|
ic_10 = 128_u8 + value;
|
||||||
|
} else if control == embedded_midi::Control::new(24) {
|
||||||
|
ic_11 = value.into();
|
||||||
|
} else if control == embedded_midi::Control::new(25) {
|
||||||
|
let value: u8 = value.into();
|
||||||
|
ic_11 = 128_u8 + value;
|
||||||
|
}
|
||||||
|
bus.output(03, ic_03, &mut delay);
|
||||||
|
bus.output(10, ic_10, &mut delay);
|
||||||
|
bus.output(11, ic_11, &mut delay);
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
info!("unhandled midi message");
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => {}
|
||||||
|
Err(nb::Error::Other(hal::serial::Error::Framing)) => {
|
||||||
|
info!("Error::Framing");
|
||||||
|
}
|
||||||
|
Err(nb::Error::Other(hal::serial::Error::Noise)) => {
|
||||||
|
info!("Error::Noise");
|
||||||
|
}
|
||||||
|
Err(nb::Error::Other(hal::serial::Error::Overrun)) => {
|
||||||
|
info!("Error::Overrun");
|
||||||
|
}
|
||||||
|
Err(nb::Error::Other(hal::serial::Error::Parity)) => {
|
||||||
|
info!("Error::Parity");
|
||||||
|
}
|
||||||
|
Err(nb::Error::Other(_)) => {
|
||||||
|
info!("Error::Other");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if button_1_5.is_high().unwrap_or(false) {
|
||||||
|
info!("Button 1-5 pressed");
|
||||||
|
delay.delay_ms(500u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if button_6_10.is_high().unwrap_or(false) {
|
||||||
|
info!("Button 6-10 pressed");
|
||||||
|
delay.delay_ms(500u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bus {
|
||||||
|
fn output(&mut self, ic: u8, data: u8, delay: &mut Delay) {
|
||||||
|
match ic {
|
||||||
|
0 | 3 => self.select(1, delay),
|
||||||
|
1 | 10 => self.select(0, delay),
|
||||||
|
2 | 11 => self.select(5, delay),
|
||||||
|
|
||||||
|
_ => self.select(2, delay),
|
||||||
|
}
|
||||||
|
delay.delay_us(1u16);
|
||||||
|
self.select_en(true);
|
||||||
|
delay.delay_us(1u16);
|
||||||
|
self.write(data);
|
||||||
|
delay.delay_us(1u16);
|
||||||
|
self.select_en(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, data: u8) {
|
||||||
|
self.pb0.set_state((data & 0b_0000_0001 != 0).into()).ok();
|
||||||
|
self.pb1.set_state((data & 0b_0000_0010 != 0).into()).ok();
|
||||||
|
self.pb2.set_state((data & 0b_0000_0100 != 0).into()).ok();
|
||||||
|
self.pb3.set_state((data & 0b_0000_1000 != 0).into()).ok();
|
||||||
|
self.pb4.set_state((data & 0b_0001_0000 != 0).into()).ok();
|
||||||
|
self.pb5.set_state((data & 0b_0010_0000 != 0).into()).ok();
|
||||||
|
self.pb6.set_state((data & 0b_0100_0000 != 0).into()).ok();
|
||||||
|
self.pb7.set_state((data & 0b_1000_0000 != 0).into()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_en(&mut self, enable: bool) {
|
||||||
|
self.select_en.set_state(enable.into()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select(&mut self, line: u8, delay: &mut Delay) {
|
||||||
|
self.write(line);
|
||||||
|
delay.delay_us(1u16);
|
||||||
|
self.latch_en.set_high().ok();
|
||||||
|
delay.delay_us(1u16);
|
||||||
|
self.latch_en.set_low().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
177
mapping.json
Normal file
177
mapping.json
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
{
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"description": "00 00001 0010 00 [G] [G] [G] [2] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 253
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00000 1100 00 [P] [P] [P] [3] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 254,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00010 0000 01 [] [F] [F] [1] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 251,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00100 0000 10 [] [E] [E] [0] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC03": 247,
|
||||||
|
"IC10": 255,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "10 01000 0000 00 [] [D] [D] [9] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 239,
|
||||||
|
"IC10": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00000 1100 00 [P] [P] [P] [3] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 222,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00001 0010 00 [G] [G] [G] [2] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 221,
|
||||||
|
"IC10": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00010 0000 01 [] [F] [F] [1] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 219,
|
||||||
|
"IC10": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00100 0000 10 [] [E] [E] [0] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC03": 215,
|
||||||
|
"IC11": 0,
|
||||||
|
"IC10": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "10 01000 0000 00 [] [D] [D] [9] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 207,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00000 1100 00 [P] [P] [P] [3] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 190,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00001 0010 00 [G] [G] [G] [2] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC03": 189,
|
||||||
|
"IC11": 0,
|
||||||
|
"IC10": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00010 0000 01 [] [F] [F] [1] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 187,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00100 0000 10 [] [E] [E] [0] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 183,
|
||||||
|
"IC10": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "10 01000 0000 00 [] [D] [D] [9] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 175,
|
||||||
|
"IC10": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "01 10000 1101 00 [B, C, E, F, G, P] [A, B, C, P] [A, B, C, P] [3, 4, 5, 6, 7, 8] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC11": 0,
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 158
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "01 10001 0011 00 [B, C, E, F, G] [A, B, C, G] [A, B, C, G] [2, 4, 5, 6, 7, 8] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 255,
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 157
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "01 10001 0010 00 [B, C, E, F, G] [] [A, B, V, G] [2, 4, 5, 6, 7, 8] []",
|
||||||
|
"register_values": {
|
||||||
|
"IC10": 253,
|
||||||
|
"IC11": 0,
|
||||||
|
"IC03": 157
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00000 0000 00 [] [] [] [] [1, 6]",
|
||||||
|
"register_values": {
|
||||||
|
"IC11": 4,
|
||||||
|
"IC10": 255,
|
||||||
|
"IC03": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00000 0000 00 [] [] [] [] [2, 7]",
|
||||||
|
"register_values": {
|
||||||
|
"IC03": 255,
|
||||||
|
"IC10": 255,
|
||||||
|
"IC11": 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "00 00000 0000 00 [] [] [] [] [3, 7]",
|
||||||
|
"register_values": {
|
||||||
|
"IC03": 255,
|
||||||
|
"IC10": 255,
|
||||||
|
"IC11": 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"register_values": {
|
||||||
|
"IC03": 0,
|
||||||
|
"IC10": 0,
|
||||||
|
"IC11": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,4 +12,5 @@ pub struct Args {
|
|||||||
pub enum Command {
|
pub enum Command {
|
||||||
Run,
|
Run,
|
||||||
Collect,
|
Collect,
|
||||||
|
Mapper,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
mod args;
|
mod args;
|
||||||
mod collect;
|
mod collect;
|
||||||
|
mod mapper;
|
||||||
mod run;
|
mod run;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
|
||||||
@ -20,5 +21,8 @@ async fn main() {
|
|||||||
Some(args::Command::Collect) => {
|
Some(args::Command::Collect) => {
|
||||||
collect::collect().await;
|
collect::collect().await;
|
||||||
}
|
}
|
||||||
|
Some(args::Command::Mapper) => {
|
||||||
|
mapper::mapper().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
153
xtask/src/mapper.rs
Normal file
153
xtask/src/mapper.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::process::{Child, Command};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
type ProcessHandle = Arc<Mutex<Option<Child>>>;
|
||||||
|
|
||||||
|
pub async fn mapper() {
|
||||||
|
let qjackctl_handle: ProcessHandle = Arc::new(Mutex::new(None));
|
||||||
|
let firmware_handle: ProcessHandle = Arc::new(Mutex::new(None));
|
||||||
|
let mapper_handle: ProcessHandle = Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
|
let cancel_token = CancellationToken::new();
|
||||||
|
|
||||||
|
let cleanup_token = cancel_token.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
tokio::signal::ctrl_c()
|
||||||
|
.await
|
||||||
|
.expect("Failed to listen for ctrl+c");
|
||||||
|
println!("Received Ctrl+C, shutting down...");
|
||||||
|
cleanup_token.cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start processes
|
||||||
|
println!("Starting qjackctl...");
|
||||||
|
let qjackctl = spawn_qjackctl().await;
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||||
|
|
||||||
|
println!("Starting firmware debugger in new terminal...");
|
||||||
|
let firmware = spawn_firmware_in_terminal().await;
|
||||||
|
|
||||||
|
println!("Starting mapper...");
|
||||||
|
let mapper = spawn_mapper().await;
|
||||||
|
|
||||||
|
// Store handles for cleanup
|
||||||
|
*qjackctl_handle.lock().await = Some(qjackctl);
|
||||||
|
*firmware_handle.lock().await = Some(firmware);
|
||||||
|
*mapper_handle.lock().await = Some(mapper);
|
||||||
|
|
||||||
|
// Get references back for waiting (firmware not monitored since gnome-terminal exits immediately)
|
||||||
|
let mut qjackctl = qjackctl_handle.lock().await.take().unwrap();
|
||||||
|
let mut mapper = mapper_handle.lock().await.take().unwrap();
|
||||||
|
|
||||||
|
// Wait for any process to exit or cancellation
|
||||||
|
tokio::select! {
|
||||||
|
result = qjackctl.wait() => {
|
||||||
|
println!("qjackctl exited: {:?}", result);
|
||||||
|
kill_process(&mut mapper, "mapper").await;
|
||||||
|
cleanup_processes(vec![firmware_handle]).await;
|
||||||
|
stop_jack_daemon().await;
|
||||||
|
}
|
||||||
|
result = mapper.wait() => {
|
||||||
|
println!("mapper exited: {:?}", result);
|
||||||
|
kill_process(&mut qjackctl, "qjackctl").await;
|
||||||
|
cleanup_processes(vec![firmware_handle]).await;
|
||||||
|
stop_jack_daemon().await;
|
||||||
|
}
|
||||||
|
_ = cancel_token.cancelled() => {
|
||||||
|
println!("Shutdown requested");
|
||||||
|
kill_process(&mut mapper, "mapper").await;
|
||||||
|
kill_process(&mut qjackctl, "qjackctl").await;
|
||||||
|
cleanup_processes(vec![firmware_handle]).await;
|
||||||
|
stop_jack_daemon().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("All processes stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cleanup_processes(handles: Vec<ProcessHandle>) {
|
||||||
|
for handle in handles {
|
||||||
|
if let Some(mut child) = handle.lock().await.take() {
|
||||||
|
let _ = child.kill().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn stop_jack_daemon() {
|
||||||
|
println!("Stopping JACK daemon...");
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let (jack, args) = ("taskkill", vec!["/f", "/im", "jackd.exe"]);
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
let (jack, args) = ("pkill", vec!["-f", "jackd"]);
|
||||||
|
|
||||||
|
Command::new(jack)
|
||||||
|
.args(args)
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.expect("Failed to stop JACK daemon");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn spawn_qjackctl() -> Child {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let qjackctl = "C:\\Program Files\\JACK2\\qjackctl\\qjackctl.exe";
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
let qjackctl = "qjackctl";
|
||||||
|
|
||||||
|
Command::new(qjackctl)
|
||||||
|
.arg("/start-server")
|
||||||
|
.spawn()
|
||||||
|
.expect("Could not start qjackctl")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn spawn_firmware_in_terminal() -> Child {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
Command::new("C:\\Program Files\\Git\\git-bash.exe")
|
||||||
|
.args(["-c", "cd firmware && cargo run; read -p 'Press Enter to close...'"])
|
||||||
|
.spawn()
|
||||||
|
.expect("Could not start firmware debugger in git-bash")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
Command::new("gnome-terminal")
|
||||||
|
.args([
|
||||||
|
"--title=Firmware Debugger - FCB Looper",
|
||||||
|
"--",
|
||||||
|
"bash", "-c",
|
||||||
|
"cd firmware && echo 'Starting firmware debugger...' && cargo run; echo 'Firmware debugger exited. Press Enter to close...'; read"
|
||||||
|
])
|
||||||
|
.spawn()
|
||||||
|
.expect("Could not start firmware debugger in gnome-terminal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn spawn_mapper() -> Child {
|
||||||
|
Command::new("cargo")
|
||||||
|
.args(["run", "--package", "mapper"])
|
||||||
|
.stdin(std::process::Stdio::inherit())
|
||||||
|
.stdout(std::process::Stdio::inherit())
|
||||||
|
.stderr(std::process::Stdio::inherit())
|
||||||
|
.spawn()
|
||||||
|
.expect("Could not start mapper")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn kill_process(child: &mut Child, name: &str) {
|
||||||
|
match child.kill().await {
|
||||||
|
Ok(()) => println!("Stopped {}", name),
|
||||||
|
Err(e) => {
|
||||||
|
// Don't print error if process already exited
|
||||||
|
let error_msg = e.to_string().to_lowercase();
|
||||||
|
if !error_msg.contains("no such process") &&
|
||||||
|
!error_msg.contains("not found") &&
|
||||||
|
!error_msg.contains("invalid argument") {
|
||||||
|
eprintln!("Failed to stop {}: {}", name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -124,13 +124,8 @@ async fn spawn_qjackctl() -> Child {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
let qjackctl = "qjackctl";
|
let qjackctl = "qjackctl";
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
let qjackctl_arg = "/start-server";
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
let qjackctl_arg = "--start-server";
|
|
||||||
|
|
||||||
Command::new(qjackctl)
|
Command::new(qjackctl)
|
||||||
.arg(qjackctl_arg)
|
.arg("/start-server")
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Could not start qjackctl")
|
.expect("Could not start qjackctl")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user