chore: apply format
This commit is contained in:
parent
819ef4db28
commit
0a6e237049
28 changed files with 705 additions and 510 deletions
|
|
@ -1,13 +1,13 @@
|
||||||
log_devices: true
|
log_devices: true
|
||||||
devices:
|
devices:
|
||||||
- name: 'VMPK'
|
- name: "VMPK"
|
||||||
max_connections: 1
|
max_connections: 1
|
||||||
queue_length: 3
|
queue_length: 3
|
||||||
interval: 100ms
|
interval: 100ms
|
||||||
connect:
|
connect:
|
||||||
- args: [ "sh", "-c", "echo Hello world!" ]
|
- args: ["sh", "-c", "echo Hello world!"]
|
||||||
disconnect:
|
disconnect:
|
||||||
- args: [ "sh", "-c", "echo Bye!" ]
|
- args: ["sh", "-c", "echo Bye!"]
|
||||||
events:
|
events:
|
||||||
- type: ProgramChange
|
- type: ProgramChange
|
||||||
run:
|
run:
|
||||||
|
|
@ -38,28 +38,28 @@ devices:
|
||||||
- cmd: "echo [$channel] PitchBend $value $raw $toto"
|
- cmd: "echo [$channel] PitchBend $value $raw $toto"
|
||||||
envconf:
|
envconf:
|
||||||
timestamp: toto
|
timestamp: toto
|
||||||
- name: 'VMPK'
|
- name: "VMPK"
|
||||||
log_events: true
|
log_events: true
|
||||||
max_connections: 1
|
max_connections: 1
|
||||||
connect:
|
connect:
|
||||||
- args: [ "sh", "-c", "echo Hello world! 2" ]
|
- args: ["sh", "-c", "echo Hello world! 2"]
|
||||||
disconnect:
|
disconnect:
|
||||||
- args: [ "sh", "-c", "echo Bye! 2" ]
|
- args: ["sh", "-c", "echo Bye! 2"]
|
||||||
events:
|
events:
|
||||||
- type: NoteOff
|
- type: NoteOff
|
||||||
id: 25-30
|
id: 25-30
|
||||||
run:
|
run:
|
||||||
- args: [ "sh", "-c", "echo 2 [$channel] NoteOff $id" ]
|
- args: ["sh", "-c", "echo 2 [$channel] NoteOff $id"]
|
||||||
- type: NoteOn
|
- type: NoteOn
|
||||||
channel: 0
|
channel: 0
|
||||||
remap: -1
|
remap: -1
|
||||||
run:
|
run:
|
||||||
- args: [ "sh", "-c", "echo 2 [$channel] NoteOn $id $value" ]
|
- args: ["sh", "-c", "echo 2 [$channel] NoteOn $id $value"]
|
||||||
- type: PitchBend
|
- type: PitchBend
|
||||||
remap: 0-100
|
remap: 0-100
|
||||||
float: true
|
float: true
|
||||||
value: 0-65535
|
value: 0-65535
|
||||||
run:
|
run:
|
||||||
- args: [ "sh", "-c", "echo [$channel] PitchBend $value $raw $toto" ]
|
- args: ["sh", "-c", "echo [$channel] PitchBend $value $raw $toto"]
|
||||||
envconf:
|
envconf:
|
||||||
timestamp: toto
|
timestamp: toto
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,3 @@ pub struct Cli {
|
||||||
#[clap(long, short, action)]
|
#[clap(long, short, action)]
|
||||||
pub list: bool,
|
pub list: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use super::serializer::DeviceConfigSerializer;
|
||||||
|
use super::{EventConfig, RunConfig};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use super::{RunConfig,EventConfig};
|
|
||||||
use super::serializer::DeviceConfigSerializer;
|
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Identifier {
|
pub enum Identifier {
|
||||||
All,
|
All,
|
||||||
Name(String),
|
Name(String),
|
||||||
|
|
@ -14,7 +14,7 @@ pub enum Identifier {
|
||||||
Addr(String),
|
Addr(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DeviceConfig {
|
pub struct DeviceConfig {
|
||||||
pub identifier: Identifier,
|
pub identifier: Identifier,
|
||||||
pub max_connections: Option<u32>,
|
pub max_connections: Option<u32>,
|
||||||
|
|
@ -29,12 +29,16 @@ pub struct DeviceConfig {
|
||||||
impl DeviceConfig {
|
impl DeviceConfig {
|
||||||
fn run_internal<'a, T>(&self, v: Option<T>) -> Result<Vec<std::process::ExitStatus>, Error>
|
fn run_internal<'a, T>(&self, v: Option<T>) -> Result<Vec<std::process::ExitStatus>, Error>
|
||||||
where
|
where
|
||||||
T: IntoIterator<Item = &'a RunConfig>
|
T: IntoIterator<Item = &'a RunConfig>,
|
||||||
{
|
{
|
||||||
let mut r = Vec::new();
|
let mut r = Vec::new();
|
||||||
if let Some(ev) = v {
|
if let Some(ev) = v {
|
||||||
for e in ev {
|
for e in ev {
|
||||||
if let Some(v) = e.run(Event::new().make_env(None, false)?.to_map(e.envconf.as_ref()))? {
|
if let Some(v) = e.run(
|
||||||
|
Event::new()
|
||||||
|
.make_env(None, false)?
|
||||||
|
.to_map(e.envconf.as_ref()),
|
||||||
|
)? {
|
||||||
r.push(v);
|
r.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,13 +61,22 @@ impl TryFrom<DeviceConfigSerializer> for DeviceConfig {
|
||||||
Ok(DeviceConfig {
|
Ok(DeviceConfig {
|
||||||
identifier: {
|
identifier: {
|
||||||
match (v.name, v.regex, v.addr) {
|
match (v.name, v.regex, v.addr) {
|
||||||
(Some(_), Some(_), _ ) => return Err(Error::IncompatibleArgs("name","regex")),
|
(Some(_), Some(_), Some(_)) => {
|
||||||
(Some(_), None , Some(_)) => return Err(Error::IncompatibleArgs("name","addr")),
|
return Err(Error::IncompatibleArgs("name", "regex"))
|
||||||
(None , Some(_), Some(_)) => return Err(Error::IncompatibleArgs("regex","addr")),
|
}
|
||||||
(Some(n), None, None ) => Identifier::Name(n),
|
(Some(_), Some(_), None) => {
|
||||||
(None, Some(r), None ) => Identifier::Regex(regex::Regex::new(&r)?),
|
return Err(Error::IncompatibleArgs("name", "regex"))
|
||||||
(None, None , Some(a)) => Identifier::Addr(a),
|
}
|
||||||
(None, None, None ) => Identifier::All,
|
(Some(_), None, Some(_)) => {
|
||||||
|
return Err(Error::IncompatibleArgs("name", "addr"))
|
||||||
|
}
|
||||||
|
(None, Some(_), Some(_)) => {
|
||||||
|
return Err(Error::IncompatibleArgs("regex", "addr"))
|
||||||
|
}
|
||||||
|
(Some(n), None, None) => Identifier::Name(n),
|
||||||
|
(None, Some(r), None) => Identifier::Regex(regex::Regex::new(&r)?),
|
||||||
|
(None, None, Some(a)) => Identifier::Addr(a),
|
||||||
|
(None, None, None) => Identifier::All,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
max_connections: v.max_connections,
|
max_connections: v.max_connections,
|
||||||
|
|
@ -71,7 +84,10 @@ impl TryFrom<DeviceConfigSerializer> for DeviceConfig {
|
||||||
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),
|
queue_length: v.queue_length.unwrap_or(256),
|
||||||
interval: v.interval.map(|x| x.unwrap()).unwrap_or_else(|| Duration::new(0, 0)),
|
interval: v
|
||||||
|
.interval
|
||||||
|
.map(|x| x.unwrap())
|
||||||
|
.unwrap_or_else(|| Duration::new(0, 0)),
|
||||||
log: v.log_events.unwrap_or(false),
|
log: v.log_events.unwrap_or(false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::RunConfig;
|
use super::RunConfig;
|
||||||
use crate::event::{Event,EventType};
|
use crate::event::{Event, EventType};
|
||||||
use crate::util::{self, SmartSet, Range, Remapper};
|
use crate::util::{self, Range, Remapper, SmartSet};
|
||||||
|
|
||||||
use super::serializer::EventConfigSerializer;
|
use super::serializer::EventConfigSerializer;
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EventConfig {
|
pub struct EventConfig {
|
||||||
pub run: Vec<RunConfig>,
|
pub run: Vec<RunConfig>,
|
||||||
pub r#type: EventType,
|
pub r#type: EventType,
|
||||||
|
|
@ -66,14 +66,23 @@ impl TryFrom<EventConfigSerializer> for EventConfig {
|
||||||
true => v.id.unwrap_or_else(|| ID_DEFAULT_MAP.clone()),
|
true => v.id.unwrap_or_else(|| ID_DEFAULT_MAP.clone()),
|
||||||
false => NULL_DEFAULT_MAP.clone(),
|
false => NULL_DEFAULT_MAP.clone(),
|
||||||
},
|
},
|
||||||
remap: v.remap.map(|x| Remapper::new(Range::new(v.r#type.min_value() as f64, v.r#type.max_value() as f64), x )),
|
remap: v.remap.map(|x| {
|
||||||
|
Remapper::new(
|
||||||
|
Range::new(v.r#type.min_value() as f64, v.r#type.max_value() as f64),
|
||||||
|
x,
|
||||||
|
)
|
||||||
|
}),
|
||||||
float: v.float.unwrap_or(false),
|
float: v.float.unwrap_or(false),
|
||||||
value: v.value,
|
value: v.value,
|
||||||
};
|
};
|
||||||
if let Some(remap) = &r.remap {
|
if let Some(remap) = &r.remap {
|
||||||
let range = remap.src();
|
let range = remap.src();
|
||||||
if range.start() < i64::MIN as f64 { return Err(Self::Error::RemapTooLow(range.start())) }
|
if range.start() < i64::MIN as f64 {
|
||||||
if range.end() > i64::MAX as f64 { return Err(Self::Error::RemapTooBig(range.end())) }
|
return Err(Self::Error::RemapTooLow(range.start()));
|
||||||
|
}
|
||||||
|
if range.end() > i64::MAX as f64 {
|
||||||
|
return Err(Self::Error::RemapTooBig(range.end()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod event;
|
|
||||||
pub mod device;
|
pub mod device;
|
||||||
|
pub mod event;
|
||||||
pub mod run;
|
pub mod run;
|
||||||
pub mod serializer;
|
pub mod serializer;
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub use event::EventConfig;
|
||||||
pub use run::RunConfig;
|
pub use run::RunConfig;
|
||||||
pub type EventEnvMap = serializer::EventEnvSerializer;
|
pub type EventEnvMap = serializer::EventEnvSerializer;
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub log: bool,
|
pub log: bool,
|
||||||
pub driver: Option<crate::midi::MidiDriver>,
|
pub driver: Option<crate::midi::MidiDriver>,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::process::{Command, ExitStatus};
|
||||||
use super::serializer::RunConfigSerializer;
|
use super::serializer::RunConfigSerializer;
|
||||||
use super::EventEnvMap;
|
use super::EventEnvMap;
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RunConfig {
|
pub struct RunConfig {
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
pub envconf: Option<EventEnvMap>,
|
pub envconf: Option<EventEnvMap>,
|
||||||
|
|
@ -19,9 +19,7 @@ impl RunConfig {
|
||||||
}
|
}
|
||||||
c.envs(env);
|
c.envs(env);
|
||||||
if self.detach {
|
if self.detach {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || c.status());
|
||||||
c.status()
|
|
||||||
});
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
c.status().map(|v| Some(v))
|
c.status().map(|v| Some(v))
|
||||||
|
|
@ -34,19 +32,17 @@ impl TryFrom<RunConfigSerializer> for RunConfig {
|
||||||
fn try_from(v: RunConfigSerializer) -> Result<Self, Self::Error> {
|
fn try_from(v: RunConfigSerializer) -> Result<Self, Self::Error> {
|
||||||
let args = if v.args.is_some() {
|
let args = if v.args.is_some() {
|
||||||
v.args.unwrap()
|
v.args.unwrap()
|
||||||
}
|
} else if v.cmd.is_some() {
|
||||||
else if v.cmd.is_some() {
|
|
||||||
crate::run::cross_shell(v.cmd.as_ref().unwrap())
|
crate::run::cross_shell(v.cmd.as_ref().unwrap())
|
||||||
}
|
} else {
|
||||||
else {
|
return Err(crate::Error::from(
|
||||||
return Err(crate::Error::from(crate::error::ConfigError::RunMissingArgs));
|
crate::error::ConfigError::RunMissingArgs,
|
||||||
|
));
|
||||||
};
|
};
|
||||||
Ok(
|
Ok(RunConfig {
|
||||||
RunConfig {
|
|
||||||
args,
|
args,
|
||||||
envconf: v.envconf,
|
envconf: v.envconf,
|
||||||
detach: v.detach.unwrap_or(false),
|
detach: v.detach.unwrap_or(false),
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::{RunConfigSerializer,EventConfigSerializer};
|
use super::{EventConfigSerializer, RunConfigSerializer};
|
||||||
|
|
||||||
use duration_str::deserialize_duration;
|
use duration_str::deserialize_duration;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug,Clone,Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum DurationWrapper {
|
pub enum DurationWrapper {
|
||||||
#[serde(deserialize_with = "deserialize_duration")]
|
#[serde(deserialize_with = "deserialize_duration")]
|
||||||
|
|
@ -13,15 +13,14 @@ pub enum DurationWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DurationWrapper {
|
impl DurationWrapper {
|
||||||
pub fn unwrap(self) -> Duration
|
pub fn unwrap(self) -> Duration {
|
||||||
{
|
|
||||||
match self {
|
match self {
|
||||||
DurationWrapper::Some(v) => v,
|
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 {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use super::RunConfigSerializer;
|
use super::RunConfigSerializer;
|
||||||
use crate::event::EventType;
|
use crate::event::EventType;
|
||||||
use crate::util::{SmartSet,Range};
|
use crate::util::{Range, SmartSet};
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize,Debug,Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct EventConfigSerializer {
|
pub struct EventConfigSerializer {
|
||||||
pub run: Vec<RunConfigSerializer>,
|
pub run: Vec<RunConfigSerializer>,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use serde::{Serialize,Deserialize};
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|
||||||
#[derive(Serialize,Deserialize,Debug,Clone)]
|
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct EventEnvSerializer {
|
pub struct EventEnvSerializer {
|
||||||
pub channel: Option<String>,
|
pub channel: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
pub mod event;
|
|
||||||
pub mod device;
|
pub mod device;
|
||||||
pub mod run;
|
pub mod event;
|
||||||
pub mod eventenv;
|
pub mod eventenv;
|
||||||
|
pub mod run;
|
||||||
|
|
||||||
pub use device::DeviceConfigSerializer;
|
pub use device::DeviceConfigSerializer;
|
||||||
pub use event::EventConfigSerializer;
|
pub use event::EventConfigSerializer;
|
||||||
pub use run::RunConfigSerializer;
|
|
||||||
pub use eventenv::EventEnvSerializer;
|
pub use eventenv::EventEnvSerializer;
|
||||||
|
pub use run::RunConfigSerializer;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize,Clone,Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ConfigSerializer {
|
pub struct ConfigSerializer {
|
||||||
pub log_devices: Option<bool>,
|
pub log_devices: Option<bool>,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::EventEnvSerializer;
|
use super::EventEnvSerializer;
|
||||||
|
|
||||||
use serde::{Serialize,Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize,Deserialize,Debug,Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct RunConfigSerializer {
|
pub struct RunConfigSerializer {
|
||||||
pub args: Option<Vec<String>>,
|
pub args: Option<Vec<String>>,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
pub const CLIENT_NAME: &str = "rmidimap";
|
pub const CLIENT_NAME: &str = "rmidimap";
|
||||||
pub const CLIENT_NAME_HANDLER: &str = "rmidimap-handler";
|
pub const CLIENT_NAME_HANDLER: &str = "rmidimap-handler";
|
||||||
pub const CLIENT_NAME_EVENT: &str = "rmidimap-event-watcher";
|
pub const CLIENT_NAME_EVENT: &str = "rmidimap-event-watcher";
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::midi::backend::alsa::AlsaError;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error,Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
|
|
@ -44,7 +44,7 @@ pub enum Error {
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error,Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
#[error("run config is missing execution configuration, either \"args\" or \"cmd\" has to be specified")]
|
#[error("run config is missing execution configuration, either \"args\" or \"cmd\" has to be specified")]
|
||||||
RunMissingArgs,
|
RunMissingArgs,
|
||||||
|
|
|
||||||
153
src/event.rs
153
src/event.rs
|
|
@ -1,11 +1,11 @@
|
||||||
|
use std::fmt::{Display, Write};
|
||||||
use std::{collections::HashMap, time::SystemTime};
|
use std::{collections::HashMap, time::SystemTime};
|
||||||
use std::fmt::{Write,Display};
|
|
||||||
|
|
||||||
use crate::config::EventEnvMap;
|
use crate::config::EventEnvMap;
|
||||||
use crate::util::Remapper;
|
use crate::util::Remapper;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
use serde::{Serialize,Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
|
@ -21,11 +21,12 @@ lazy_static! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn event_to_key(r#type: EventType, channel: u8, id: u8) -> u32 {
|
pub fn event_to_key(r#type: EventType, channel: u8, id: u8) -> u32 {
|
||||||
(r#type as u32)*256*256 + (channel as u32)*256 + (id as u32)
|
(r#type as u32) * 256 * 256 + (channel as u32) * 256 + (id as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Serialize,Deserialize,Debug,Copy,Clone,Default,Display)]
|
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default, enum_display_derive::Display)]
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
#[default]
|
#[default]
|
||||||
Unknown = 0b0000,
|
Unknown = 0b0000,
|
||||||
|
|
@ -41,42 +42,36 @@ pub enum EventType {
|
||||||
|
|
||||||
impl EventType {
|
impl EventType {
|
||||||
pub fn has_id(&self) -> bool {
|
pub fn has_id(&self) -> bool {
|
||||||
!matches!(self, EventType::Unknown | EventType::ChannelPressure | EventType::PitchBend | EventType::System )
|
!matches!(
|
||||||
|
self,
|
||||||
|
EventType::Unknown
|
||||||
|
| EventType::ChannelPressure
|
||||||
|
| EventType::PitchBend
|
||||||
|
| EventType::System
|
||||||
|
)
|
||||||
}
|
}
|
||||||
pub fn has_channel(&self) -> bool {
|
pub fn has_channel(&self) -> bool {
|
||||||
!matches!(self, EventType::Unknown | EventType::System )
|
!matches!(self, EventType::Unknown | EventType::System)
|
||||||
}
|
}
|
||||||
pub fn min_value(&self) -> i32 {
|
pub fn min_value(&self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
EventType::NoteOff |
|
EventType::NoteOff | EventType::NoteOn | EventType::Controller => 0,
|
||||||
EventType::NoteOn |
|
EventType::PolyphonicKeyPressure | EventType::ChannelPressure => 127,
|
||||||
EventType::Controller
|
EventType::PitchBend => 0,
|
||||||
=> 0,
|
|
||||||
EventType::PolyphonicKeyPressure |
|
|
||||||
EventType::ChannelPressure
|
|
||||||
=> 127,
|
|
||||||
EventType::PitchBend
|
|
||||||
=> 0,
|
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn max_value(&self) -> i32 {
|
pub fn max_value(&self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
EventType::NoteOff |
|
EventType::NoteOff | EventType::NoteOn | EventType::Controller => 127,
|
||||||
EventType::NoteOn |
|
EventType::PolyphonicKeyPressure | EventType::ChannelPressure => 127,
|
||||||
EventType::Controller
|
EventType::PitchBend => 32767,
|
||||||
=> 127,
|
|
||||||
EventType::PolyphonicKeyPressure |
|
|
||||||
EventType::ChannelPressure
|
|
||||||
=> 127,
|
|
||||||
EventType::PitchBend
|
|
||||||
=> 32767,
|
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Event<'a> {
|
pub struct Event<'a> {
|
||||||
pub r#type: EventType,
|
pub r#type: EventType,
|
||||||
pub channel: u8,
|
pub channel: u8,
|
||||||
|
|
@ -86,7 +81,7 @@ pub struct Event<'a> {
|
||||||
pub timestamp: Option<SystemTime>,
|
pub timestamp: Option<SystemTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone,Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct EventBuf {
|
pub struct EventBuf {
|
||||||
pub r#type: EventType,
|
pub r#type: EventType,
|
||||||
pub channel: u8,
|
pub channel: u8,
|
||||||
|
|
@ -105,7 +100,7 @@ pub struct EventEnv {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct EventEnvRef<'a> {
|
struct EventEnvRef<'a> {
|
||||||
pub channel: &'a str,
|
pub channel: &'a str,
|
||||||
pub id: &'a str,
|
pub id: &'a str,
|
||||||
|
|
@ -115,11 +110,17 @@ struct EventEnvRef<'a> {
|
||||||
pub value: &'a str,
|
pub value: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> std::fmt::Display for Event<'a> {
|
impl<'a> std::fmt::Display for Event<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{{ \"type\": \"{}\", \"channel\": {}, \"id\": {}, \"value\": {}, \"raw\": \"{}\" }}",
|
write!(
|
||||||
self.r#type, self.channel, self.id, self.value, bytes_to_strhex(self.raw, " "))
|
f,
|
||||||
|
"{{ \"type\": \"{}\", \"channel\": {}, \"id\": {}, \"value\": {}, \"raw\": \"{}\" }}",
|
||||||
|
self.r#type,
|
||||||
|
self.channel,
|
||||||
|
self.id,
|
||||||
|
self.value,
|
||||||
|
bytes_to_strhex(self.raw, " ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,11 +158,10 @@ impl Into<EventBuf> for Event<'_> {
|
||||||
|
|
||||||
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) {
|
||||||
// not in defined space: unknown
|
// not in defined space: unknown
|
||||||
EventType::Unknown
|
EventType::Unknown
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// safe since all valid cases are defined
|
// safe since all valid cases are defined
|
||||||
unsafe { std::mem::transmute(v) }
|
unsafe { std::mem::transmute(v) }
|
||||||
}
|
}
|
||||||
|
|
@ -195,19 +195,23 @@ impl<'a> Event<'a> {
|
||||||
event_to_key(self.r#type, self.channel, self.id)
|
event_to_key(self.r#type, self.channel, self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_env(&self, remap: Option<&Remapper<f64>>, float: bool) -> Result<EventEnv, Error>
|
pub fn make_env(&self, remap: Option<&Remapper<f64>>, float: bool) -> Result<EventEnv, Error> {
|
||||||
{
|
|
||||||
Ok(EventEnv {
|
Ok(EventEnv {
|
||||||
channel: self.channel.to_string(),
|
channel: self.channel.to_string(),
|
||||||
id: self.id.to_string(),
|
id: self.id.to_string(),
|
||||||
rawvalue: self.value.to_string(),
|
rawvalue: self.value.to_string(),
|
||||||
raw: bytes_to_strhex(self.raw, " "),
|
raw: bytes_to_strhex(self.raw, " "),
|
||||||
timestamp: self.timestamp.unwrap_or(SystemTime::now()).duration_since(SystemTime::UNIX_EPOCH)?.as_secs_f64().to_string(),
|
timestamp: self
|
||||||
value: match (remap,float) {
|
.timestamp
|
||||||
(Some(r),true) => r.remap(self.value as f64).to_string(),
|
.unwrap_or(SystemTime::now())
|
||||||
(Some(r),false) => r.remap_to::<i64>(self.value as f64).unwrap().to_string(),
|
.duration_since(SystemTime::UNIX_EPOCH)?
|
||||||
|
.as_secs_f64()
|
||||||
|
.to_string(),
|
||||||
|
value: match (remap, float) {
|
||||||
|
(Some(r), true) => r.remap(self.value as f64).to_string(),
|
||||||
|
(Some(r), false) => r.remap_to::<i64>(self.value as f64).unwrap().to_string(),
|
||||||
_ => self.value.to_string(),
|
_ => self.value.to_string(),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -218,20 +222,25 @@ impl<'a> From<&'a [u8]> for Event<'a> {
|
||||||
eprintln!("warning: empty signal");
|
eprintln!("warning: empty signal");
|
||||||
return Default::default();
|
return Default::default();
|
||||||
}
|
}
|
||||||
let event_type = EventType::from(v[0]/16);
|
let event_type = EventType::from(v[0] / 16);
|
||||||
let channel = if event_type.has_channel() { v[0]%16 } else { 0 };
|
let channel = if event_type.has_channel() {
|
||||||
|
v[0] % 16
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
let (id, value) = match event_type {
|
let (id, value) = match event_type {
|
||||||
EventType::PitchBend => {
|
EventType::PitchBend => (0, (v[2] as u16) * 256 + (v[1] as u16)),
|
||||||
(0, (v[2] as u16)*256 + (v[1] as u16) )
|
|
||||||
},
|
|
||||||
EventType::Unknown => {
|
EventType::Unknown => {
|
||||||
eprintln!("warning: unknown signal type: {}", v[0]);
|
eprintln!("warning: unknown signal type: {}", v[0]);
|
||||||
(0,0)
|
(0, 0)
|
||||||
}
|
}
|
||||||
EventType::System => (0,0),
|
EventType::System => (0, 0),
|
||||||
EventType::ChannelPressure => (0,v[1] as u16),
|
EventType::ChannelPressure => (0, v[1] as u16),
|
||||||
EventType::ProgramChange => (v[1],0),
|
EventType::ProgramChange => (v[1], 0),
|
||||||
EventType::NoteOn | EventType::NoteOff | EventType::PolyphonicKeyPressure | EventType::Controller => (v[1],(v[2] as u16)),
|
EventType::NoteOn
|
||||||
|
| EventType::NoteOff
|
||||||
|
| EventType::PolyphonicKeyPressure
|
||||||
|
| EventType::Controller => (v[1], (v[2] as u16)),
|
||||||
};
|
};
|
||||||
Event {
|
Event {
|
||||||
r#type: event_type,
|
r#type: event_type,
|
||||||
|
|
@ -245,19 +254,41 @@ impl<'a> From<&'a [u8]> for Event<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEnv {
|
impl EventEnv {
|
||||||
pub fn to_map(self, m: Option<&EventEnvMap>) -> HashMap<&str,String> {
|
pub fn to_map(self, m: Option<&EventEnvMap>) -> HashMap<&str, String> {
|
||||||
let mut r = HashMap::new();
|
let mut r = HashMap::new();
|
||||||
let keys: EventEnvRef = match m {
|
let keys: EventEnvRef = match m {
|
||||||
Some(v) => {
|
Some(v) => EventEnvRef {
|
||||||
EventEnvRef {
|
channel: v
|
||||||
channel: v.channel.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.channel),
|
.channel
|
||||||
id: v.id.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.id),
|
.as_ref()
|
||||||
raw: v.raw.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.raw),
|
.map(|x| &x[..])
|
||||||
rawvalue: v.rawvalue.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.rawvalue),
|
.unwrap_or(EVENT_ENV_DEFAULT.channel),
|
||||||
timestamp: v.timestamp.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.timestamp),
|
id: v
|
||||||
value: v.value.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.value),
|
.id
|
||||||
}
|
.as_ref()
|
||||||
}
|
.map(|x| &x[..])
|
||||||
|
.unwrap_or(EVENT_ENV_DEFAULT.id),
|
||||||
|
raw: v
|
||||||
|
.raw
|
||||||
|
.as_ref()
|
||||||
|
.map(|x| &x[..])
|
||||||
|
.unwrap_or(EVENT_ENV_DEFAULT.raw),
|
||||||
|
rawvalue: v
|
||||||
|
.rawvalue
|
||||||
|
.as_ref()
|
||||||
|
.map(|x| &x[..])
|
||||||
|
.unwrap_or(EVENT_ENV_DEFAULT.rawvalue),
|
||||||
|
timestamp: v
|
||||||
|
.timestamp
|
||||||
|
.as_ref()
|
||||||
|
.map(|x| &x[..])
|
||||||
|
.unwrap_or(EVENT_ENV_DEFAULT.timestamp),
|
||||||
|
value: v
|
||||||
|
.value
|
||||||
|
.as_ref()
|
||||||
|
.map(|x| &x[..])
|
||||||
|
.unwrap_or(EVENT_ENV_DEFAULT.value),
|
||||||
|
},
|
||||||
_ => EVENT_ENV_DEFAULT.clone(),
|
_ => EVENT_ENV_DEFAULT.clone(),
|
||||||
};
|
};
|
||||||
r.insert(keys.channel, self.channel);
|
r.insert(keys.channel, self.channel);
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::config::{EventConfig,DeviceConfig};
|
use crate::config::{DeviceConfig, EventConfig};
|
||||||
use crate::event::{EventType,Event};
|
use crate::event::{Event, EventType};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Debug,Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct EventMap<'a> {
|
pub struct EventMap<'a> {
|
||||||
pub map: HashMap<u32, Vec<&'a EventConfig>>,
|
pub map: HashMap<u32, Vec<&'a EventConfig>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event_to_key(r#type: EventType, channel: u8, id: u8) -> u32 {
|
fn event_to_key(r#type: EventType, channel: u8, id: u8) -> u32 {
|
||||||
(r#type as u32)*256*256 + (channel as u32)*256 + (id as u32)
|
(r#type as u32) * 256 * 256 + (channel as u32) * 256 + (id as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_events(events: &[EventConfig]) -> usize {
|
pub fn count_events(events: &[EventConfig]) -> usize {
|
||||||
events.iter().map(|x| {
|
events
|
||||||
|
.iter()
|
||||||
|
.map(|x| {
|
||||||
let nchannel = x.channel.len();
|
let nchannel = x.channel.len();
|
||||||
let nid = x.id.len();
|
let nid = x.id.len();
|
||||||
nchannel * nid
|
nchannel * nid
|
||||||
}).sum()
|
})
|
||||||
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EventMap<'a> {
|
impl<'a> EventMap<'a> {
|
||||||
|
|
@ -29,8 +32,7 @@ impl<'a> EventMap<'a> {
|
||||||
let key = event_to_key(event.r#type, channel, id);
|
let key = event_to_key(event.r#type, channel, id);
|
||||||
if let Some(v) = self.map.get_mut(&key) {
|
if let Some(v) = self.map.get_mut(&key) {
|
||||||
v.push(event);
|
v.push(event);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.map.insert(key, Vec::from([event]));
|
self.map.insert(key, Vec::from([event]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,13 +40,15 @@ impl<'a> EventMap<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_event(&self, event: &Event) -> Result<(), Error > {
|
pub fn run_event(&self, event: &Event) -> Result<(), Error> {
|
||||||
let key = event_to_key(event.r#type, event.channel, event.id);
|
let key = event_to_key(event.r#type, event.channel, event.id);
|
||||||
if let Some(v) = self.map.get(&key) {
|
if let Some(v) = self.map.get(&key) {
|
||||||
for ev in v {
|
for ev in v {
|
||||||
if ev.match_value(event) {
|
if ev.match_value(event) {
|
||||||
for r in &ev.run {
|
for r in &ev.run {
|
||||||
let env = event.make_env(ev.remap.as_ref(), ev.float )?.to_map(r.envconf.as_ref());
|
let env = event
|
||||||
|
.make_env(ev.remap.as_ref(), ev.float)?
|
||||||
|
.to_map(r.envconf.as_ref());
|
||||||
r.run(env)?;
|
r.run(env)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,12 +62,13 @@ impl<'a> From<&'a [EventConfig]> for EventMap<'a> {
|
||||||
fn from(events: &'a [EventConfig]) -> Self {
|
fn from(events: &'a [EventConfig]) -> Self {
|
||||||
// init hashmap with size for optimizing
|
// init hashmap with size for optimizing
|
||||||
let size = count_events(events);
|
let size = count_events(events);
|
||||||
let mut ret = EventMap { map: HashMap::with_capacity(size) };
|
let mut ret = EventMap {
|
||||||
|
map: HashMap::with_capacity(size),
|
||||||
|
};
|
||||||
// insert references
|
// insert references
|
||||||
ret.add_events(events);
|
ret.add_events(events);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a DeviceConfig> for EventMap<'a> {
|
impl<'a> From<&'a DeviceConfig> for EventMap<'a> {
|
||||||
|
|
@ -71,12 +76,13 @@ impl<'a> From<&'a DeviceConfig> for EventMap<'a> {
|
||||||
// init hashmap with size for optimizing
|
// init hashmap with size for optimizing
|
||||||
let size = count_events(device.events.as_ref().map(|x| &x[..]).unwrap_or(&[]));
|
let size = count_events(device.events.as_ref().map(|x| &x[..]).unwrap_or(&[]));
|
||||||
//let size = events.iter().map(|x| x.channels.len()*x.ids.len() ).sum();
|
//let size = events.iter().map(|x| x.channels.len()*x.ids.len() ).sum();
|
||||||
let mut ret = EventMap { map: HashMap::with_capacity(size) };
|
let mut ret = EventMap {
|
||||||
|
map: HashMap::with_capacity(size),
|
||||||
|
};
|
||||||
// insert references
|
// insert references
|
||||||
if let Some(x) = device.events.as_ref() {
|
if let Some(x) = device.events.as_ref() {
|
||||||
ret.add_events(x);
|
ret.add_events(x);
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
src/main.rs
31
src/main.rs
|
|
@ -1,15 +1,12 @@
|
||||||
#[macro_use]
|
pub mod cli;
|
||||||
extern crate enum_display_derive;
|
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod run;
|
pub mod constant;
|
||||||
|
pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod eventmap;
|
pub mod eventmap;
|
||||||
pub mod midi;
|
pub mod midi;
|
||||||
|
pub mod run;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod cli;
|
|
||||||
pub mod error;
|
|
||||||
pub mod constant;
|
|
||||||
|
|
||||||
type Error = error::Error;
|
type Error = error::Error;
|
||||||
|
|
||||||
|
|
@ -17,8 +14,8 @@ use std::path::Path;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use config::Config;
|
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
|
use config::Config;
|
||||||
use midi::MidiHandler;
|
use midi::MidiHandler;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -26,24 +23,18 @@ fn main() {
|
||||||
|
|
||||||
if c.list {
|
if c.list {
|
||||||
let mut handler = err_handle(MidiHandler::new(constant::CLIENT_NAME));
|
let mut handler = err_handle(MidiHandler::new(constant::CLIENT_NAME));
|
||||||
err_handle(
|
err_handle(handler.builder_handler(run::ListDevicesBuilder, ()));
|
||||||
handler.builder_handler(run::ListDevicesBuilder, ())
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let map_file = err_handle(
|
let map_file = err_handle(c.map_file.ok_or(Error::NoArgument));
|
||||||
c.map_file.ok_or(Error::NoArgument)
|
|
||||||
);
|
|
||||||
loop {
|
loop {
|
||||||
err_handle(
|
err_handle(run_file(&map_file));
|
||||||
run_file(&map_file)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn err_handle<T,E>(r: Result<T, E>) -> T
|
fn err_handle<T, E>(r: Result<T, E>) -> T
|
||||||
where
|
where
|
||||||
E: std::fmt::Display
|
E: std::fmt::Display,
|
||||||
{
|
{
|
||||||
match r {
|
match r {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
|
|
@ -56,7 +47,7 @@ where
|
||||||
|
|
||||||
fn run_file(filepath: &Path) -> Result<(), Error> {
|
fn run_file(filepath: &Path) -> Result<(), Error> {
|
||||||
println!("Load file {}", filepath.to_str().unwrap_or("<unknown>"));
|
println!("Load file {}", filepath.to_str().unwrap_or("<unknown>"));
|
||||||
let dat = std::fs::read( filepath )?;
|
let dat = std::fs::read(filepath)?;
|
||||||
let conf = Config::try_from(&dat[..])?;
|
let conf = Config::try_from(&dat[..])?;
|
||||||
let mut handler = match conf.driver {
|
let mut handler = match conf.driver {
|
||||||
Some(v) => MidiHandler::new_with_driver(constant::CLIENT_NAME, v),
|
Some(v) => MidiHandler::new_with_driver(constant::CLIENT_NAME, v),
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,24 @@
|
||||||
extern crate libc;
|
|
||||||
extern crate alsa;
|
extern crate alsa;
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{mem, thread};
|
|
||||||
use std::ffi::{CString, CStr};
|
|
||||||
use std::time::SystemTime;
|
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use std::{mem, thread};
|
||||||
|
|
||||||
use crate::midi::{MidiInput,MidiPort,PortFilter};
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::midi::{MidiInput, MidiPort, PortFilter};
|
||||||
use crate::util::InternalTryFrom;
|
use crate::util::InternalTryFrom;
|
||||||
|
|
||||||
use alsa::{Seq, Direction};
|
use alsa::seq::{
|
||||||
use alsa::seq::{ClientIter, PortIter, MidiEvent, PortInfo, PortSubscribe, Addr, QueueTempo, EventType, PortCap, PortType};
|
Addr, ClientIter, EventType, MidiEvent, PortCap, PortInfo, PortIter, PortSubscribe, PortType,
|
||||||
|
QueueTempo,
|
||||||
|
};
|
||||||
|
use alsa::{Direction, Seq};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug,Clone,PartialEq,Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct DeviceAddr(Addr);
|
pub struct DeviceAddr(Addr);
|
||||||
|
|
||||||
const ANNOUNCE_ADDR: &str = "System:Announce";
|
const ANNOUNCE_ADDR: &str = "System:Announce";
|
||||||
|
|
@ -39,15 +42,16 @@ impl FromStr for DeviceAddr {
|
||||||
// todo!()
|
// todo!()
|
||||||
// let mut osep = ;
|
// let mut osep = ;
|
||||||
if let Some(sep) = s.find(':') {
|
if let Some(sep) = s.find(':') {
|
||||||
let (p1,p2) = (&s[..sep], &s[sep+1..] );
|
let (p1, p2) = (&s[..sep], &s[sep + 1..]);
|
||||||
Ok(DeviceAddr(
|
Ok(DeviceAddr(Addr {
|
||||||
Addr {
|
client: p1
|
||||||
client: p1.parse().map_err(|_| AlsaError::AddrParse(s.to_string()))?,
|
.parse()
|
||||||
port: p2.parse().map_err(|_| AlsaError::AddrParse(s.to_string()))?,
|
.map_err(|_| AlsaError::AddrParse(s.to_string()))?,
|
||||||
}
|
port: p2
|
||||||
))
|
.parse()
|
||||||
}
|
.map_err(|_| AlsaError::AddrParse(s.to_string()))?,
|
||||||
else {
|
}))
|
||||||
|
} else {
|
||||||
Err(AlsaError::AddrParse(s.to_string()))
|
Err(AlsaError::AddrParse(s.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +76,8 @@ impl DeviceAddr {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ports(s: &Seq, capability: PortCap) -> Vec<PortInfo> {
|
pub fn get_ports(s: &Seq, capability: PortCap) -> Vec<PortInfo> {
|
||||||
ClientIter::new(s).flat_map(|c| PortIter::new(s, c.get_client()))
|
ClientIter::new(s)
|
||||||
|
.flat_map(|c| PortIter::new(s, c.get_client()))
|
||||||
.filter(|p| p.get_capability().contains(capability))
|
.filter(|p| p.get_capability().contains(capability))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +88,7 @@ mod helpers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error,Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum AlsaError {
|
pub enum AlsaError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ALSA(#[from] alsa::Error),
|
ALSA(#[from] alsa::Error),
|
||||||
|
|
@ -99,7 +104,7 @@ pub struct MidiInputAlsa {
|
||||||
subscription: Option<PortSubscribe>,
|
subscription: Option<PortSubscribe>,
|
||||||
connect_addr: Option<Addr>,
|
connect_addr: Option<Addr>,
|
||||||
start_time: Option<SystemTime>,
|
start_time: Option<SystemTime>,
|
||||||
stop_trigger: [i32;2],
|
stop_trigger: [i32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for MidiInputAlsa {
|
impl Drop for MidiInputAlsa {
|
||||||
|
|
@ -119,10 +124,11 @@ impl MidiInputAlsa {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn init_queue(&mut self) -> Result<i32, alsa::Error> {
|
fn init_queue(&mut self) -> Result<i32, alsa::Error> {
|
||||||
// Create the input queue
|
// Create the input queue
|
||||||
let queue_id = self.seq.alloc_named_queue(unsafe { CStr::from_bytes_with_nul_unchecked(b"midir queue\0") })?;
|
let queue_id = self
|
||||||
|
.seq
|
||||||
|
.alloc_named_queue(unsafe { CStr::from_bytes_with_nul_unchecked(b"midir queue\0") })?;
|
||||||
// Set arbitrary tempo (mm=100) and resolution (240)
|
// Set arbitrary tempo (mm=100) and resolution (240)
|
||||||
let qtempo = QueueTempo::empty()?;
|
let qtempo = QueueTempo::empty()?;
|
||||||
qtempo.set_tempo(600_000);
|
qtempo.set_tempo(600_000);
|
||||||
|
|
@ -130,7 +136,6 @@ impl MidiInputAlsa {
|
||||||
self.seq.set_queue_tempo(queue_id, &qtempo)?;
|
self.seq.set_queue_tempo(queue_id, &qtempo)?;
|
||||||
let _ = self.seq.drain_output();
|
let _ = self.seq.drain_output();
|
||||||
|
|
||||||
|
|
||||||
Ok(queue_id)
|
Ok(queue_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,18 +164,20 @@ impl MidiInputAlsa {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_internal(&mut self)
|
fn close_internal(&mut self) {
|
||||||
{
|
|
||||||
if let Some(ref subscription) = self.subscription {
|
if let Some(ref subscription) = self.subscription {
|
||||||
let _ = self.seq.unsubscribe_port(subscription.get_sender(), subscription.get_dest());
|
let _ = self
|
||||||
|
.seq
|
||||||
|
.unsubscribe_port(subscription.get_sender(), subscription.get_dest());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop and free the input queue
|
// Stop and free the input queue
|
||||||
let _ = self.seq.control_queue(self.queue_id, EventType::Stop, 0, None);
|
let _ = self
|
||||||
|
.seq
|
||||||
|
.control_queue(self.queue_id, EventType::Stop, 0, None);
|
||||||
let _ = self.seq.drain_output();
|
let _ = self.seq.drain_output();
|
||||||
let _ = self.seq.free_queue(self.queue_id);
|
let _ = self.seq.free_queue(self.queue_id);
|
||||||
|
|
||||||
|
|
||||||
for fd in self.stop_trigger {
|
for fd in self.stop_trigger {
|
||||||
if fd >= 0 {
|
if fd >= 0 {
|
||||||
unsafe { self::libc::close(fd) };
|
unsafe { self::libc::close(fd) };
|
||||||
|
|
@ -179,16 +186,24 @@ impl MidiInputAlsa {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signal_stop_input_internal(stop_trigger: i32) -> Result<(), Error> {
|
fn signal_stop_input_internal(stop_trigger: i32) -> Result<(), Error> {
|
||||||
if unsafe { self::libc::write(stop_trigger, &false as *const bool as *const _, mem::size_of::<bool>() as self::libc::size_t) } == -1 {
|
if unsafe {
|
||||||
|
self::libc::write(
|
||||||
|
stop_trigger,
|
||||||
|
&false as *const bool as *const _,
|
||||||
|
mem::size_of::<bool>() as self::libc::size_t,
|
||||||
|
)
|
||||||
|
} == -1
|
||||||
|
{
|
||||||
Err(Error::Pipe)
|
Err(Error::Pipe)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alsa_input_handler<F, D>(&mut self, callback: F, mut userdata: D) -> Result<(), Error>
|
fn alsa_input_handler<F, D>(&mut self, callback: F, mut userdata: D) -> Result<(), Error>
|
||||||
where F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error> {
|
where
|
||||||
|
F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error>,
|
||||||
|
{
|
||||||
// fd defitions
|
// fd defitions
|
||||||
use self::alsa::PollDescriptors;
|
use self::alsa::PollDescriptors;
|
||||||
use self::libc::pollfd;
|
use self::libc::pollfd;
|
||||||
|
|
@ -202,7 +217,7 @@ impl MidiInputAlsa {
|
||||||
|
|
||||||
// make poll fds
|
// make poll fds
|
||||||
let poll_desc_info = (&self.seq, Some(Direction::Capture));
|
let poll_desc_info = (&self.seq, Some(Direction::Capture));
|
||||||
let mut poll_fds = vec![INVALID_POLLFD; poll_desc_info.count()+1];
|
let mut poll_fds = vec![INVALID_POLLFD; poll_desc_info.count() + 1];
|
||||||
poll_fds[0] = pollfd {
|
poll_fds[0] = pollfd {
|
||||||
fd: self.stop_trigger[0],
|
fd: self.stop_trigger[0],
|
||||||
events: self::libc::POLLIN,
|
events: self::libc::POLLIN,
|
||||||
|
|
@ -217,7 +232,13 @@ impl MidiInputAlsa {
|
||||||
// Read stop event from triggerer
|
// Read stop event from triggerer
|
||||||
if poll_fds[0].revents & self::libc::POLLIN != 0 {
|
if poll_fds[0].revents & self::libc::POLLIN != 0 {
|
||||||
let mut pollread = false;
|
let mut pollread = false;
|
||||||
let _res = unsafe { self::libc::read(poll_fds[0].fd, &mut pollread as *mut bool as *mut libc::c_void, mem::size_of::<bool>() as self::libc::size_t) };
|
let _res = unsafe {
|
||||||
|
self::libc::read(
|
||||||
|
poll_fds[0].fd,
|
||||||
|
&mut pollread as *mut bool as *mut libc::c_void,
|
||||||
|
mem::size_of::<bool>() as self::libc::size_t,
|
||||||
|
)
|
||||||
|
};
|
||||||
if !pollread {
|
if !pollread {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -243,7 +264,7 @@ impl MidiInputAlsa {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("ALSA CALLBACK ERROR: {}", e);
|
eprintln!("ALSA CALLBACK ERROR: {}", e);
|
||||||
eprintln!("continuing execution");
|
eprintln!("continuing execution");
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -251,13 +272,13 @@ impl MidiInputAlsa {
|
||||||
|
|
||||||
fn binary_input_handler<F, D>(&mut self, callback: F, userdata: D) -> Result<(), Error>
|
fn binary_input_handler<F, D>(&mut self, callback: F, userdata: D) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: Fn(&Self, Option<SystemTime>, &[u8], &mut D) -> Result<(),Error> + Send
|
F: Fn(&Self, Option<SystemTime>, &[u8], &mut D) -> Result<(), Error> + Send,
|
||||||
{
|
{
|
||||||
let decoder = MidiEvent::new(0)?;
|
let decoder = MidiEvent::new(0)?;
|
||||||
decoder.enable_running_status(false);
|
decoder.enable_running_status(false);
|
||||||
|
|
||||||
let message = vec!();
|
let message = vec![];
|
||||||
let buffer: [u8;12] = [0;12];
|
let buffer: [u8; 12] = [0; 12];
|
||||||
let continue_sysex = false;
|
let continue_sysex = false;
|
||||||
|
|
||||||
let ts = match self.start_time.as_ref() {
|
let ts = match self.start_time.as_ref() {
|
||||||
|
|
@ -265,48 +286,61 @@ impl MidiInputAlsa {
|
||||||
_ => SystemTime::now(),
|
_ => SystemTime::now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.alsa_input_handler(|s, mut ev, (message, buffer, continue_sysex, userdata)| {
|
self.alsa_input_handler(
|
||||||
if !*continue_sysex { message.clear() }
|
|s, mut ev, (message, buffer, continue_sysex, userdata)| {
|
||||||
|
if !*continue_sysex {
|
||||||
|
message.clear()
|
||||||
|
}
|
||||||
|
|
||||||
let do_decode = match ev.get_type() {
|
let do_decode = match ev.get_type() {
|
||||||
EventType::PortSubscribed |
|
EventType::PortSubscribed
|
||||||
EventType::PortUnsubscribed |
|
| EventType::PortUnsubscribed
|
||||||
EventType::Qframe |
|
| EventType::Qframe
|
||||||
EventType::Tick |
|
| EventType::Tick
|
||||||
EventType::Clock |
|
| EventType::Clock
|
||||||
EventType::Sensing => false,
|
| EventType::Sensing => false,
|
||||||
EventType::Sysex => {
|
EventType::Sysex => {
|
||||||
message.extend_from_slice(ev.get_ext().unwrap());
|
message.extend_from_slice(ev.get_ext().unwrap());
|
||||||
*continue_sysex = *message.last().unwrap() != 0xF7;
|
*continue_sysex = *message.last().unwrap() != 0xF7;
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
_ => true
|
_ => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: SysEx messages have already been "decoded" at this point!
|
// NOTE: SysEx messages have already been "decoded" at this point!
|
||||||
if do_decode {
|
if do_decode {
|
||||||
let nbytes = decoder.decode(buffer, &mut ev).map_err(|_| AlsaError::Decode)?;
|
let nbytes = decoder
|
||||||
|
.decode(buffer, &mut ev)
|
||||||
|
.map_err(|_| AlsaError::Decode)?;
|
||||||
if nbytes > 0 {
|
if nbytes > 0 {
|
||||||
message.extend_from_slice(&buffer[0..nbytes+1]);
|
message.extend_from_slice(&buffer[0..nbytes + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.is_empty() || *continue_sysex { return Ok(false); }
|
if message.is_empty() || *continue_sysex {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
let ts: Option<SystemTime> = ev.get_time().map(|v| ts+v);
|
let ts: Option<SystemTime> = ev.get_time().map(|v| ts + v);
|
||||||
(callback)(s, ts, message, userdata)?;
|
(callback)(s, ts, message, userdata)?;
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
},
|
||||||
, (message, buffer, continue_sysex, userdata))?;
|
(message, buffer, continue_sysex, userdata),
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn threaded_alsa_input<F, D>(&mut self, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(),Error>
|
fn threaded_alsa_input<F, D>(
|
||||||
|
&mut self,
|
||||||
|
callback: F,
|
||||||
|
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
userdata: D,
|
||||||
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error> + Send,
|
F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error> + Send,
|
||||||
D: Send,
|
D: Send,
|
||||||
{
|
{
|
||||||
thread::scope( |sc| -> Result<(), Error> {
|
thread::scope(|sc| -> Result<(), Error> {
|
||||||
let stop_trigger = self.stop_trigger[1];
|
let stop_trigger = self.stop_trigger[1];
|
||||||
let t = sc.spawn(move || -> Result<(), Error> {
|
let t = sc.spawn(move || -> Result<(), Error> {
|
||||||
let userdata = userdata;
|
let userdata = userdata;
|
||||||
|
|
@ -316,20 +350,26 @@ impl MidiInputAlsa {
|
||||||
});
|
});
|
||||||
match rs.recv()? {
|
match rs.recv()? {
|
||||||
true => Self::signal_stop_input_internal(stop_trigger)?,
|
true => Self::signal_stop_input_internal(stop_trigger)?,
|
||||||
false => ()
|
false => (),
|
||||||
};
|
};
|
||||||
t.join().expect("unexpected thread error")?;
|
t.join().expect("unexpected thread error")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn threaded_handler<H, F, T, RF: ?Sized, D, R>(&mut self, handler: H, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(),Error>
|
fn threaded_handler<H, F, T, RF: ?Sized, D, R>(
|
||||||
|
&mut self,
|
||||||
|
handler: H,
|
||||||
|
callback: F,
|
||||||
|
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
userdata: D,
|
||||||
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
H: Fn(&mut Self, F,D) -> Result<(), Error> + Send,
|
H: Fn(&mut Self, F, D) -> Result<(), Error> + Send,
|
||||||
F: Fn(&Self, T, &RF, &mut D) -> Result<R,Error> + Send,
|
F: Fn(&Self, T, &RF, &mut D) -> Result<R, Error> + Send,
|
||||||
D: Send,
|
D: Send,
|
||||||
{
|
{
|
||||||
thread::scope( |sc| -> Result<(), Error> {
|
thread::scope(|sc| -> Result<(), Error> {
|
||||||
let stop_trigger = self.stop_trigger[1];
|
let stop_trigger = self.stop_trigger[1];
|
||||||
let t = sc.spawn(move || -> Result<(), Error> {
|
let t = sc.spawn(move || -> Result<(), Error> {
|
||||||
let userdata = userdata;
|
let userdata = userdata;
|
||||||
|
|
@ -339,7 +379,7 @@ impl MidiInputAlsa {
|
||||||
});
|
});
|
||||||
match rs.recv()? {
|
match rs.recv()? {
|
||||||
true => Self::signal_stop_input_internal(stop_trigger)?,
|
true => Self::signal_stop_input_internal(stop_trigger)?,
|
||||||
false => ()
|
false => (),
|
||||||
};
|
};
|
||||||
t.join().expect("unexpected thread error")?;
|
t.join().expect("unexpected thread error")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -361,7 +401,7 @@ impl MidiInput for MidiInputAlsa {
|
||||||
subscription: None,
|
subscription: None,
|
||||||
connect_addr: None,
|
connect_addr: None,
|
||||||
start_time: None,
|
start_time: None,
|
||||||
stop_trigger: [-1,-1],
|
stop_trigger: [-1, -1],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,26 +411,29 @@ impl MidiInput for MidiInputAlsa {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ports(&self) -> Result<Vec<MidiPort<DeviceAddr>>, Error> {
|
fn ports(&self) -> Result<Vec<MidiPort<DeviceAddr>>, Error> {
|
||||||
get_ports(&self.seq, PortCap::READ | PortCap::SUBS_READ).iter().map(|x| -> Result<MidiPort<DeviceAddr>, Error> {
|
get_ports(&self.seq, PortCap::READ | PortCap::SUBS_READ)
|
||||||
|
.iter()
|
||||||
|
.map(|x| -> Result<MidiPort<DeviceAddr>, Error> {
|
||||||
let cinfo = self.seq.get_any_client_info(x.get_client())?;
|
let cinfo = self.seq.get_any_client_info(x.get_client())?;
|
||||||
Ok(MidiPort {
|
Ok(MidiPort {
|
||||||
name: cinfo.get_name()?.to_string()+":"+x.get_name()?,
|
name: cinfo.get_name()?.to_string() + ":" + x.get_name()?,
|
||||||
addr: x.addr().into(),
|
addr: x.addr().into(),
|
||||||
})
|
})
|
||||||
}).collect()
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_ports(&self, mut ports: Vec<MidiPort<DeviceAddr>>, filter: PortFilter<Self::DeviceAddr>) -> Vec<MidiPort<DeviceAddr>> {
|
fn filter_ports(
|
||||||
ports.retain(
|
&self,
|
||||||
|p| {
|
mut ports: Vec<MidiPort<DeviceAddr>>,
|
||||||
match &filter {
|
filter: PortFilter<Self::DeviceAddr>,
|
||||||
|
) -> Vec<MidiPort<DeviceAddr>> {
|
||||||
|
ports.retain(|p| match &filter {
|
||||||
PortFilter::All => true,
|
PortFilter::All => true,
|
||||||
PortFilter::Name(s) => p.name.contains(s),
|
PortFilter::Name(s) => p.name.contains(s),
|
||||||
PortFilter::Regex(s) => s.is_match(&p.name),
|
PortFilter::Regex(s) => s.is_match(&p.name),
|
||||||
PortFilter::Addr(s) => p.addr == *s,
|
PortFilter::Addr(s) => p.addr == *s,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
ports
|
ports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,7 +446,10 @@ impl MidiInput for MidiInputAlsa {
|
||||||
|
|
||||||
let sub = PortSubscribe::empty()?;
|
let sub = PortSubscribe::empty()?;
|
||||||
sub.set_sender(src_pinfo.addr());
|
sub.set_sender(src_pinfo.addr());
|
||||||
sub.set_dest(Addr { client: self.seq.client_id()?, port: vport});
|
sub.set_dest(Addr {
|
||||||
|
client: self.seq.client_id()?,
|
||||||
|
port: vport,
|
||||||
|
});
|
||||||
self.seq.subscribe_port(&sub)?;
|
self.seq.subscribe_port(&sub)?;
|
||||||
self.subscription = Some(sub);
|
self.subscription = Some(sub);
|
||||||
self.init_trigger()?;
|
self.init_trigger()?;
|
||||||
|
|
@ -413,26 +459,35 @@ impl MidiInput for MidiInputAlsa {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, (tss, rss): (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error> {
|
fn device_events(
|
||||||
|
&mut self,
|
||||||
|
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
|
||||||
|
(tss, rss): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
) -> Result<(), Error> {
|
||||||
let ports = self.ports()?;
|
let ports = self.ports()?;
|
||||||
let port = self.filter_ports(ports, PortFilter::Name(ANNOUNCE_ADDR.to_string()));
|
let port = self.filter_ports(ports, PortFilter::Name(ANNOUNCE_ADDR.to_string()));
|
||||||
self.connect(&port[0].addr, CLIENT_NAME_ANNOUNCE)?;
|
self.connect(&port[0].addr, CLIENT_NAME_ANNOUNCE)?;
|
||||||
self.threaded_alsa_input(move |s: &Self, ev: alsa::seq::Event, _| -> Result<bool, Error> {
|
self.threaded_alsa_input(
|
||||||
|
move |s: &Self, ev: alsa::seq::Event, _| -> Result<bool, Error> {
|
||||||
// handle disconnect event on watched port
|
// handle disconnect event on watched port
|
||||||
match ev.get_type() {
|
match ev.get_type() {
|
||||||
EventType::PortStart => {
|
EventType::PortStart => {
|
||||||
if let Some(a) = ev.get_data::<alsa::seq::Addr>() {
|
if let Some(a) = ev.get_data::<alsa::seq::Addr>() {
|
||||||
let p = s.ports()?;
|
let p = s.ports()?;
|
||||||
let pp = s.filter_ports(p, PortFilter::Addr( a.into() ) );
|
let pp = s.filter_ports(p, PortFilter::Addr(a.into()));
|
||||||
if !pp.is_empty() {
|
if !pp.is_empty() {
|
||||||
ts.send(Some(pp[0].clone())).expect("unexpected send() error");
|
ts.send(Some(pp[0].clone()))
|
||||||
|
.expect("unexpected send() error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
_ => Ok(false),
|
_ => Ok(false),
|
||||||
}
|
}
|
||||||
}, (tss, rss), ())?;
|
},
|
||||||
|
(tss, rss),
|
||||||
|
(),
|
||||||
|
)?;
|
||||||
self.close_internal();
|
self.close_internal();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -441,15 +496,24 @@ impl MidiInput for MidiInputAlsa {
|
||||||
Self::signal_stop_input_internal(self.stop_trigger[1])
|
Self::signal_stop_input_internal(self.stop_trigger[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input<F, D>(&mut self, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(), Error>
|
fn handle_input<F, D>(
|
||||||
|
&mut self,
|
||||||
|
callback: F,
|
||||||
|
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
userdata: D,
|
||||||
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
|
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
|
||||||
D: Send,
|
D: Send,
|
||||||
{
|
{
|
||||||
self.threaded_handler(Self::binary_input_handler,
|
self.threaded_handler(
|
||||||
|s, t, m, d| -> Result<(),Error> {
|
Self::binary_input_handler,
|
||||||
callback(s,m,t,d);
|
|s, t, m, d| -> Result<(), Error> {
|
||||||
|
callback(s, m, t, d);
|
||||||
Ok(())
|
Ok(())
|
||||||
}, (ts, rs), userdata)
|
},
|
||||||
|
(ts, rs),
|
||||||
|
userdata,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
use crate::util::InternalTryFrom;
|
use crate::util::InternalTryFrom;
|
||||||
|
|
||||||
pub use super::input::{MidiInput,MidiInputHandler};
|
pub use super::input::{MidiInput, MidiInputHandler};
|
||||||
|
|
||||||
pub trait Builder<D, R> {
|
pub trait Builder<D, R> {
|
||||||
fn build<T>(&self) -> fn(&T, D) -> R
|
fn build<T>(&self) -> fn(&T, D) -> R
|
||||||
where
|
where
|
||||||
T: MidiInputHandler+MidiInput+Send+'static,
|
T: MidiInputHandler + MidiInput + Send + 'static,
|
||||||
<T as MidiInputHandler>::DeviceAddr: std::fmt::Display+'static+InternalTryFrom<String>,
|
<T as MidiInputHandler>::DeviceAddr: std::fmt::Display + 'static + InternalTryFrom<String>;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! builder {
|
macro_rules! builder {
|
||||||
|
|
@ -16,8 +15,9 @@ macro_rules! builder {
|
||||||
impl Builder<$intype, $rettype> for $name {
|
impl Builder<$intype, $rettype> for $name {
|
||||||
fn build<T>(&self) -> fn(&T, $intype) -> $rettype
|
fn build<T>(&self) -> fn(&T, $intype) -> $rettype
|
||||||
where
|
where
|
||||||
T: MidiInputHandler+Send+'static,
|
T: MidiInputHandler + Send + 'static,
|
||||||
<T as MidiInputHandler>::DeviceAddr: std::fmt::Display+'static+InternalTryFrom<String>,
|
<T as MidiInputHandler>::DeviceAddr:
|
||||||
|
std::fmt::Display + 'static + InternalTryFrom<String>,
|
||||||
{
|
{
|
||||||
$fct
|
$fct
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize,Debug,Clone,Copy,Eq,PartialEq)]
|
#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum MidiDriver {
|
pub enum MidiDriver {
|
||||||
ALSA,
|
ALSA,
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,83 @@
|
||||||
use crate::util::InternalTryFrom;
|
|
||||||
use crate::{Error, constant};
|
|
||||||
use crate::config::DeviceConfig;
|
use crate::config::DeviceConfig;
|
||||||
use crate::eventmap::EventMap;
|
|
||||||
use crate::event::{Event, EventBuf};
|
use crate::event::{Event, EventBuf};
|
||||||
|
use crate::eventmap::EventMap;
|
||||||
|
use crate::util::InternalTryFrom;
|
||||||
|
use crate::{constant, Error};
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{SystemTime, Instant};
|
use std::time::{Instant, SystemTime};
|
||||||
use std::sync::{mpsc, Mutex, Arc};
|
|
||||||
|
|
||||||
use queues::{CircularBuffer, IsQueue};
|
use queues::{CircularBuffer, IsQueue};
|
||||||
|
|
||||||
use super::{PortFilter, MidiPort};
|
use super::{MidiPort, PortFilter};
|
||||||
|
|
||||||
pub trait MidiInput
|
pub trait MidiInput
|
||||||
where
|
where
|
||||||
<Self as MidiInput>::DeviceAddr: Clone+Send+FromStr,
|
<Self as MidiInput>::DeviceAddr: Clone + Send + FromStr,
|
||||||
<Self as MidiInput>::DeviceAddr: InternalTryFrom<std::string::String>
|
<Self as MidiInput>::DeviceAddr: InternalTryFrom<std::string::String>,
|
||||||
{
|
{
|
||||||
type DeviceAddr;
|
type DeviceAddr;
|
||||||
fn new(client_name: &str) -> Result<Self, Error>
|
fn new(client_name: &str) -> Result<Self, Error>
|
||||||
where Self: Sized;
|
where
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
fn close(self) -> Result<(), Error>;
|
fn close(self) -> Result<(), Error>;
|
||||||
|
|
||||||
fn ports(&self) -> Result<Vec<MidiPort<Self::DeviceAddr>>, Error>;
|
fn ports(&self) -> Result<Vec<MidiPort<Self::DeviceAddr>>, Error>;
|
||||||
|
|
||||||
fn filter_ports(&self, ports: Vec<MidiPort<Self::DeviceAddr>>, filter: PortFilter<Self::DeviceAddr>) -> Vec<MidiPort<Self::DeviceAddr>>;
|
fn filter_ports(
|
||||||
|
&self,
|
||||||
|
ports: Vec<MidiPort<Self::DeviceAddr>>,
|
||||||
|
filter: PortFilter<Self::DeviceAddr>,
|
||||||
|
) -> Vec<MidiPort<Self::DeviceAddr>>;
|
||||||
|
|
||||||
fn connect(&mut self, port_addr: &Self::DeviceAddr, port_name: &str) -> Result<(), Error>;
|
fn connect(&mut self, port_addr: &Self::DeviceAddr, port_name: &str) -> Result<(), Error>;
|
||||||
|
|
||||||
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error>;
|
fn device_events(
|
||||||
|
&mut self,
|
||||||
|
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
|
||||||
|
ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
fn signal_stop_input(&self) -> Result<(), Error>;
|
fn signal_stop_input(&self) -> Result<(), Error>;
|
||||||
|
|
||||||
fn handle_input<F, D>(&mut self, callback: F, rts: (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(), Error>
|
fn handle_input<F, D>(
|
||||||
|
&mut self,
|
||||||
|
callback: F,
|
||||||
|
rts: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
userdata: D,
|
||||||
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
|
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
|
||||||
D: Send,
|
D: Send;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MidiInputHandler
|
pub trait MidiInputHandler
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
<Self as MidiInputHandler>::DeviceAddr: Clone+Send+FromStr,
|
<Self as MidiInputHandler>::DeviceAddr: Clone + Send + FromStr,
|
||||||
{
|
{
|
||||||
type DeviceAddr;
|
type DeviceAddr;
|
||||||
fn new(client_name: &str) -> Result<Self, Error>;
|
fn new(client_name: &str) -> Result<Self, Error>;
|
||||||
fn ports(&self) -> Result<Vec<MidiPort<Self::DeviceAddr>>, Error>;
|
fn ports(&self) -> Result<Vec<MidiPort<Self::DeviceAddr>>, Error>;
|
||||||
fn try_connect(&self, port: MidiPort<Self::DeviceAddr>, filter: PortFilter<Self::DeviceAddr> ) -> Result<Option<Self>, Error>;
|
fn try_connect(
|
||||||
fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, trs: (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error>;
|
&self,
|
||||||
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, ss: (mpsc::Sender<bool>,mpsc::Receiver<bool>)) -> Result<(), Error>;
|
port: MidiPort<Self::DeviceAddr>,
|
||||||
|
filter: PortFilter<Self::DeviceAddr>,
|
||||||
|
) -> Result<Option<Self>, Error>;
|
||||||
|
fn run(
|
||||||
|
&mut self,
|
||||||
|
conf: &DeviceConfig,
|
||||||
|
eventmap: &EventMap,
|
||||||
|
trs: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
fn device_events(
|
||||||
|
&mut self,
|
||||||
|
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
|
||||||
|
ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic implementation
|
// Generic implementation
|
||||||
|
|
@ -63,7 +89,6 @@ where
|
||||||
{
|
{
|
||||||
type DeviceAddr = T::DeviceAddr;
|
type DeviceAddr = T::DeviceAddr;
|
||||||
|
|
||||||
|
|
||||||
fn new(client_name: &str) -> Result<Self, Error> {
|
fn new(client_name: &str) -> Result<Self, Error> {
|
||||||
MidiInput::new(client_name)
|
MidiInput::new(client_name)
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +97,11 @@ where
|
||||||
MidiInput::ports(self)
|
MidiInput::ports(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_connect(&self, port: MidiPort<Self::DeviceAddr>, filter: PortFilter<T::DeviceAddr> ) -> Result<Option<Self>, Error> {
|
fn try_connect(
|
||||||
|
&self,
|
||||||
|
port: MidiPort<Self::DeviceAddr>,
|
||||||
|
filter: PortFilter<T::DeviceAddr>,
|
||||||
|
) -> Result<Option<Self>, Error> {
|
||||||
let portmap = self.ports()?;
|
let portmap = self.ports()?;
|
||||||
let pv = self.filter_ports(portmap, PortFilter::Addr(port.addr));
|
let pv = self.filter_ports(portmap, PortFilter::Addr(port.addr));
|
||||||
let pv = self.filter_ports(pv, filter);
|
let pv = self.filter_ports(pv, filter);
|
||||||
|
|
@ -81,35 +110,44 @@ where
|
||||||
let mut v = T::new(constant::CLIENT_NAME_HANDLER)?;
|
let mut v = T::new(constant::CLIENT_NAME_HANDLER)?;
|
||||||
v.connect(&port.addr, constant::CLIENT_NAME_HANDLER)?;
|
v.connect(&port.addr, constant::CLIENT_NAME_HANDLER)?;
|
||||||
Ok(Some(v))
|
Ok(Some(v))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, ss: (mpsc::Sender<bool>,mpsc::Receiver<bool>)) -> Result<(), Error> {
|
fn device_events(
|
||||||
|
&mut self,
|
||||||
|
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
|
||||||
|
ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
) -> Result<(), Error> {
|
||||||
self.device_events(ts, ss)
|
self.device_events(ts, ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error> {
|
fn run(
|
||||||
|
&mut self,
|
||||||
|
conf: &DeviceConfig,
|
||||||
|
eventmap: &EventMap,
|
||||||
|
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
|
||||||
|
) -> Result<(), Error> {
|
||||||
thread::scope(|s| -> Result<(), Error> {
|
thread::scope(|s| -> Result<(), Error> {
|
||||||
|
|
||||||
// parking signal for runner, true = stop
|
// parking signal for runner, true = stop
|
||||||
let (pts,prs) = mpsc::channel::<bool>();
|
let (pts, prs) = mpsc::channel::<bool>();
|
||||||
|
|
||||||
// event queue populated by the main thread and consumed by the exec thread
|
// event queue populated by the main thread and consumed by the exec thread
|
||||||
let evq = Arc::new(Mutex::new(CircularBuffer::<EventBuf>::new(conf.queue_length)));
|
let evq = Arc::new(Mutex::new(CircularBuffer::<EventBuf>::new(
|
||||||
|
conf.queue_length,
|
||||||
|
)));
|
||||||
|
|
||||||
// background execution loop
|
// background execution loop
|
||||||
let rq = evq.clone();
|
let rq = evq.clone();
|
||||||
let exec_thread = s.spawn(move || -> Result<(),Error> {
|
let exec_thread = s.spawn(move || -> Result<(), Error> {
|
||||||
loop {
|
loop {
|
||||||
if prs.recv()? {
|
if prs.recv()? {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
// nest the lock into a scope to release it before run
|
// nest the lock into a scope to release it before run
|
||||||
let (ev,start): (EventBuf,Instant) = {
|
let (ev, start): (EventBuf, Instant) = {
|
||||||
let mut evq = rq.lock().unwrap();
|
let mut evq = rq.lock().unwrap();
|
||||||
if evq.size() > 0 {
|
if evq.size() > 0 {
|
||||||
(evq.remove().unwrap(), Instant::now())
|
(evq.remove().unwrap(), Instant::now())
|
||||||
|
|
@ -117,7 +155,9 @@ where
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
eventmap.run_event(&ev.as_event()).unwrap_or_else(|e| eprintln!("ERROR: error on run: {}", e) );
|
eventmap
|
||||||
|
.run_event(&ev.as_event())
|
||||||
|
.unwrap_or_else(|e| eprintln!("ERROR: error on run: {}", e));
|
||||||
// wait until interval has been reached
|
// wait until interval has been reached
|
||||||
let elapsed_time = start.elapsed();
|
let elapsed_time = start.elapsed();
|
||||||
if elapsed_time < conf.interval {
|
if elapsed_time < conf.interval {
|
||||||
|
|
@ -128,7 +168,8 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.handle_input(|_,m,t,(evq,pts)| {
|
self.handle_input(
|
||||||
|
|_, m, t, (evq, pts)| {
|
||||||
let mut event: EventBuf = Event::from(m).into();
|
let mut event: EventBuf = Event::from(m).into();
|
||||||
event.timestamp = t;
|
event.timestamp = t;
|
||||||
if conf.log {
|
if conf.log {
|
||||||
|
|
@ -137,13 +178,15 @@ where
|
||||||
let mut evq = evq.lock().unwrap();
|
let mut evq = evq.lock().unwrap();
|
||||||
evq.add(event).unwrap();
|
evq.add(event).unwrap();
|
||||||
pts.send(false).expect("unexpected write error");
|
pts.send(false).expect("unexpected write error");
|
||||||
}, (ts,rs), (evq,pts.clone()))?;
|
},
|
||||||
|
(ts, rs),
|
||||||
|
(evq, pts.clone()),
|
||||||
|
)?;
|
||||||
|
|
||||||
pts.send(true).expect("unexpected write error");
|
pts.send(true).expect("unexpected write error");
|
||||||
let _ = exec_thread.join();
|
let _ = exec_thread.join();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
|
||||||
pub mod port;
|
|
||||||
pub mod portfilter;
|
|
||||||
pub mod input;
|
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod driver;
|
pub mod driver;
|
||||||
|
pub mod input;
|
||||||
|
pub mod port;
|
||||||
|
pub mod portfilter;
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
pub use driver::MidiDriver;
|
|
||||||
pub use builder::Builder;
|
pub use builder::Builder;
|
||||||
|
pub use driver::MidiDriver;
|
||||||
|
pub use input::{MidiInput, MidiInputHandler};
|
||||||
pub use port::MidiPort;
|
pub use port::MidiPort;
|
||||||
pub use portfilter::PortFilter;
|
pub use portfilter::PortFilter;
|
||||||
pub use input::{MidiInput,MidiInputHandler};
|
|
||||||
|
|
||||||
pub enum MidiHandler {
|
pub enum MidiHandler {
|
||||||
ALSA(backend::MidiInputAlsa),
|
ALSA(backend::MidiInputAlsa),
|
||||||
|
|
@ -35,7 +35,7 @@ impl MidiHandler {
|
||||||
// wrap generic functions into builder because functions with generic traits cannot be passed as arguments
|
// wrap generic functions into builder because functions with generic traits cannot be passed as arguments
|
||||||
pub fn builder_handler<B, D, R>(&mut self, builder: B, data: D) -> R
|
pub fn builder_handler<B, D, R>(&mut self, builder: B, data: D) -> R
|
||||||
where
|
where
|
||||||
B: Builder<D,R>,
|
B: Builder<D, R>,
|
||||||
D: Send,
|
D: Send,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,17 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||||
#[derive(Eq,PartialEq,Debug,Clone)]
|
|
||||||
pub struct MidiPort<T>
|
pub struct MidiPort<T>
|
||||||
where
|
where
|
||||||
T: Clone
|
T: Clone,
|
||||||
{
|
{
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub addr: T,
|
pub addr: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> Display for MidiPort<T>
|
impl<T> Display for MidiPort<T>
|
||||||
where
|
where
|
||||||
T: Display+Clone
|
T: Display + Clone,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}\t{}", self.addr, self.name)
|
write!(f, "{}\t{}", self.addr, self.name)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
use crate::Error;
|
|
||||||
use crate::config::DeviceConfig;
|
|
||||||
use crate::config::device::Identifier;
|
use crate::config::device::Identifier;
|
||||||
|
use crate::config::DeviceConfig;
|
||||||
use crate::util::InternalTryFrom;
|
use crate::util::InternalTryFrom;
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
#[derive(Debug,Clone)]
|
pub enum PortFilter<T> {
|
||||||
pub enum PortFilter<T>{
|
|
||||||
All,
|
All,
|
||||||
Name(String),
|
Name(String),
|
||||||
Regex(regex::Regex),
|
Regex(regex::Regex),
|
||||||
Addr(T),
|
Addr(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> InternalTryFrom<&DeviceConfig> for PortFilter<T>
|
impl<T> InternalTryFrom<&DeviceConfig> for PortFilter<T>
|
||||||
where
|
where
|
||||||
T: InternalTryFrom<String>,
|
T: InternalTryFrom<String>,
|
||||||
|
|
|
||||||
91
src/run.rs
91
src/run.rs
|
|
@ -1,29 +1,36 @@
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::sync::{Mutex,Arc};
|
|
||||||
|
|
||||||
use libc::SIGUSR1;
|
use libc::SIGUSR1;
|
||||||
use signal_hook::iterator::Signals;
|
use signal_hook::iterator::Signals;
|
||||||
|
|
||||||
use crate::util::InternalTryFrom;
|
use crate::config::{Config, DeviceConfig};
|
||||||
use crate::{Error, constant};
|
|
||||||
use crate::midi::{PortFilter,MidiInputHandler, MidiPort, Builder};
|
|
||||||
use crate::config::{Config,DeviceConfig};
|
|
||||||
use crate::eventmap::EventMap;
|
use crate::eventmap::EventMap;
|
||||||
use crate::midi::builder::builder;
|
use crate::midi::builder::builder;
|
||||||
|
use crate::midi::{Builder, MidiInputHandler, MidiPort, PortFilter};
|
||||||
|
use crate::util::InternalTryFrom;
|
||||||
|
use crate::{constant, Error};
|
||||||
|
|
||||||
type DeviceRunItem<'a> = (&'a DeviceConfig, EventMap<'a>, Option<Arc<Mutex<(u32, u32)>>>);
|
type DeviceRunItem<'a> = (
|
||||||
type DeviceRunResult<'a> =(thread::ScopedJoinHandle<'a, Result<(), Error>>, mpsc::Sender<bool>);
|
&'a DeviceConfig,
|
||||||
|
EventMap<'a>,
|
||||||
|
Option<Arc<Mutex<(u32, u32)>>>,
|
||||||
|
);
|
||||||
|
type DeviceRunResult<'a> = (
|
||||||
|
thread::ScopedJoinHandle<'a, Result<(), Error>>,
|
||||||
|
mpsc::Sender<bool>,
|
||||||
|
);
|
||||||
|
|
||||||
pub fn cross_shell(cmd: &str) -> Vec<String> {
|
pub fn cross_shell(cmd: &str) -> Vec<String> {
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
vec!("cmd", "/C", cmd)
|
vec!["cmd", "/C", cmd]
|
||||||
} else {
|
} else {
|
||||||
vec!("sh", "-c", cmd)
|
vec!["sh", "-c", cmd]
|
||||||
}
|
}
|
||||||
.iter().map(
|
.iter()
|
||||||
|x| x.to_string()
|
.map(|x| x.to_string())
|
||||||
).collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
builder!(ListDevicesBuilder, list_devices, (), Result<(), Error>);
|
builder!(ListDevicesBuilder, list_devices, (), Result<(), Error>);
|
||||||
|
|
@ -31,8 +38,8 @@ builder!(RunConfigBuilder, run_config, &Config, Result<(), Error>);
|
||||||
|
|
||||||
pub fn list_devices<T>(input: &T, _: ()) -> Result<(), Error>
|
pub fn list_devices<T>(input: &T, _: ()) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: MidiInputHandler+Send+'static,
|
T: MidiInputHandler + Send + 'static,
|
||||||
<T as MidiInputHandler>::DeviceAddr: 'static+std::fmt::Display,
|
<T as MidiInputHandler>::DeviceAddr: 'static + std::fmt::Display,
|
||||||
{
|
{
|
||||||
let ports = MidiInputHandler::ports(input)?;
|
let ports = MidiInputHandler::ports(input)?;
|
||||||
println!(" Addr\t Name");
|
println!(" Addr\t Name");
|
||||||
|
|
@ -44,17 +51,23 @@ where
|
||||||
|
|
||||||
pub fn run_config<T>(input: &T, conf: &Config) -> Result<(), Error>
|
pub fn run_config<T>(input: &T, conf: &Config) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: MidiInputHandler+Send+'static,
|
T: MidiInputHandler + Send + 'static,
|
||||||
<T as MidiInputHandler>::DeviceAddr: 'static+std::fmt::Display+InternalTryFrom<String>,
|
<T as MidiInputHandler>::DeviceAddr: 'static + std::fmt::Display + InternalTryFrom<String>,
|
||||||
{
|
{
|
||||||
let cfevmap: Vec<DeviceRunItem> = conf.devices.iter().map(|x|
|
let cfevmap: Vec<DeviceRunItem> = conf
|
||||||
(x, EventMap::from(x),
|
.devices
|
||||||
x.max_connections.map(|v| (Arc::new(Mutex::new((0,v)))))
|
.iter()
|
||||||
|
.map(|x| {
|
||||||
|
(
|
||||||
|
x,
|
||||||
|
EventMap::from(x),
|
||||||
|
x.max_connections.map(|v| (Arc::new(Mutex::new((0, v))))),
|
||||||
)
|
)
|
||||||
).collect();
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let (tdev,rdev) = mpsc::channel::<Option<MidiPort<T::DeviceAddr>>>();
|
let (tdev, rdev) = mpsc::channel::<Option<MidiPort<T::DeviceAddr>>>();
|
||||||
let (tsd,rsd) = mpsc::channel::<bool>();
|
let (tsd, rsd) = mpsc::channel::<bool>();
|
||||||
|
|
||||||
let ntsd = tsd.clone();
|
let ntsd = tsd.clone();
|
||||||
let ntdev = tdev.clone();
|
let ntdev = tdev.clone();
|
||||||
|
|
@ -77,12 +90,14 @@ where
|
||||||
let mut threads: Vec<DeviceRunResult> = Vec::new();
|
let mut threads: Vec<DeviceRunResult> = Vec::new();
|
||||||
let ports = input.ports()?;
|
let ports = input.ports()?;
|
||||||
for p in ports {
|
for p in ports {
|
||||||
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? { threads.push(v) }
|
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? {
|
||||||
|
threads.push(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let event_thread = s.spawn(move || {
|
let event_thread = s.spawn(move || {
|
||||||
let mut input = T::new(constant::CLIENT_NAME_EVENT).unwrap();
|
let mut input = T::new(constant::CLIENT_NAME_EVENT).unwrap();
|
||||||
let r = input.device_events(tdev.clone(), (tsd,rsd));
|
let r = input.device_events(tdev.clone(), (tsd, rsd));
|
||||||
tdev.send(None).unwrap();
|
tdev.send(None).unwrap();
|
||||||
r
|
r
|
||||||
});
|
});
|
||||||
|
|
@ -96,12 +111,17 @@ where
|
||||||
if conf.log {
|
if conf.log {
|
||||||
println!("{}: device connect: {}", constant::CLIENT_NAME, p);
|
println!("{}: device connect: {}", constant::CLIENT_NAME, p);
|
||||||
}
|
}
|
||||||
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? { threads.push(v) }
|
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? {
|
||||||
};
|
threads.push(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
event_thread.join().unwrap()?;
|
event_thread.join().unwrap()?;
|
||||||
for (thread,ss) in threads {
|
for (thread, ss) in threads {
|
||||||
let _ = ss.send(true);
|
let _ = ss.send(true);
|
||||||
let _ = thread.join().unwrap().map_err(|e| eprintln!("WARN: error in thread: {}", e));
|
let _ = thread
|
||||||
|
.join()
|
||||||
|
.unwrap()
|
||||||
|
.map_err(|e| eprintln!("WARN: error in thread: {}", e));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -112,12 +132,11 @@ fn try_connect_process<'a, T>(
|
||||||
input: &T,
|
input: &T,
|
||||||
s: &'a thread::Scope<'a, '_>,
|
s: &'a thread::Scope<'a, '_>,
|
||||||
p: &MidiPort<T::DeviceAddr>,
|
p: &MidiPort<T::DeviceAddr>,
|
||||||
cfevmap: &'a[DeviceRunItem<'a>],
|
cfevmap: &'a [DeviceRunItem<'a>],
|
||||||
)
|
) -> Result<Option<DeviceRunResult<'a>>, Error>
|
||||||
-> Result<Option<DeviceRunResult<'a>>, Error>
|
|
||||||
where
|
where
|
||||||
T: MidiInputHandler+Send+'static,
|
T: MidiInputHandler + Send + 'static,
|
||||||
<T as MidiInputHandler>::DeviceAddr: 'static+InternalTryFrom<String>,
|
<T as MidiInputHandler>::DeviceAddr: 'static + InternalTryFrom<String>,
|
||||||
{
|
{
|
||||||
for (dev, eventmap, counter) in cfevmap {
|
for (dev, eventmap, counter) in cfevmap {
|
||||||
// device counter is full
|
// device counter is full
|
||||||
|
|
@ -135,13 +154,13 @@ where
|
||||||
m.0 += 1;
|
m.0 += 1;
|
||||||
}
|
}
|
||||||
// stop signal channel
|
// stop signal channel
|
||||||
let (sts,srs) = mpsc::channel::<bool>();
|
let (sts, srs) = mpsc::channel::<bool>();
|
||||||
let mm = counter.as_ref().map(Arc::clone);
|
let mm = counter.as_ref().map(Arc::clone);
|
||||||
let nsts = sts.clone();
|
let nsts = sts.clone();
|
||||||
let t = s.spawn( move || -> Result<(), Error> {
|
let t = s.spawn(move || -> Result<(), Error> {
|
||||||
dev.run_connect()?;
|
dev.run_connect()?;
|
||||||
// blocking process
|
// blocking process
|
||||||
c.run(dev, eventmap, (nsts,srs))?;
|
c.run(dev, eventmap, (nsts, srs))?;
|
||||||
// decrease device counter
|
// decrease device counter
|
||||||
if let Some(m) = mm {
|
if let Some(m) = mm {
|
||||||
let mut m = m.lock().unwrap();
|
let mut m = m.lock().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
|
|
||||||
pub mod smartset;
|
|
||||||
pub mod range;
|
pub mod range;
|
||||||
pub mod remap;
|
pub mod remap;
|
||||||
|
pub mod smartset;
|
||||||
|
|
||||||
pub type SmartSet<T> = smartset::SmartSet<T>;
|
pub type SmartSet<T> = smartset::SmartSet<T>;
|
||||||
pub type Range<T> = range::Range<T>;
|
pub type Range<T> = range::Range<T>;
|
||||||
pub type Remapper<T> = remap::Remapper<T>;
|
pub type Remapper<T> = remap::Remapper<T>;
|
||||||
|
|
||||||
|
|
||||||
macro_rules! visit_from {
|
macro_rules! visit_from {
|
||||||
( $obj:ident , $( ($fct:ident, $type:ty) ),+ $(,)?) => {
|
( $obj:ident , $( ($fct:ident, $type:ty) ),+ $(,)?) => {
|
||||||
$(
|
$(
|
||||||
|
|
@ -23,21 +21,25 @@ macro_rules! visit_from {
|
||||||
|
|
||||||
pub(crate) use visit_from;
|
pub(crate) use visit_from;
|
||||||
|
|
||||||
pub fn map_tryfrom<S,D>(v: Vec<S>) -> Result<Vec<D>, <D as TryFrom<S>>::Error>
|
pub fn map_tryfrom<S, D>(v: Vec<S>) -> Result<Vec<D>, <D as TryFrom<S>>::Error>
|
||||||
where
|
where
|
||||||
D: TryFrom<S>,
|
D: TryFrom<S>,
|
||||||
{
|
{
|
||||||
v.into_iter().map(|x| D::try_from(x)).collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()
|
v.into_iter()
|
||||||
|
.map(|x| D::try_from(x))
|
||||||
|
.collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_opt_tryfrom<S,D>(v: Option<Vec<S>>) -> Result<Option<Vec<D>>, <D as TryFrom<S>>::Error>
|
pub fn map_opt_tryfrom<S, D>(v: Option<Vec<S>>) -> Result<Option<Vec<D>>, <D as TryFrom<S>>::Error>
|
||||||
where
|
where
|
||||||
D: TryFrom<S>,
|
D: TryFrom<S>,
|
||||||
{
|
{
|
||||||
match v {
|
match v {
|
||||||
Some(v) => {
|
Some(v) => Ok(Some(
|
||||||
Ok( Some(v.into_iter().map(|x| D::try_from(x)).collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()?) )
|
v.into_iter()
|
||||||
}
|
.map(|x| D::try_from(x))
|
||||||
|
.collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()?,
|
||||||
|
)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,34 @@
|
||||||
use std::str::FromStr;
|
|
||||||
use std::cmp::PartialOrd;
|
use std::cmp::PartialOrd;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use num::{Num,NumCast};
|
use num::{Num, NumCast};
|
||||||
|
|
||||||
use super::visit_from;
|
use super::visit_from;
|
||||||
|
|
||||||
// Trait aliases are unstable
|
// Trait aliases are unstable
|
||||||
//trait smartsetnum = T: Num+Copy + FromStr;
|
//trait smartsetnum = T: Num+Copy + FromStr;
|
||||||
|
|
||||||
#[derive(Debug,Clone,Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Range<T>
|
pub struct Range<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy + FromStr + PartialOrd,
|
T: Num + Copy + FromStr + PartialOrd,
|
||||||
{
|
{
|
||||||
start: T,
|
start: T,
|
||||||
end: T,
|
end: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn parse_range<T>(s: &str) -> Result<Range<T>, <T as FromStr>::Err>
|
pub fn parse_range<T>(s: &str) -> Result<Range<T>, <T as FromStr>::Err>
|
||||||
where
|
where
|
||||||
T: Num+Copy + FromStr + PartialOrd,
|
T: Num + Copy + FromStr + PartialOrd,
|
||||||
{
|
{
|
||||||
let mut osep = s.find(':');
|
let mut osep = s.find(':');
|
||||||
if osep.is_none() {
|
if osep.is_none() {
|
||||||
osep = s.find('-');
|
osep = s.find('-');
|
||||||
}
|
}
|
||||||
let r = if let Some(sep) = osep {
|
let r = if let Some(sep) = osep {
|
||||||
let (p1,p2) = (&s[..sep], &s[sep+1..] );
|
let (p1, p2) = (&s[..sep], &s[sep + 1..]);
|
||||||
( p1.parse()?, p2.parse()? )
|
(p1.parse()?, p2.parse()?)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let n = s.parse()?;
|
let n = s.parse()?;
|
||||||
(n, n)
|
(n, n)
|
||||||
};
|
};
|
||||||
|
|
@ -39,13 +37,10 @@ T: Num+Copy + FromStr + PartialOrd,
|
||||||
|
|
||||||
impl<T> Range<T>
|
impl<T> Range<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy + FromStr + PartialOrd,
|
T: Num + Copy + FromStr + PartialOrd,
|
||||||
{
|
{
|
||||||
pub fn new(start: T, end: T) -> Self {
|
pub fn new(start: T, end: T) -> Self {
|
||||||
Self {
|
Self { start, end }
|
||||||
start,
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&self) -> T {
|
pub fn start(&self) -> T {
|
||||||
|
|
@ -57,10 +52,10 @@ T: Num+Copy + FromStr + PartialOrd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T,U> From<U> for Range<T>
|
impl<T, U> From<U> for Range<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy+NumCast + FromStr + PartialOrd,
|
T: Num + Copy + NumCast + FromStr + PartialOrd,
|
||||||
U: Num+Copy+num::ToPrimitive + FromStr,
|
U: Num + Copy + num::ToPrimitive + FromStr,
|
||||||
{
|
{
|
||||||
fn from(i: U) -> Self {
|
fn from(i: U) -> Self {
|
||||||
let ti: T = num::NumCast::from(i).unwrap();
|
let ti: T = num::NumCast::from(i).unwrap();
|
||||||
|
|
@ -70,7 +65,7 @@ where
|
||||||
|
|
||||||
impl<T> FromStr for Range<T>
|
impl<T> FromStr for Range<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy + FromStr + PartialOrd,
|
T: Num + Copy + FromStr + PartialOrd,
|
||||||
{
|
{
|
||||||
type Err = <T as FromStr>::Err;
|
type Err = <T as FromStr>::Err;
|
||||||
|
|
||||||
|
|
@ -79,32 +74,31 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use serde::de::{self,Deserialize, Deserializer, Visitor};
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
struct RangeVisitor<T>
|
struct RangeVisitor<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy + FromStr + PartialOrd,
|
T: Num + Copy + FromStr + PartialOrd,
|
||||||
{
|
{
|
||||||
marker: PhantomData<fn() -> Range<T>>
|
marker: PhantomData<fn() -> Range<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> RangeVisitor<T>
|
impl<T> RangeVisitor<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy + FromStr + PartialOrd,
|
T: Num + Copy + FromStr + PartialOrd,
|
||||||
{
|
{
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
marker: PhantomData
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, T> Visitor<'de> for RangeVisitor<T>
|
impl<'de, T> Visitor<'de> for RangeVisitor<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy+PartialOrd + FromStr +NumCast + Deserialize<'de> + std::fmt::Debug,
|
T: Num + Copy + PartialOrd + FromStr + NumCast + Deserialize<'de> + std::fmt::Debug,
|
||||||
<T as FromStr>::Err: std::fmt::Display,
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
type Value = Range<T>;
|
type Value = Range<T>;
|
||||||
|
|
@ -113,7 +107,7 @@ where
|
||||||
formatter.write_str("a set of integer")
|
formatter.write_str("a set of integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_from!{ Range ,
|
visit_from! { Range ,
|
||||||
(visit_i8, i8),
|
(visit_i8, i8),
|
||||||
(visit_i16, i16),
|
(visit_i16, i16),
|
||||||
(visit_i32, i32),
|
(visit_i32, i32),
|
||||||
|
|
@ -135,7 +129,7 @@ where
|
||||||
// This is the trait that informs Serde how to deserialize MyMap.
|
// This is the trait that informs Serde how to deserialize MyMap.
|
||||||
impl<'de, T> Deserialize<'de> for Range<T>
|
impl<'de, T> Deserialize<'de> for Range<T>
|
||||||
where
|
where
|
||||||
T: Num+Copy+NumCast+PartialOrd + Deserialize<'de> + FromStr + std::fmt::Debug,
|
T: Num + Copy + NumCast + PartialOrd + Deserialize<'de> + FromStr + std::fmt::Debug,
|
||||||
<T as FromStr>::Err: std::fmt::Display,
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
|
use num::{Bounded, Num, NumCast, ToPrimitive};
|
||||||
use num::{Num,NumCast,ToPrimitive, Bounded};
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::Range;
|
use super::Range;
|
||||||
|
|
||||||
// Trait aliases are unstable
|
// Trait aliases are unstable
|
||||||
//trait remapnum = T: Num+ToPrimitive+Copy+PartialOrd + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul;
|
//trait remapnum = T: Num+ToPrimitive+Copy+PartialOrd + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul;
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Remapper<T>
|
pub struct Remapper<T>
|
||||||
where
|
where
|
||||||
T: Num+ToPrimitive+Copy+PartialOrd+NumCast + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul,
|
T: Num
|
||||||
|
+ ToPrimitive
|
||||||
|
+ Copy
|
||||||
|
+ PartialOrd
|
||||||
|
+ NumCast
|
||||||
|
+ FromStr
|
||||||
|
+ ops::Add
|
||||||
|
+ ops::Sub
|
||||||
|
+ ops::Div
|
||||||
|
+ ops::Mul,
|
||||||
{
|
{
|
||||||
src: Range<T>,
|
src: Range<T>,
|
||||||
dst: Range<T>,
|
dst: Range<T>,
|
||||||
|
|
@ -19,9 +27,18 @@ where
|
||||||
|
|
||||||
impl<T> Remapper<T>
|
impl<T> Remapper<T>
|
||||||
where
|
where
|
||||||
T: Num+ToPrimitive+Copy+PartialOrd+NumCast + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul + std::fmt::Debug,
|
T: Num
|
||||||
|
+ ToPrimitive
|
||||||
|
+ Copy
|
||||||
|
+ PartialOrd
|
||||||
|
+ NumCast
|
||||||
|
+ FromStr
|
||||||
|
+ ops::Add
|
||||||
|
+ ops::Sub
|
||||||
|
+ ops::Div
|
||||||
|
+ ops::Mul
|
||||||
|
+ std::fmt::Debug,
|
||||||
{
|
{
|
||||||
|
|
||||||
pub fn src(&self) -> &Range<T> {
|
pub fn src(&self) -> &Range<T> {
|
||||||
&self.src
|
&self.src
|
||||||
}
|
}
|
||||||
|
|
@ -31,17 +48,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(src: Range<T>, dst: Range<T>) -> Self {
|
pub fn new(src: Range<T>, dst: Range<T>) -> Self {
|
||||||
Self {
|
Self { src, dst }
|
||||||
src,
|
|
||||||
dst,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remap(&self, v: T) -> T
|
pub fn remap(&self, v: T) -> T {
|
||||||
{
|
|
||||||
// compute actual value in source type
|
// compute actual value in source type
|
||||||
let r: T = (v-self.src.start())*(self.dst.end()-self.dst.start())
|
let r: T = (v - self.src.start()) * (self.dst.end() - self.dst.start())
|
||||||
/ (self.src.end()-self.src.start())
|
/ (self.src.end() - self.src.start())
|
||||||
+ self.dst.start();
|
+ self.dst.start();
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
@ -49,30 +62,39 @@ where
|
||||||
|
|
||||||
impl<T> Remapper<T>
|
impl<T> Remapper<T>
|
||||||
where
|
where
|
||||||
T: Num+ToPrimitive+Copy+PartialOrd+NumCast + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul + std::fmt::Debug,
|
T: Num
|
||||||
|
+ ToPrimitive
|
||||||
|
+ Copy
|
||||||
|
+ PartialOrd
|
||||||
|
+ NumCast
|
||||||
|
+ FromStr
|
||||||
|
+ ops::Add
|
||||||
|
+ ops::Sub
|
||||||
|
+ ops::Div
|
||||||
|
+ ops::Mul
|
||||||
|
+ std::fmt::Debug,
|
||||||
{
|
{
|
||||||
pub fn remap_to<U>(&self, v: T) -> Option<U>
|
pub fn remap_to<U>(&self, v: T) -> Option<U>
|
||||||
where
|
where
|
||||||
U: Num+NumCast+Copy+Bounded + FromStr,
|
U: Num + NumCast + Copy + Bounded + FromStr,
|
||||||
{
|
{
|
||||||
// compute actual value in source type
|
// compute actual value in source type
|
||||||
let r: T = self.remap(v);
|
let r: T = self.remap(v);
|
||||||
|
|
||||||
// find min/max values of target type
|
// find min/max values of target type
|
||||||
let (rmin,rmax): (Option<T>,Option<T>) = (
|
let (rmin, rmax): (Option<T>, Option<T>) = (
|
||||||
num::NumCast::from(U::min_value()),
|
num::NumCast::from(U::min_value()),
|
||||||
num::NumCast::from(U::max_value()),
|
num::NumCast::from(U::max_value()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// if target min/max can be casted onto source, bound output to those
|
// if target min/max can be casted onto source, bound output to those
|
||||||
match (rmin,rmax) {
|
match (rmin, rmax) {
|
||||||
(Some(min),Some(max)) => {
|
(Some(min), Some(max)) => {
|
||||||
if r >= max {
|
if r >= max {
|
||||||
Some(U::max_value())
|
Some(U::max_value())
|
||||||
} else if r <= min {
|
} else if r <= min {
|
||||||
Some(U::min_value())
|
Some(U::min_value())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
num::NumCast::from(r)
|
num::NumCast::from(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use num::{Num,NumCast};
|
use num::{Num, NumCast};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -13,9 +12,8 @@ use thiserror::Error;
|
||||||
|
|
||||||
pub fn parse_int_set<T>(s: &str) -> Result<BTreeSet<T>, <T as std::str::FromStr>::Err>
|
pub fn parse_int_set<T>(s: &str) -> Result<BTreeSet<T>, <T as std::str::FromStr>::Err>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
|
|
||||||
let mut r: BTreeSet<T> = BTreeSet::new();
|
let mut r: BTreeSet<T> = BTreeSet::new();
|
||||||
let parts: Vec<&str> = s.split(',').collect();
|
let parts: Vec<&str> = s.split(',').collect();
|
||||||
for p in parts {
|
for p in parts {
|
||||||
|
|
@ -25,11 +23,11 @@ where
|
||||||
osep = s.find('-');
|
osep = s.find('-');
|
||||||
}
|
}
|
||||||
if let Some(sep) = osep {
|
if let Some(sep) = osep {
|
||||||
let (p1,p2) = (&s[..sep], &s[sep+1..] );
|
let (p1, p2) = (&s[..sep], &s[sep + 1..]);
|
||||||
let (low,high): (T,T) = ( p1.parse()?, p2.parse()? );
|
let (low, high): (T, T) = (p1.parse()?, p2.parse()?);
|
||||||
let (mut low,high) = match low <= high {
|
let (mut low, high) = match low <= high {
|
||||||
true => (low,high),
|
true => (low, high),
|
||||||
false => (high,low),
|
false => (high, low),
|
||||||
};
|
};
|
||||||
r.insert(low);
|
r.insert(low);
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -39,8 +37,7 @@ where
|
||||||
}
|
}
|
||||||
low += T::one();
|
low += T::one();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
r.insert(p.parse()?);
|
r.insert(p.parse()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,17 +45,17 @@ where
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SmartSet<T>
|
pub struct SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
pub set: BTreeSet<T>
|
pub set: BTreeSet<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SmartSet<T>
|
impl<T> SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -77,7 +74,7 @@ where
|
||||||
|
|
||||||
impl<T> From<T> for SmartSet<T>
|
impl<T> From<T> for SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy+NumCast + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + NumCast + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
fn from(i: T) -> Self {
|
fn from(i: T) -> Self {
|
||||||
SmartSet {
|
SmartSet {
|
||||||
|
|
@ -86,20 +83,20 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error,Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error<T>
|
pub enum Error<T>
|
||||||
where
|
where
|
||||||
T: std::fmt::Display,
|
T: std::fmt::Display,
|
||||||
{
|
{
|
||||||
#[error("invalid type: `{0}`, expected {1}")]
|
#[error("invalid type: `{0}`, expected {1}")]
|
||||||
Cast(T,String),
|
Cast(T, String),
|
||||||
#[error("unknown error")]
|
#[error("unknown error")]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait InternalTryFrom<T>
|
trait InternalTryFrom<T>
|
||||||
where
|
where
|
||||||
Self: Sized
|
Self: Sized,
|
||||||
{
|
{
|
||||||
type Error;
|
type Error;
|
||||||
fn i_try_from(other: T) -> Result<Self, Self::Error>;
|
fn i_try_from(other: T) -> Result<Self, Self::Error>;
|
||||||
|
|
@ -107,19 +104,17 @@ where
|
||||||
|
|
||||||
impl<T, U> InternalTryFrom<U> for SmartSet<T>
|
impl<T, U> InternalTryFrom<U> for SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy+NumCast + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + NumCast + std::str::FromStr + ops::AddAssign,
|
||||||
U: Num+Ord+Copy+num::ToPrimitive + std::str::FromStr + ops::AddAssign + std::fmt::Display,
|
U: Num + Ord + Copy + num::ToPrimitive + std::str::FromStr + ops::AddAssign + std::fmt::Display,
|
||||||
{
|
{
|
||||||
type Error = Error<U>;
|
type Error = Error<U>;
|
||||||
fn i_try_from(i: U) -> Result<Self, Self::Error> {
|
fn i_try_from(i: U) -> Result<Self, Self::Error> {
|
||||||
// let mut r = SmartSet::<T>::new();
|
// let mut r = SmartSet::<T>::new();
|
||||||
match num::NumCast::from(i) {
|
match num::NumCast::from(i) {
|
||||||
Some(v) => {
|
Some(v) => Ok(SmartSet {
|
||||||
Ok(SmartSet {
|
|
||||||
set: BTreeSet::from([v]),
|
set: BTreeSet::from([v]),
|
||||||
})
|
}),
|
||||||
}
|
_ => Err(Error::Cast(i, std::any::type_name::<T>().to_string())),
|
||||||
_ => Err(Error::Cast(i, std::any::type_name::<T>().to_string()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +136,7 @@ where
|
||||||
|
|
||||||
impl<T> FromStr for SmartSet<T>
|
impl<T> FromStr for SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
type Err = <T as FromStr>::Err;
|
type Err = <T as FromStr>::Err;
|
||||||
|
|
||||||
|
|
@ -154,7 +149,7 @@ where
|
||||||
|
|
||||||
impl<T> IntoIterator for SmartSet<T>
|
impl<T> IntoIterator for SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
type Item = T;
|
type Item = T;
|
||||||
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
|
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
|
||||||
|
|
@ -166,7 +161,7 @@ where
|
||||||
|
|
||||||
impl<'a, T> IntoIterator for &'a SmartSet<T>
|
impl<'a, T> IntoIterator for &'a SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
type Item = &'a T;
|
type Item = &'a T;
|
||||||
type IntoIter = std::collections::btree_set::Iter<'a, T>;
|
type IntoIter = std::collections::btree_set::Iter<'a, T>;
|
||||||
|
|
@ -176,25 +171,24 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use serde::de::{self,Deserialize, Deserializer, Visitor};
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
struct SmartSetVisitor<T>
|
struct SmartSetVisitor<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
marker: PhantomData<fn() -> SmartSet<T>>
|
marker: PhantomData<fn() -> SmartSet<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> SmartSetVisitor<T>
|
impl<T> SmartSetVisitor<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
|
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
|
||||||
{
|
{
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
marker: PhantomData
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +208,14 @@ macro_rules! visit_from {
|
||||||
|
|
||||||
impl<'de, T> Visitor<'de> for SmartSetVisitor<T>
|
impl<'de, T> Visitor<'de> for SmartSetVisitor<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy+NumCast + Deserialize<'de> + std::str::FromStr + ops::AddAssign + std::fmt::Debug,
|
T: Num
|
||||||
|
+ Ord
|
||||||
|
+ Copy
|
||||||
|
+ NumCast
|
||||||
|
+ Deserialize<'de>
|
||||||
|
+ std::str::FromStr
|
||||||
|
+ ops::AddAssign
|
||||||
|
+ std::fmt::Debug,
|
||||||
<T as FromStr>::Err: std::fmt::Display,
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
type Value = SmartSet<T>;
|
type Value = SmartSet<T>;
|
||||||
|
|
@ -223,7 +224,7 @@ where
|
||||||
formatter.write_str("a set of integer")
|
formatter.write_str("a set of integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
visit_from!{
|
visit_from! {
|
||||||
(visit_i8, i8),
|
(visit_i8, i8),
|
||||||
(visit_i16, i16),
|
(visit_i16, i16),
|
||||||
(visit_i32, i32),
|
(visit_i32, i32),
|
||||||
|
|
@ -250,12 +251,14 @@ where
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Ok(Some(value)) = seq.next_element::<String>() {
|
if let Ok(Some(value)) = seq.next_element::<String>() {
|
||||||
r.set.extend(&SmartSet::<T>::from_str(&value).map_err(serde::de::Error::custom)?.set);
|
r.set.extend(
|
||||||
}
|
&SmartSet::<T>::from_str(&value)
|
||||||
else if let Some(value) = seq.next_element()? {
|
.map_err(serde::de::Error::custom)?
|
||||||
|
.set,
|
||||||
|
);
|
||||||
|
} else if let Some(value) = seq.next_element()? {
|
||||||
r.set.insert(value);
|
r.set.insert(value);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,7 +270,14 @@ where
|
||||||
// This is the trait that informs Serde how to deserialize MyMap.
|
// This is the trait that informs Serde how to deserialize MyMap.
|
||||||
impl<'de, T> Deserialize<'de> for SmartSet<T>
|
impl<'de, T> Deserialize<'de> for SmartSet<T>
|
||||||
where
|
where
|
||||||
T: Num+Ord+Copy+NumCast + Deserialize<'de> + std::str::FromStr + ops::AddAssign + std::fmt::Debug,
|
T: Num
|
||||||
|
+ Ord
|
||||||
|
+ Copy
|
||||||
|
+ NumCast
|
||||||
|
+ Deserialize<'de>
|
||||||
|
+ std::str::FromStr
|
||||||
|
+ ops::AddAssign
|
||||||
|
+ std::fmt::Debug,
|
||||||
<T as FromStr>::Err: std::fmt::Display,
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue