Skip to content

Commit

Permalink
stateful translation wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ahihi committed Jul 7, 2024
1 parent b605644 commit 64a2937
Show file tree
Hide file tree
Showing 5 changed files with 459 additions and 220 deletions.
2 changes: 2 additions & 0 deletions src/autocrap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod config;
pub mod interpreter;
250 changes: 250 additions & 0 deletions src/autocrap/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
use std::{iter, net::{SocketAddrV4}};

use rosc::{OscMessage, OscType};
use serde::{Serialize, Deserialize};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Numbering {
Single {
ctrl_in_sequence: Option<Vec<u8>>,
ctrl_in: Option<u8>,
ctrl_out: Option<u8>,
midi: Option<u8>,
},
Range {
ctrl_in: Option<(u8, u8)>,
ctrl_out: Option<(u8, u8)>,
midi: Option<(u8, u8)>,
}
}

impl Numbering {
pub fn num_alternatives(&self) -> Option<u8> {
match self {
Numbering::Single { .. } => Some(1),
Numbering::Range {ctrl_in, ctrl_out, midi} => {
let ranges = iter::once(ctrl_in)
.chain(iter::once(ctrl_out))
.chain(iter::once(midi))
.filter_map(|r| *r);

let mut num_opt: Option<u8> = None;
for (lo, hi) in ranges {
let range_num = hi - lo + 1;
let Some(num) = num_opt else {
num_opt = Some(range_num);
continue;
};

if range_num != num {
return None;
}
}

num_opt
}
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum CtrlNum {
Single(u8),
Range(u8, u8),
Sequence(Vec<u8>)
}

impl CtrlNum {
pub fn match_num(&self, num: u8) -> Option<u8> {
match *self {
CtrlNum::Single(n) if num == n =>
Some(0),
CtrlNum::Range(lo, hi) if lo <= num && num <= hi =>
Some(num - lo),
// TODO: Sequence
_ =>
None
}
}

pub fn range_size(&self) -> u8 {
match *self {
CtrlNum::Single(_) => 1,
CtrlNum::Range(lo, hi) => hi - lo + 1,
_ => unimplemented!()
}
}

pub fn index_to_num(&self, i: u8) -> Option<u8> {
match *self {
CtrlNum::Single(num) if i == 0 =>
Some(num),
CtrlNum::Range(lo, hi) if i <= hi-lo =>
Some(lo + i),
CtrlNum::Sequence(_) =>
unimplemented!(),
_ => None
}
}
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum CtrlKind {
OnOff,
EightBit,
Relative,
}

impl CtrlKind {
pub fn ctrl_to_osc(&self, val: u8) -> Vec<OscType> {
match self {
CtrlKind::OnOff =>
vec![OscType::Float(if val == 0x7f { 1.0 } else { 0.0 })],
CtrlKind::Relative =>
vec![OscType::Float(if val < 0x40 { val as f32 } else { val as f32 - 128.0 })],
_ => unimplemented!()
}
}

pub fn osc_to_ctrl(&self, args: &[OscType]) -> Option<u8> {
if args.len() < 1 {
return None;
}

let OscType::Float(val) = args[0] else {
return None;
};

match self {
CtrlKind::OnOff =>
Some(float_to_7bit(val)),
CtrlKind::Relative =>
Some(float_to_7bit(val)),
_ => unimplemented!()
}
}
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum MidiKind {
Cc,
CoarseFine,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Mapping {
pub name: String,
pub numbering: Numbering,
pub ctrl_kind: CtrlKind,
pub midi_kind: MidiKind,
}

impl Mapping {
pub fn expand_iter(&self) -> impl Iterator<Item = Mapping> {
let mut mappings = vec![];
match self.numbering {
Numbering::Single { .. } => mappings.push(self.clone()),
Numbering::Range {ctrl_in, ctrl_out, midi} => {
println!("{:?}", self.numbering);
let num = self.numbering.num_alternatives().unwrap();
for i in 0..num {
mappings.push(Mapping {
name: self.name.replace("{i}", &i.to_string()),
numbering: Numbering::Single {
ctrl_in_sequence: None,
ctrl_in: ctrl_in.map(|(lo, hi)| lo + i),
ctrl_out: ctrl_out.map(|(lo, hi)| lo + i),
midi: midi.map(|(lo, hi)| lo + i),
},
ctrl_kind: self.ctrl_kind,
midi_kind: self.midi_kind,
});
}
}
};
mappings.into_iter()
}

pub fn osc_addr(&self, i: u8) -> String {
format!("/{}", self.name.replace("{i}", &i.to_string()))
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
pub vendor_id: u16,
pub product_id: u16,
pub in_endpoint: u8,
pub out_endpoint: u8,
pub host_addr: SocketAddrV4,
pub osc_out_addr: SocketAddrV4,
pub osc_in_addr: SocketAddrV4,
pub mappings: Vec<Mapping>
}

impl Config {
// pub fn match_ctrl(&self, num: u8, val: u8) -> Option<CtrlMatchData> {
// for mapping in self.mappings.iter() {
// let Some(ctrl_in_num) = mapping.ctrl_in_num else {
// continue;
// };

// let Some(i) = ctrl_in_num.match_num(num) else {
// continue;
// };

// return Some(CtrlMatchData {
// osc_addr: mapping.osc_addr(i),
// osc_args: mapping.ctrl_kind.ctrl_to_osc(val)
// })
// }

// None
// }

// pub fn match_osc(&self, msg: &OscMessage) -> Option<OscMatchData> {
// for mapping in self.mappings.iter() {
// let Some(ctrl_out_num) = mapping.ctrl_out_num else {
// continue;
// };

// for i in 0..ctrl_out_num.range_size() {
// let addr = mapping.osc_addr(i);

// if addr != msg.addr {
// continue;
// }

// let Some(num) = ctrl_out_num.index_to_num(i) else {
// continue;
// };

// let Some(val) = mapping.ctrl_kind.osc_to_ctrl(&msg.args) else {
// continue;
// };

// return Some(OscMatchData {
// ctrl_data: vec![num, val]
// });
// }
// }

// None
// }
}

#[derive(Clone, Debug)]
pub struct CtrlMatchData {
pub osc_addr: String,
pub osc_args: Vec<OscType>,
}

#[derive(Clone, Debug)]
pub struct OscMatchData {
pub ctrl_data: Vec<u8>,
}


fn float_to_7bit(val: f32) -> u8 {
(val.max(0.0).min(1.0) * 127.0).round() as u8
}
133 changes: 133 additions & 0 deletions src/autocrap/interpreter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use std::{
sync::{Arc, RwLock}
};

use rosc::{OscMessage, OscType};

use super::config::{Config, CtrlKind, Numbering};

pub struct Interpreter {
ctrls: Vec<Box<dyn CtrlLogic>>,
}

impl Interpreter {
pub fn new(config: &Config) -> Interpreter {
let mut ctrls: Vec<Box<dyn CtrlLogic>> = vec![];
for mapping in config.mappings.iter() {
for m in mapping.expand_iter() {
let Numbering::Single { ctrl_in, ctrl_out, midi, ref ctrl_in_sequence } = m.numbering
else {
unreachable!();
};

match m.ctrl_kind {
CtrlKind::OnOff => {
ctrls.push(Box::new(OnOffLogic {
ctrl_in_num: ctrl_in,
ctrl_out_num: ctrl_out,
osc_addr: format!("/{}", m.name),
state: Arc::new(RwLock::new(false))
}));
},
CtrlKind::EightBit => {
if let Some(ctrl_in_sequence) = ctrl_in_sequence {
ctrls.push(Box::new(EightBitLogic {
ctrl_in_first: ctrl_in_sequence[0],
ctrl_in_num: ctrl_in_sequence[1],
osc_addr: format!("/{}", m.name),
state: Arc::new(RwLock::new([0x00,0x00]))
}));
}
},
_ => {
println!("{:?}", m);
}
}
}
}

Interpreter {
ctrls
}
}

pub fn handle_ctrl(&self, num: u8, val: u8) -> Option<CtrlResponse> {
for ctrl in &self.ctrls {
let Some(response) = ctrl.handle_ctrl(num, val) else {
continue;
};

return Some(response);
}

None
}
}

pub trait CtrlLogic {
fn handle_ctrl(&self, num: u8, val: u8) -> Option<CtrlResponse>;
}

pub struct OnOffLogic {
ctrl_in_num: Option<u8>,
ctrl_out_num: Option<u8>,
osc_addr: String,
state: Arc<RwLock<bool>>
}

impl CtrlLogic for OnOffLogic {
fn handle_ctrl(&self, num: u8, val: u8) -> Option<CtrlResponse> {
let Some(ctrl_in_num) = self.ctrl_in_num else {
return None;
};

if num != ctrl_in_num {
return None;
}

let mut state = self.state.write().unwrap();
*state = if val != 0 { true } else { false };
let ctrl_out = self.ctrl_out_num.map(|num| vec![num, if *state { 0x7f} else { 0x00 }]);
Some(CtrlResponse {
osc: Some((self.osc_addr.clone(), vec![OscType::Float(if *state { 1.0 } else { 0.0 })])),
ctrl: ctrl_out
})
}
}

pub struct EightBitLogic {
ctrl_in_first: u8,
ctrl_in_num: u8,
osc_addr: String,
state: Arc<RwLock<[u8;2]>>
}

impl CtrlLogic for EightBitLogic {
fn handle_ctrl(&self, num: u8, val: u8) -> Option<CtrlResponse> {
if num == self.ctrl_in_first {
let mut state = self.state.write().unwrap();
state[0] = val;
return Some(CtrlResponse {
osc: None,
ctrl: None
});
}

if num == self.ctrl_in_num {
let mut state = self.state.write().unwrap();
state[1] = val;
let val8 = state[0] << 1 | (if state[1] != 0x00 { 1 } else { 0 });
return Some(CtrlResponse {
osc: Some((self.osc_addr.clone(), vec![OscType::Float(val8 as f32 / 255.0)])),
ctrl: None
})
}

None
}
}

pub struct CtrlResponse {
pub osc: Option<(String, Vec<OscType>)>,
pub ctrl: Option<Vec<u8>>
}
Loading

0 comments on commit 64a2937

Please sign in to comment.