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