use jack_sys as j;
use std::marker::PhantomData;
use std::{mem, slice};
use crate::{Error, Frames, Port, PortFlags, PortSpec, ProcessScope};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RawMidi<'a> {
pub time: Frames,
pub bytes: &'a [u8],
}
#[derive(Copy, Clone, Debug, Default)]
pub struct MidiIn {
_internal: (),
}
#[derive(Copy, Clone, Debug, Default)]
pub struct MidiOut {
_internal: (),
}
unsafe impl PortSpec for MidiIn {
fn jack_port_type(&self) -> &'static str {
j::RAW_MIDI_TYPE
}
fn jack_flags(&self) -> PortFlags {
PortFlags::IS_INPUT
}
fn jack_buffer_size(&self) -> libc::c_ulong {
0
}
}
impl Port<MidiIn> {
pub fn iter<'a>(&'a self, ps: &'a ProcessScope) -> MidiIter<'a> {
assert_eq!(self.client_ptr(), ps.client_ptr());
MidiIter {
buffer: unsafe { self.buffer(ps.n_frames()) },
index: 0,
_phantom: PhantomData,
}
}
}
#[derive(Debug, Clone)]
pub struct MidiIter<'a> {
buffer: *mut ::libc::c_void,
index: usize,
_phantom: PhantomData<&'a ()>,
}
unsafe impl Sync for MidiIter<'_> {}
impl<'a> MidiIter<'a> {
pub fn peek(&self) -> Option<RawMidi<'a>> {
self.absolute_nth(self.index as u32)
}
pub fn next_if<P>(&mut self, predicate: P) -> Option<RawMidi<'a>>
where
P: FnOnce(RawMidi) -> bool,
{
if self.peek().map(predicate).unwrap_or(false) {
self.next()
} else {
None
}
}
fn absolute_nth(&self, n: u32) -> Option<RawMidi<'a>> {
let mut ev = mem::MaybeUninit::<j::jack_midi_event_t>::uninit();
let res = unsafe { j::jack_midi_event_get(ev.as_mut_ptr(), self.buffer, n) };
if res != 0 {
return None;
}
let ev = unsafe { ev.assume_init() };
let time = ev.time;
let bytes: &[u8] = unsafe { slice::from_raw_parts(ev.buffer as *const u8, ev.size) };
Some(RawMidi { time, bytes })
}
fn absolute_len(&self) -> usize {
if self.buffer.is_null() {
0
} else {
unsafe { j::jack_midi_get_event_count(self.buffer) as usize }
}
}
}
impl<'a> Iterator for MidiIter<'a> {
type Item = RawMidi<'a>;
fn next(&mut self) -> Option<Self::Item> {
let ret = self.peek();
self.index += 1;
ret
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.absolute_len() - self.index;
(len, Some(len))
}
fn count(self) -> usize {
self.absolute_len() - self.index
}
fn last(self) -> Option<Self::Item> {
let len = self.absolute_len() as u32;
if len == 0 || self.index >= len as usize {
None
} else {
self.absolute_nth(len - 1)
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.index += n;
self.next()
}
}
unsafe impl PortSpec for MidiOut {
fn jack_port_type(&self) -> &'static str {
j::RAW_MIDI_TYPE
}
fn jack_flags(&self) -> PortFlags {
PortFlags::IS_OUTPUT
}
fn jack_buffer_size(&self) -> libc::c_ulong {
0
}
}
impl Port<MidiOut> {
pub fn writer<'a>(&'a mut self, ps: &'a ProcessScope) -> MidiWriter<'a> {
assert_eq!(self.client_ptr(), ps.client_ptr());
let buffer = unsafe { self.buffer(ps.n_frames()) };
unsafe { j::jack_midi_clear_buffer(buffer) };
MidiWriter {
buffer,
_phantom: PhantomData,
}
}
}
#[derive(Debug)]
pub struct MidiWriter<'a> {
buffer: *mut ::libc::c_void,
_phantom: PhantomData<&'a ()>,
}
impl MidiWriter<'_> {
pub fn write(&mut self, message: &RawMidi) -> Result<(), Error> {
let ev = j::jack_midi_event_t {
time: message.time,
size: message.bytes.len(),
buffer: message.bytes.as_ptr() as *mut u8,
};
let res = unsafe { j::jack_midi_event_write(self.buffer, ev.time, ev.buffer, ev.size) };
match -res {
0 => Ok(()),
libc::ENOBUFS => Err(Error::NotEnoughSpace),
error_code => Err(Error::UnknownError { error_code }),
}
}
pub fn lost_count(&self) -> usize {
let n = unsafe { j::jack_midi_get_lost_event_count(self.buffer) };
n as usize
}
pub fn max_event_size(&self) -> usize {
unsafe { j::jack_midi_max_event_size(self.buffer) }
}
}