feat: add queue and interval
This commit is contained in:
parent
73e824ee5c
commit
45f5f447c0
7 changed files with 126 additions and 18 deletions
|
|
@ -15,6 +15,8 @@ lazy_static = "1.4"
|
||||||
clap = { version = "4.1", features = ["derive"] }
|
clap = { version = "4.1", features = ["derive"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
enum-display-derive = "0.1"
|
enum-display-derive = "0.1"
|
||||||
|
queues = "1.1.0"
|
||||||
|
duration-str = { version = "0.5.1", features = ["serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
alsa = "0.7.0"
|
alsa = "0.7.0"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
@ -19,6 +21,8 @@ pub struct DeviceConfig {
|
||||||
pub connect: Option<Vec<RunConfig>>,
|
pub connect: Option<Vec<RunConfig>>,
|
||||||
pub disconnect: Option<Vec<RunConfig>>,
|
pub disconnect: Option<Vec<RunConfig>>,
|
||||||
pub events: Option<Vec<EventConfig>>,
|
pub events: Option<Vec<EventConfig>>,
|
||||||
|
pub queue_length: usize,
|
||||||
|
pub interval: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceConfig {
|
impl DeviceConfig {
|
||||||
|
|
@ -66,6 +70,8 @@ impl TryFrom<DeviceConfigSerializer> for DeviceConfig {
|
||||||
connect: util::map_opt_tryfrom(v.connect)?,
|
connect: util::map_opt_tryfrom(v.connect)?,
|
||||||
disconnect: util::map_opt_tryfrom(v.disconnect)?,
|
disconnect: util::map_opt_tryfrom(v.disconnect)?,
|
||||||
events: util::map_opt_tryfrom(v.events)?,
|
events: util::map_opt_tryfrom(v.events)?,
|
||||||
|
queue_length: v.queue_length.unwrap_or(256),
|
||||||
|
interval: v.interval.map(|x| x.unwrap()).unwrap_or_else(|| Duration::new(0, 0)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,26 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::{RunConfigSerializer,EventConfigSerializer};
|
use super::{RunConfigSerializer,EventConfigSerializer};
|
||||||
|
|
||||||
|
use duration_str::deserialize_duration;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum DurationWrapper {
|
||||||
|
#[serde(deserialize_with = "deserialize_duration")]
|
||||||
|
Some(Duration),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DurationWrapper {
|
||||||
|
pub fn unwrap(self) -> Duration
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
DurationWrapper::Some(v) => v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize,Debug,Clone)]
|
#[derive(Deserialize,Debug,Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct DeviceConfigSerializer {
|
pub struct DeviceConfigSerializer {
|
||||||
|
|
@ -12,4 +31,6 @@ pub struct DeviceConfigSerializer {
|
||||||
pub disconnect: Option<Vec<RunConfigSerializer>>,
|
pub disconnect: Option<Vec<RunConfigSerializer>>,
|
||||||
pub events: Option<Vec<EventConfigSerializer>>,
|
pub events: Option<Vec<EventConfigSerializer>>,
|
||||||
pub max_connections: Option<u32>,
|
pub max_connections: Option<u32>,
|
||||||
|
pub queue_length: Option<usize>,
|
||||||
|
pub interval: Option<DurationWrapper>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
src/event.rs
36
src/event.rs
|
|
@ -87,6 +87,16 @@ pub struct Event<'a> {
|
||||||
pub timestamp: Option<SystemTime>,
|
pub timestamp: Option<SystemTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Default)]
|
||||||
|
pub struct EventBuf {
|
||||||
|
pub r#type: EventType,
|
||||||
|
pub channel: u8,
|
||||||
|
pub id: u8,
|
||||||
|
pub value: u16,
|
||||||
|
pub raw: Vec<u8>,
|
||||||
|
pub timestamp: Option<SystemTime>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EventEnv {
|
pub struct EventEnv {
|
||||||
pub channel: String,
|
pub channel: String,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
@ -106,6 +116,32 @@ struct EventEnvRef<'a> {
|
||||||
pub value: &'a str,
|
pub value: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EventBuf {
|
||||||
|
pub fn as_event(&self) -> Event {
|
||||||
|
Event {
|
||||||
|
r#type: self.r#type,
|
||||||
|
channel: self.channel,
|
||||||
|
id: self.id,
|
||||||
|
value: self.value,
|
||||||
|
raw: &self.raw[..],
|
||||||
|
timestamp: self.timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<EventBuf> for Event<'_> {
|
||||||
|
fn into(self) -> EventBuf {
|
||||||
|
EventBuf {
|
||||||
|
r#type: self.r#type,
|
||||||
|
channel: self.channel,
|
||||||
|
id: self.id,
|
||||||
|
value: self.value,
|
||||||
|
raw: Vec::from(self.raw),
|
||||||
|
timestamp: self.timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u8> for EventType {
|
impl From<u8> for EventType {
|
||||||
fn from(v: u8) -> Self {
|
fn from(v: u8) -> Self {
|
||||||
if ! (0b1000..=0b1111).contains(&v) {
|
if ! (0b1000..=0b1111).contains(&v) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
pub mod alsa;
|
pub mod alsa;
|
||||||
|
|
||||||
|
use queues::{CircularBuffer, IsQueue};
|
||||||
|
|
||||||
use crate::config::device::Identifier;
|
use crate::config::device::Identifier;
|
||||||
use crate::midi::alsa::MidiInputAlsa;
|
use crate::midi::alsa::MidiInputAlsa;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
@ -8,10 +10,11 @@ extern crate libc;
|
||||||
|
|
||||||
use crate::config::DeviceConfig;
|
use crate::config::DeviceConfig;
|
||||||
use crate::eventmap::EventMap;
|
use crate::eventmap::EventMap;
|
||||||
use crate::event::Event;
|
use crate::event::{Event, EventBuf};
|
||||||
|
|
||||||
use std::time::SystemTime;
|
use std::thread;
|
||||||
use std::sync::mpsc;
|
use std::time::{SystemTime, Instant};
|
||||||
|
use std::sync::{mpsc, Mutex, Arc};
|
||||||
|
|
||||||
#[derive(Eq,PartialEq,Debug,Clone)]
|
#[derive(Eq,PartialEq,Debug,Clone)]
|
||||||
pub struct MidiPort<T>{
|
pub struct MidiPort<T>{
|
||||||
|
|
@ -161,9 +164,9 @@ impl MidiHandler {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, eventmap: &EventMap, (rs,ts): (mpsc::Receiver<bool>, mpsc::Sender<bool>)) -> Result<(), Error> {
|
pub fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, (rs,ts): (mpsc::Receiver<bool>, mpsc::Sender<bool>)) -> Result<(), Error> {
|
||||||
handler_fcall!{
|
handler_fcall!{
|
||||||
self, handle_inputport ,(eventmap,(rs,ts)),
|
self, handle_inputport, (conf, eventmap,(rs,ts)),
|
||||||
ALSA
|
ALSA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,19 +192,57 @@ where T: MidiInput<A>
|
||||||
input.ports_handle()
|
input.ports_handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_inputport<T>(input: &mut T, (eventmap, (rs, ts)): (&EventMap, (mpsc::Receiver<bool>, mpsc::Sender<bool>))) -> Result<(), Error>
|
fn handle_inputport<T>(input: &mut T, (conf, eventmap, (rs, ts)): (&DeviceConfig, &EventMap, (mpsc::Receiver<bool>, mpsc::Sender<bool>))) -> Result<(), Error>
|
||||||
where T: MidiInputHandler
|
where T: MidiInputHandler + Send
|
||||||
{
|
{
|
||||||
input.handle_input(|t,m,_| {
|
thread::scope(|s| -> Result<(), Error> {
|
||||||
let mut event = Event::from(m);
|
|
||||||
event.timestamp = t;
|
// parking signal for runner, true = stop
|
||||||
match eventmap.run_event(&event) {
|
let (pts,prs) = mpsc::channel::<bool>();
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => {
|
let evq = Arc::new(Mutex::new(CircularBuffer::<EventBuf>::new(conf.queue_length)));
|
||||||
eprintln!("ERROR: error on run: {}", e)
|
|
||||||
},
|
// background execution loop
|
||||||
}
|
let rq = evq.clone();
|
||||||
}, (rs,ts), ())?;
|
let exec_thread = s.spawn(move || -> Result<(),Error> {
|
||||||
|
loop {
|
||||||
|
if prs.recv()? {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
// nest the lock into a scope to release it before run
|
||||||
|
let (ev,start): (EventBuf,Instant) = {
|
||||||
|
let mut evq = rq.lock().unwrap();
|
||||||
|
if evq.size() > 0 {
|
||||||
|
(evq.remove().unwrap(), Instant::now())
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
eventmap.run_event(&ev.as_event()).unwrap_or_else(|e| eprintln!("ERROR: error on run: {}", e) );
|
||||||
|
let elapsed_time = start.elapsed();
|
||||||
|
if elapsed_time < conf.interval {
|
||||||
|
thread::sleep(conf.interval - elapsed_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
input.handle_input(|t,m,(evq,pts)| {
|
||||||
|
let mut event: EventBuf = Event::from(m).into();
|
||||||
|
event.timestamp = t;
|
||||||
|
let mut evq = evq.lock().unwrap();
|
||||||
|
evq.add(event).unwrap();
|
||||||
|
pts.send(false).expect("unexpected write error");
|
||||||
|
}, (rs,ts), (evq,pts.clone()))?;
|
||||||
|
|
||||||
|
pts.send(true).expect("unexpected write error");
|
||||||
|
let _ = exec_thread.join();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ fn try_connect_process<'a>(
|
||||||
let mm = m.as_ref().map(Arc::clone);
|
let mm = m.as_ref().map(Arc::clone);
|
||||||
let t = s.spawn( move || {
|
let t = s.spawn( move || {
|
||||||
dev.run_connect().unwrap();
|
dev.run_connect().unwrap();
|
||||||
c.run(eventmap, (srs,nsts)).unwrap();
|
c.run(dev, eventmap, (srs,nsts)).unwrap();
|
||||||
if let Some(m) = mm {
|
if let Some(m) = mm {
|
||||||
let mut m = m.lock().unwrap();
|
let mut m = m.lock().unwrap();
|
||||||
m.0 -= 1;
|
m.0 -= 1;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
devices:
|
devices:
|
||||||
- name: 'VMPK'
|
- name: 'VMPK'
|
||||||
max_connections: 1
|
max_connections: 1
|
||||||
|
queue_length: 3
|
||||||
|
interval: 100ms
|
||||||
connect:
|
connect:
|
||||||
- args: [ "sh", "-c", "echo Hello world!" ]
|
- args: [ "sh", "-c", "echo Hello world!" ]
|
||||||
disconnect:
|
disconnect:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue