use {
crate::{
os::{OsMidiOutput,OsMidiInput},
makepad_live_id::{LiveId, FromLiveId},
}
};
#[derive(Clone, Debug)]
pub struct MidiPortsEvent {
pub descs: Vec<MidiPortDesc>,
}
impl MidiPortsEvent {
pub fn all_inputs(&self) -> Vec<MidiPortId> {
let mut out = Vec::new();
for d in &self.descs {
if d.port_type.is_input() {
out.push(d.port_id);
}
}
out
}
pub fn all_outputs(&self) -> Vec<MidiPortId> {
let mut out = Vec::new();
for d in &self.descs {
if d.port_type.is_output() {
out.push(d.port_id);
}
}
out
}
}
impl std::fmt::Display for MidiPortsEvent {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "MIDI ports:\n").unwrap();
for desc in &self.descs {
if desc.port_type.is_input() {
write!(f, "[Input] {}\n", desc.name).unwrap()
}
else {
write!(f, "[Output] {}\n", desc.name).unwrap()
}
}
Ok(())
}
}
impl std::fmt::Debug for MidiPortDesc {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_tuple("name").field(&self.name).finish()
}
}
#[derive(Default)]
pub struct MidiInput(pub (crate) Option<OsMidiInput>);
unsafe impl Send for MidiInput {}
impl MidiInput {
pub fn receive(&mut self) -> Option<(MidiPortId, MidiData)> {
self.0.as_mut().unwrap().receive()
}
}
pub struct MidiOutput(pub (crate) Option<OsMidiOutput>);
unsafe impl Send for MidiOutput {}
impl MidiOutput {
pub fn send(&self, port: Option<MidiPortId>, data: MidiData) {
let output = self.0.as_ref().unwrap();
output.send(port, data);
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MidiData {
pub data: [u8; 3],
}
impl std::convert::From<u32> for MidiData {
fn from(data: u32) -> Self {
MidiData {
data: [((data >> 16) & 0xff) as u8, ((data >> 8) & 0xff) as u8, ((data >> 0) & 0xff) as u8]
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MidiPortType {
Input,
Output,
}
impl MidiPortType {
pub fn is_input(&self) -> bool {
match self {
Self::Input => true,
_ => false
}
}
pub fn is_output(&self) -> bool {
match self {
Self::Output => true,
_ => false
}
}
}
#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
pub struct MidiPortId(pub LiveId);
#[derive(Clone, PartialEq)]
pub struct MidiPortDesc {
pub name: String,
pub port_id: MidiPortId,
pub port_type: MidiPortType,
}
#[derive(Clone, Copy, Debug)]
pub struct MidiNote {
pub is_on: bool,
pub channel: u8,
pub note_number: u8,
pub velocity: u8,
}
impl Into<MidiData> for MidiNote {
fn into(self) -> MidiData {
MidiData {
data: [
(if self.is_on {0x9}else {0x8} << 4) | self.channel,
self.note_number,
self.velocity
]
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MidiAftertouch {
pub channel: u8,
pub note_number: u8,
pub velocity: u8
}
impl Into<MidiData> for MidiAftertouch {
fn into(self) -> MidiData {
MidiData {
data: [
0xA0 | self.channel,
self.note_number,
self.velocity
]
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MidiControlChange {
pub channel: u8,
pub param: u8,
pub value: u8,
}
impl Into<MidiData> for MidiControlChange {
fn into(self) -> MidiData {
MidiData {
data: [
0xB0 | self.channel,
self.param,
self.value
]
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MidiProgramChange {
pub channel: u8,
pub hi: u8,
pub lo: u8
}
impl Into<MidiData> for MidiProgramChange {
fn into(self) -> MidiData {
MidiData {
data: [
0xC0 | self.channel,
self.hi,
self.lo
]
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MidiChannelAftertouch {
pub channel: u8,
pub value: u16
}
impl Into<MidiData> for MidiChannelAftertouch {
fn into(self) -> MidiData {
MidiData {
data: [
0xD0 | self.channel,
(((self.value as u32)>>7)&0x7f) as u8,
((self.value as u32)&0x7f) as u8,
]
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MidiPitchBend {
pub channel: u8,
pub bend: u16,
}
impl Into<MidiData> for MidiPitchBend {
fn into(self) -> MidiData {
MidiData {
data: [
0xE0 | self.channel,
(((self.bend as u32)>>7)&0x7f) as u8,
((self.bend as u32)&0x7f) as u8,
]
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MidiSystem {
pub channel: u8,
pub hi: u8,
pub lo: u8
}
impl Into<MidiData> for MidiSystem {
fn into(self) -> MidiData {
MidiData {
data: [
0xF0 | self.channel,
self.hi,
self.lo
]
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum MidiEvent {
Note(MidiNote),
Aftertouch(MidiAftertouch),
ControlChange(MidiControlChange),
ProgramChange(MidiProgramChange),
PitchBend(MidiPitchBend),
ChannelAftertouch(MidiChannelAftertouch),
System(MidiSystem),
Unknown(MidiData)
}
impl MidiEvent {
pub fn on_note(&self) -> Option<MidiNote> {
match self {
Self::Note(note) => Some(*note),
_ => None
}
}
}
impl MidiData {
pub fn status(&self) -> u8 {
self.data[0] >> 4
}
pub fn channel(&self) -> u8 {
self.data[0] & 0xf
}
pub fn decode(&self) -> MidiEvent {
let status = self.status();
let channel = self.channel();
match status {
0x8 | 0x9 => MidiEvent::Note(MidiNote {
is_on: status == 0x9,
channel,
note_number: self.data[1],
velocity: self.data[2]
}),
0xA => MidiEvent::Aftertouch(MidiAftertouch {
channel,
note_number: self.data[1],
velocity: self.data[2],
}),
0xB => MidiEvent::ControlChange(MidiControlChange {
channel,
param: self.data[1],
value: self.data[2]
}),
0xC => MidiEvent::ProgramChange(MidiProgramChange {
channel,
hi: self.data[1],
lo: self.data[2]
}),
0xD => MidiEvent::ChannelAftertouch(MidiChannelAftertouch {
channel,
value: ((self.data[1] as u16) << 7) | self.data[2] as u16,
}),
0xE => MidiEvent::PitchBend(MidiPitchBend {
channel,
bend: ((self.data[1] as u16) << 7) | self.data[2] as u16,
}),
0xF => MidiEvent::System(MidiSystem {
channel,
hi: self.data[1],
lo: self.data[2]
}),
_ => MidiEvent::Unknown(*self)
}
}
}