First working DI
This commit is contained in:
parent
ca8ee979ea
commit
809e0c5b10
6 changed files with 109 additions and 48 deletions
|
@ -1,9 +1,14 @@
|
|||
use crate::{config::NanoKeys, interaction_server::InteractionServer};
|
||||
use crossbeam_channel::Sender;
|
||||
use midir::{Ignore, MidiInput};
|
||||
use regex::Regex;
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::time;
|
||||
use midir::MidiInput;
|
||||
use shaku::{module, HasComponent, Interface};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::midi_client::MidiPort;
|
||||
use super::midi_service::MidiService;
|
||||
use super::midi_service_impl::{MidiServiceImpl, MidiServiceImplParameters};
|
||||
use super::midir_client_impl::{MidirClientImpl, MidirPortImpl};
|
||||
|
||||
const CLIENT_NAME: &str = "PicoKontroller";
|
||||
const PORT_NAME: &str = "PicoController Input";
|
||||
|
@ -16,11 +21,10 @@ pub struct MidiClient {
|
|||
pub type KeyEvent = (NanoKeys, KeyState);
|
||||
pub type KeyState = u8;
|
||||
|
||||
impl InteractionServer for MidiClient {
|
||||
fn start(self) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
self.exec();
|
||||
})
|
||||
module! {
|
||||
MyModule<MP: MidiPort + Interface> {
|
||||
components = [MidiServiceImpl<MidirPortImpl>, MidirClientImpl],
|
||||
providers = [],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,34 +36,20 @@ impl MidiClient {
|
|||
}
|
||||
}
|
||||
|
||||
fn exec(self) {
|
||||
let port_filter = Regex::new(&self.device_name).expect("Creating RegEx failed");
|
||||
pub fn start(self) -> () {
|
||||
let module: MyModule<MidirPortImpl> = MyModule::builder()
|
||||
.with_component_parameters::<MidiServiceImpl<MidirPortImpl>>(
|
||||
MidiServiceImplParameters::<MidirPortImpl> {
|
||||
device_name: "nanoKONTROL2".to_string(),
|
||||
out_channel: self.out_channel,
|
||||
phantom: PhantomData,
|
||||
},
|
||||
)
|
||||
.build();
|
||||
|
||||
let mut midi_in = MidiInput::new(CLIENT_NAME).expect("Creating MIDI device failed");
|
||||
midi_in.ignore(Ignore::None);
|
||||
|
||||
let in_ports = midi_in.ports();
|
||||
let in_port = in_ports
|
||||
.iter()
|
||||
.find(|p| port_filter.is_match(&midi_in.port_name(p).unwrap()))
|
||||
.expect("Couldn't find a port matching the supplied RegEx");
|
||||
|
||||
// _conn_in needs to be a named parameter, because it needs to be kept alive until the end of the scope
|
||||
let _conn_in = midi_in.connect(
|
||||
in_port,
|
||||
PORT_NAME,
|
||||
move |_, message, _| {
|
||||
let key = NanoKeys::try_from(message[1]).unwrap();
|
||||
let state = message[2];
|
||||
let _ = self.out_channel.send((key, state));
|
||||
},
|
||||
(),
|
||||
);
|
||||
loop {
|
||||
// sleep forever, callback functions happen on separate thread
|
||||
// couldn't reuse this thread, since _conn_in needs to be alive in this scope
|
||||
std::thread::sleep(time::Duration::from_secs(u64::MAX));
|
||||
}
|
||||
let service: Arc<dyn MidiService<MidirPortImpl>> = module.resolve();
|
||||
let handle = service.start();
|
||||
let _ = handle.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use shaku::{self, Interface};
|
||||
|
||||
pub type MidiConnectionCallback = Box<dyn Fn(&[u8]) + Send + 'static>;
|
||||
pub type MidiConnectionCallback = Box<dyn Fn(&[u8]) + Send>;
|
||||
|
||||
#[cfg_attr(test, mockall::automock)]
|
||||
pub trait MidiClient<MP: MidiPort>: Interface {
|
||||
pub trait MidiClient<MP: MidiPort + Interface>: Interface {
|
||||
fn ports(&self) -> Vec<MP>;
|
||||
fn connect(&self, port: MP, port_name: &str, callback: MidiConnectionCallback) -> ();
|
||||
fn connect(&self, port: &MP, port_name: &str, callback: MidiConnectionCallback) -> ();
|
||||
}
|
||||
|
||||
#[cfg_attr(test, mockall::automock)]
|
||||
pub trait MidiPort : Interface{
|
||||
pub trait MidiPort: Interface {
|
||||
fn name(&self) -> String;
|
||||
}
|
||||
|
|
11
src/clients/midi_service.rs
Normal file
11
src/clients/midi_service.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use std::{sync::Arc, thread::JoinHandle};
|
||||
|
||||
use shaku::Interface;
|
||||
|
||||
use super::midi_client::MidiPort;
|
||||
|
||||
pub trait MidiService<MP: MidiPort + Interface>: Interface {
|
||||
fn exec(self: Arc<Self>);
|
||||
fn list_ports(&self) -> ();
|
||||
fn start(self: Arc<Self>) -> JoinHandle<()>;
|
||||
}
|
59
src/clients/midi_service_impl.rs
Normal file
59
src/clients/midi_service_impl.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use super::midi_client::{MidiClient, MidiPort};
|
||||
use super::midi_service::MidiService;
|
||||
use crate::config::NanoKeys;
|
||||
use crossbeam_channel::Sender;
|
||||
use regex::Regex;
|
||||
use shaku::{self, Component, Interface};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
const CLIENT_NAME: &str = "PicoKontroller";
|
||||
const PORT_NAME: &str = "PicoController Input";
|
||||
|
||||
#[derive(Component)]
|
||||
#[shaku(interface = MidiService<MP>)]
|
||||
pub struct MidiServiceImpl<MP: MidiPort + Interface> {
|
||||
#[shaku(default)]
|
||||
device_name: String,
|
||||
#[shaku(default = unimplemented!())]
|
||||
out_channel: Sender<KeyEvent>,
|
||||
#[shaku(inject)]
|
||||
midi_client: Arc<dyn MidiClient<MP>>,
|
||||
#[shaku(default = PhantomData)]
|
||||
phantom: PhantomData<MP>,
|
||||
}
|
||||
|
||||
pub type KeyEvent = (NanoKeys, KeyState);
|
||||
pub type KeyState = u8;
|
||||
|
||||
impl<MP: MidiPort + Interface> MidiService<MP> for MidiServiceImpl<MP> {
|
||||
fn exec(self: Arc<Self>) {
|
||||
let port_filter = Regex::new(&self.device_name).expect("Creating RegEx failed");
|
||||
let ports = self.midi_client.ports();
|
||||
|
||||
let port = ports
|
||||
.iter()
|
||||
.find(|p| port_filter.is_match(&p.name()))
|
||||
.expect("Couldn't find a port matching the supplied RegEx");
|
||||
|
||||
let channel = self.out_channel.clone();
|
||||
|
||||
let callback = Box::new(move |message: &[u8]| {
|
||||
let key = NanoKeys::try_from(message[1]).unwrap();
|
||||
let state = message[2];
|
||||
let _ = channel.send((key, state));
|
||||
});
|
||||
|
||||
self.midi_client.connect(&port, PORT_NAME, callback);
|
||||
}
|
||||
|
||||
fn list_ports(&self) -> () {}
|
||||
|
||||
fn start(self: Arc<Self>) -> JoinHandle<()> {
|
||||
let foo = self.clone();
|
||||
thread::spawn(move || {
|
||||
foo.exec();
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use midir::{MidiInput, MidiInputPort, Ignore};
|
||||
use midir::{Ignore, MidiInput, MidiInputPort};
|
||||
use shaku::{self, Component};
|
||||
|
||||
use super::midi_client::{MidiClient, MidiConnectionCallback, MidiPort};
|
||||
|
@ -13,6 +13,7 @@ fn create_input(client_name: &str) -> MidiInput {
|
|||
#[derive(Component)]
|
||||
#[shaku(interface = MidiClient<MidirPortImpl>)]
|
||||
pub struct MidirClientImpl {
|
||||
#[shaku(default)]
|
||||
client_name: String,
|
||||
}
|
||||
|
||||
|
@ -25,9 +26,10 @@ impl MidiClient<MidirPortImpl> for MidirClientImpl {
|
|||
.map(|p| MidirPortImpl::new(p.clone(), self.client_name.clone())) // Cast to Box<dyn MidiPort>
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn connect(
|
||||
&self,
|
||||
port: MidirPortImpl,
|
||||
port: &MidirPortImpl,
|
||||
port_name: &str,
|
||||
callback: MidiConnectionCallback,
|
||||
) -> () {
|
||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -1,19 +1,18 @@
|
|||
mod config;
|
||||
mod router;
|
||||
mod interaction_server;
|
||||
mod router;
|
||||
|
||||
mod controllers {
|
||||
pub mod exec;
|
||||
pub mod mpris;
|
||||
pub mod pulse;
|
||||
pub mod exec;
|
||||
}
|
||||
mod clients;
|
||||
|
||||
use clap::Parser;
|
||||
use clients::midi::MidiClient;
|
||||
use crossbeam_channel::unbounded;
|
||||
use directories::ProjectDirs;
|
||||
use interaction_server::InteractionServer;
|
||||
use clients::midi::MidiClient;
|
||||
use std::{path::PathBuf, process::exit, sync::Arc};
|
||||
|
||||
#[derive(Parser)]
|
||||
|
@ -47,6 +46,6 @@ fn main() {
|
|||
|
||||
router::run(receiver, Arc::clone(&arc_cfg));
|
||||
|
||||
let server = midi.start();
|
||||
let _ = server.join();
|
||||
midi.start();
|
||||
println!("fooo");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue