Moved files

This commit is contained in:
2025-06-10 21:14:10 +02:00
parent b70026d562
commit 06600b8341
85 changed files with 194 additions and 37 deletions

1
simulator/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

157
simulator/Cargo.lock generated Normal file
View File

@@ -0,0 +1,157 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "jack"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f70ca699f44c04a32d419fc9ed699aaea89657fc09014bf3fa238e91d13041b9"
dependencies = [
"bitflags 2.9.1",
"jack-sys",
"lazy_static",
"libc",
"log",
]
[[package]]
name = "jack-sys"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6013b7619b95a22b576dfb43296faa4ecbe40abbdb97dfd22ead520775fc86ab"
dependencies = [
"bitflags 1.3.2",
"lazy_static",
"libc",
"libloading",
"log",
"pkg-config",
]
[[package]]
name = "kanal"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e3953adf0cd667798b396c2fa13552d6d9b3269d7dd1154c4c416442d1ff574"
dependencies = [
"futures-core",
"lock_api",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "simulator"
version = "0.1.0"
dependencies = [
"jack",
"kanal",
"wmidi",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wmidi"
version = "4.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e55f35b40ad0178422d06e9ba845041baf2faf04627b91fde928d0f6a21c712"

9
simulator/Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "simulator"
version = "0.1.0"
edition = "2021"
[dependencies]
jack = "0.13"
kanal = "0.1"
wmidi = "4"

184
simulator/src/main.rs Normal file
View File

@@ -0,0 +1,184 @@
use jack::RawMidi;
use kanal::{Receiver, Sender};
use std::io::{self, Write};
#[derive(Debug)]
enum MidiCommand {
ControlChange { cc: u8, value: u8 },
Quit,
}
struct MidiSender {
midi_out: jack::Port<jack::MidiOut>,
command_receiver: Receiver<MidiCommand>,
}
impl MidiSender {
fn new(
client: &jack::Client,
command_receiver: Receiver<MidiCommand>,
) -> Result<Self, Box<dyn std::error::Error>> {
let midi_out = client
.register_port("midi_out", jack::MidiOut::default())
.map_err(|e| format!("Could not create MIDI output port: {}", e))?;
Ok(Self {
midi_out,
command_receiver,
})
}
}
impl jack::ProcessHandler for MidiSender {
fn process(&mut self, _client: &jack::Client, ps: &jack::ProcessScope) -> jack::Control {
let mut midi_writer = self.midi_out.writer(ps);
// Process all pending commands
while let Ok(Some(command)) = self.command_receiver.try_recv() {
match command {
MidiCommand::ControlChange { cc, value } => {
let midi_data = [0xB0, cc, value]; // Control Change on channel 1
if let Err(e) = midi_writer.write(&RawMidi {
time: 0,
bytes: &midi_data,
}) {
eprintln!("Failed to send MIDI: {}", e);
}
}
MidiCommand::Quit => return jack::Control::Quit,
}
}
jack::Control::Continue
}
}
fn show_help() {
println!("\nFCB1010 MIDI simulator");
println!("=====================================");
println!("Button mappings:");
println!("1 - Button 1 (CC 20): Record/Arm / Tap Tempo");
println!("2 - Button 2 (CC 21): Play/Mute Track / Click Toggle");
println!("3 - Button 3 (CC 22): Solo Track");
println!("4 - Button 4 (CC 23): Overdub");
println!("5 - Button 5 (CC 24): Clear Track / Clear Column");
println!("6 - Button 6 (CC 25): Column 1 Select");
println!("7 - Button 7 (CC 26): Column 2 Select");
println!("8 - Button 8 (CC 27): Column 3 Select");
println!("9 - Button 9 (CC 28): Column 4 Select");
println!("0 - Button 10 (CC 29): Column 5 Select");
println!("u - UP (CC 30): Row Up / Mode Switch");
println!("d - DOWN (CC 31): Row Down / Mode Switch");
println!("e - Expression A (CC 1): Track/Click Volume (0-127)");
println!("m - Master Volume (CC 7): Master Volume (0-127)");
println!("h - Show this help");
println!("q - Quit\n");
}
fn get_expression_value(prompt: &str) -> Result<u8, Box<dyn std::error::Error>> {
print!("{}", prompt);
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let value: u8 = input
.trim()
.parse()
.map_err(|_| "Invalid number. Please enter 0-127")?;
if value > 127 {
return Err("Value must be 0-127".into());
}
Ok(value)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Starting FCB1010 MIDI Simulator...");
// Create JACK client
let (client, status) = jack::Client::new("FCB1010", jack::ClientOptions::NO_START_SERVER)?;
if !status.is_empty() {
eprintln!("JACK client status: {:?}", status);
}
// Create command channel
let (command_sender, command_receiver): (Sender<MidiCommand>, Receiver<MidiCommand>) =
kanal::bounded(32);
// Create MIDI sender
let midi_sender = MidiSender::new(&client, command_receiver)?;
// Activate client
let _active_client = client.activate_async((), midi_sender)?;
show_help();
// Main input loop
loop {
print!("Command (h for help): ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim().to_lowercase();
if input.is_empty() {
continue;
}
let first_char = input.chars().next().unwrap();
let command = match first_char {
'1' => Some(MidiCommand::ControlChange { cc: 20, value: 127 }),
'2' => Some(MidiCommand::ControlChange { cc: 21, value: 127 }),
'3' => Some(MidiCommand::ControlChange { cc: 22, value: 127 }),
'4' => Some(MidiCommand::ControlChange { cc: 23, value: 127 }),
'5' => Some(MidiCommand::ControlChange { cc: 24, value: 127 }),
'6' => Some(MidiCommand::ControlChange { cc: 25, value: 127 }),
'7' => Some(MidiCommand::ControlChange { cc: 26, value: 127 }),
'8' => Some(MidiCommand::ControlChange { cc: 27, value: 127 }),
'9' => Some(MidiCommand::ControlChange { cc: 28, value: 127 }),
'0' => Some(MidiCommand::ControlChange { cc: 29, value: 127 }),
'u' => Some(MidiCommand::ControlChange { cc: 30, value: 127 }),
'd' => Some(MidiCommand::ControlChange { cc: 31, value: 127 }),
'e' => match get_expression_value("Expression A value (0-127): ") {
Ok(value) => Some(MidiCommand::ControlChange { cc: 1, value }),
Err(e) => {
eprintln!("Error: {}", e);
None
}
},
'm' => match get_expression_value("Master volume value (0-127): ") {
Ok(value) => Some(MidiCommand::ControlChange { cc: 7, value }),
Err(e) => {
eprintln!("Error: {}", e);
None
}
},
'h' => {
show_help();
None
}
'q' => {
println!("Exiting...");
command_sender.send(MidiCommand::Quit)?;
break;
}
_ => {
println!("Unknown command '{}'. Press 'h' for help.", first_char);
None
}
};
if let Some(cmd) = command {
if let MidiCommand::ControlChange { cc, value } = &cmd {
println!("Sending CC {} with value {}", cc, value);
}
command_sender.send(cmd)?;
}
}
Ok(())
}