-
Notifications
You must be signed in to change notification settings - Fork 115
ndk: Add AMidi interface #353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
paxbun
wants to merge
56
commits into
rust-mobile:master
Choose a base branch
from
paxbun:feat/amidi
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+615
−1
Open
Changes from 2 commits
Commits
Show all changes
56 commits
Select commit
Hold shift + click to select a range
2daf09c
ndk: Add AMidi interface
paxbun 9456589
ndk: Remove needless explicit lifetimes in midi.rs
paxbun 9cf8581
ndk: Fix mismatching doc comments in midi.rs
paxbun 9f7c9ef
ndk: Remove get_ prefix from midi.rs
paxbun 466bf4a
ndk: Fix doc comments of MidiOutputPort::receive
paxbun 4013f68
ndk-sys: Add feature `midi`
paxbun ff6697c
ndk: Add feature `midi`
paxbun 02630b0
ndk: Make Midi types implement Send
paxbun d3d616e
ndk: Update ndk/src/midi.rs
paxbun 4f53506
ndk: Fix mismatching doc comments in midi.rs
paxbun 15b917f
ndk: Fix link to ndk-samples with a permalink
paxbun 5ae1cad
ndk: Replace try_from_primitives in midi.rs with try_from
paxbun 06ebda5
ndk: Make MidiOutputPort::receive return timestamp only with MidiOpco…
paxbun 2c92479
ndk: Add ptr exposing functions to wrappers in midi.rs
paxbun b2ec4ea
ndk: Raise error when 2 or more msgs received in MidiOutputPort
paxbun 6f262cc
ndk: Remove as_ptr() from structs in midi.rs
paxbun 01877bc
ndk: Add more strict opcode checks to MidiOutputPort
paxbun 48b4416
ndk: Make msg for unexpected opcode in midi.rs more clear
paxbun aa26247
ndk: Fix vague doc comments in midi.rs
paxbun 8235a5c
ndk: Add mod media_error
paxbun 12df844
ndk: Remove pub use for MediaErrorResult from media/mod.rs
paxbun 34357c7
ndk: Remove pub use from media/mod.rs
paxbun 23b18b0
ndk: Reflect midi.rs changes to CHANGELOG.md
paxbun c79c8c5
fix: Make MidiDevice::from_java unsafe
paxbun 895350b
Merge branch 'master' into feat/amidi
paxbun acce571
fix(ndk): Remove all occurrences of ffi::size_t from midi.rs
paxbun 8900c1c
fix(ndk): Remove unnecessary casts from midi.rs
paxbun 95220e7
ndk: Rework media error types
MarijnS95 85288eb
Merge branch 'ndk-rework-media-error' into feat/amidi
paxbun cc1c2e3
fix(ndk): Reflect changes in media_error to midi
paxbun 200ab56
Merge branch 'master' into feat/amidi
paxbun 408e460
Update comments in ndk/src/midi.rs
paxbun 9a5744b
Update ndk/src/midi.rs
paxbun 412b6ad
Update ndk/src/midi.rs
paxbun aa4467e
Update ndk/CHANGELOG.md
paxbun 3cfaad4
fix: Remove impl Send for MidiDevice
paxbun 2e036d2
Fix comments in ndk/src/media_error.rs
paxbun aa79a19
ndk: Make "midi" feature dependent on "media"
paxbun 0341b4d
fix: Remove redundant Result mapping from midi.rs
paxbun c8b2b2e
ndk: Add MidiDeviceType::Unknown
paxbun 6044fc2
ndk: Add doc-comments to MidiInputPort
paxbun f22054d
ndk: Add doc-comments to fields of MidiOpcode::Data
paxbun 734b81b
ndk: Reference the Java Android MIDI docs in the doc-comments
paxbun 7220a9a
ndk: Fix doc-comments of MidiOutputPort::receive
paxbun 241b096
ndk: Drop Send for ndk::midi::Midi*Port
paxbun d9ef310
ndk: Describe about Java VM thread attachment in the safety section o…
paxbun 31159dc
ndk: Add safe wrapper of midi
paxbun 129edc2
ndk: Fix typos in ndk::midi::safe
paxbun 1ea5b32
Merge branch 'master' into feat/amidi
paxbun 83fe4b2
ndk: Fix clippy warnings in ndk::midi
paxbun 8c64160
ndk: Make examples in ndk::midi compilable
paxbun 5428ca7
ndk: Remove link to SafeMidiDeviceBox from the module-level doc-comme…
paxbun 802ab4a
ndk: Add more detailed description about safety of AMidi functions to…
paxbun 300118e
ndk: Add missing brackets to links to functions in doc-comments in nd…
paxbun 8086a7d
ndk: Enable media_error when "midi" is enabled
paxbun 5707555
Merge remote-tracking branch 'origin/master' into feat/amidi
paxbun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
//! Bindings for [`AMidiDevice`], [`AMidiInputPort`], and [`AMidiOutputPort`] | ||
//! | ||
//! See [the NDK guide](https://developer.android.com/ndk/guides/audio/midi) for | ||
//! design and usage instructions, and [the NDK reference](https://developer.android.com/ndk/reference/group/midi) | ||
//! for an API overview. | ||
//! | ||
//! [`AMidiDevice`]: https://developer.android.com/ndk/reference/group/midi#amididevice | ||
//! [`AMidiInputPort`]: https://developer.android.com/ndk/reference/group/midi#amidiinputport | ||
//! [`AMidiOutputPort`]: https://developer.android.com/ndk/reference/group/midi#amidioutputport | ||
#![cfg(feature = "media")] | ||
|
||
pub use super::media::Result; | ||
use super::media::{construct, NdkMediaError}; | ||
|
||
use num_enum::{IntoPrimitive, TryFromPrimitive}; | ||
use std::fmt; | ||
use std::marker::PhantomData; | ||
use std::os::raw::{c_int, c_uint}; | ||
use std::ptr::NonNull; | ||
|
||
/// Result of [`MidiOutputPort::receive`]. | ||
paxbun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[derive(Copy, Clone, Debug)] | ||
#[repr(u32)] | ||
pub enum MidiOpcode { | ||
/// No MIDI messages are available. | ||
NoMessage, | ||
/// Received a MIDI message with the given length. | ||
Data(usize), | ||
/// Instructed to discard all pending MIDI data. | ||
Flush, | ||
} | ||
|
||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] | ||
#[repr(u32)] | ||
pub enum MidiDeviceType { | ||
Bluetooth = ffi::AMIDI_DEVICE_TYPE_BLUETOOTH, | ||
USB = ffi::AMIDI_DEVICE_TYPE_USB, | ||
Virtual = ffi::AMIDI_DEVICE_TYPE_VIRTUAL, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct MidiDevice { | ||
inner: NonNull<ffi::AMidiDevice>, | ||
} | ||
|
||
impl MidiDevice { | ||
fn from_ptr(inner: NonNull<ffi::AMidiDevice>) -> Self { | ||
Self { inner } | ||
} | ||
|
||
fn as_ptr(&self) -> *mut ffi::AMidiDevice { | ||
self.inner.as_ptr() | ||
} | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// Connects a native Midi Device object to the associated Java MidiDevice object. | ||
/// | ||
/// Use this AMidiDevice to access the rest of the native MIDI API. | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub fn from_java( | ||
env: *mut jni_sys::JNIEnv, | ||
midi_device_obj: jni_sys::jobject, | ||
) -> Result<MidiDevice> { | ||
unsafe { | ||
let ptr = construct(|res| ffi::AMidiDevice_fromJava(env, midi_device_obj, res))?; | ||
Ok(Self::from_ptr(NonNull::new_unchecked(ptr))) | ||
} | ||
} | ||
|
||
/// Gets the number of input (sending) ports available on the specified MIDI device. | ||
pub fn get_num_input_ports(&self) -> Result<usize> { | ||
let num_input_ports = unsafe { ffi::AMidiDevice_getNumInputPorts(self.as_ptr()) }; | ||
if num_input_ports >= 0 { | ||
Ok(num_input_ports as usize) | ||
} else { | ||
NdkMediaError::from_status(ffi::media_status_t(num_input_ports as c_int)).map(|_| 0) | ||
} | ||
} | ||
|
||
/// Gets the number of output (receiving) ports available on the specified MIDI device. | ||
pub fn get_num_output_ports(&self) -> Result<usize> { | ||
let num_output_ports = unsafe { ffi::AMidiDevice_getNumOutputPorts(self.as_ptr()) }; | ||
if num_output_ports >= 0 { | ||
Ok(num_output_ports as usize) | ||
} else { | ||
Err( | ||
NdkMediaError::from_status(ffi::media_status_t(num_output_ports as c_int)) | ||
.unwrap_err(), | ||
) | ||
} | ||
} | ||
|
||
/// Gets the MIDI device type. | ||
pub fn get_type(&self) -> Result<MidiDeviceType> { | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let device_type = unsafe { ffi::AMidiDevice_getType(self.as_ptr()) }; | ||
if device_type >= 0 { | ||
let device_type = | ||
MidiDeviceType::try_from_primitive(device_type as u32).map_err(|e| { | ||
paxbun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
NdkMediaError::UnknownResult(ffi::media_status_t(e.number as c_int)) | ||
})?; | ||
Ok(device_type) | ||
} else { | ||
Err(NdkMediaError::from_status(ffi::media_status_t(device_type)).unwrap_err()) | ||
} | ||
} | ||
|
||
/// Opens the input port so that the client can send data to it. | ||
pub fn open_input_port(&self, port_number: i32) -> Result<MidiInputPort> { | ||
unsafe { | ||
let input_port = | ||
construct(|res| ffi::AMidiInputPort_open(self.as_ptr(), port_number, res))?; | ||
Ok(MidiInputPort::from_ptr(NonNull::new_unchecked(input_port))) | ||
} | ||
} | ||
|
||
/// Opens the output port so that the client can receive data from it. | ||
pub fn open_output_port(&self, port_number: i32) -> Result<MidiOutputPort> { | ||
unsafe { | ||
let output_port = | ||
construct(|res| ffi::AMidiOutputPort_open(self.as_ptr(), port_number, res))?; | ||
Ok(MidiOutputPort::from_ptr(NonNull::new_unchecked( | ||
output_port, | ||
))) | ||
} | ||
} | ||
} | ||
|
||
impl Drop for MidiDevice { | ||
fn drop(&mut self) { | ||
let status = unsafe { ffi::AMidiDevice_release(self.as_ptr()) }; | ||
NdkMediaError::from_status(status).unwrap(); | ||
} | ||
} | ||
|
||
pub struct MidiInputPort<'a> { | ||
inner: NonNull<ffi::AMidiInputPort>, | ||
_marker: PhantomData<&'a MidiDevice>, | ||
} | ||
|
||
impl<'a> fmt::Debug for MidiInputPort<'a> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("MidiInputPort") | ||
.field("inner", &self.inner) | ||
.finish() | ||
} | ||
} | ||
|
||
impl<'a> MidiInputPort<'a> { | ||
fn from_ptr(inner: NonNull<ffi::AMidiInputPort>) -> Self { | ||
Self { | ||
inner, | ||
_marker: PhantomData, | ||
} | ||
} | ||
|
||
fn as_ptr(&self) -> *mut ffi::AMidiInputPort { | ||
self.inner.as_ptr() | ||
} | ||
|
||
/// Sends data to the specified input port. | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub fn send(&self, buffer: &[u8]) -> Result<usize> { | ||
let num_bytes_sent = unsafe { | ||
ffi::AMidiInputPort_send(self.as_ptr(), buffer.as_ptr(), buffer.len() as ffi::size_t) | ||
}; | ||
if num_bytes_sent >= 0 { | ||
Ok(num_bytes_sent as usize) | ||
} else { | ||
Err( | ||
NdkMediaError::from_status(ffi::media_status_t(num_bytes_sent as c_int)) | ||
.unwrap_err(), | ||
) | ||
} | ||
} | ||
|
||
/// Sends a message with a 'MIDI flush command code' to the specified port. | ||
/// | ||
/// This should cause a receiver to discard any pending MIDI data it may have accumulated and | ||
/// not processed. | ||
pub fn send_flush(&self) -> Result<()> { | ||
let result = unsafe { ffi::AMidiInputPort_sendFlush(self.as_ptr()) }; | ||
NdkMediaError::from_status(result) | ||
} | ||
|
||
pub fn send_with_timestamp(&self, buffer: &[u8], timestamp: i64) -> Result<usize> { | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let num_bytes_sent = unsafe { | ||
ffi::AMidiInputPort_sendWithTimestamp( | ||
self.as_ptr(), | ||
buffer.as_ptr(), | ||
buffer.len() as ffi::size_t, | ||
timestamp, | ||
) | ||
}; | ||
if num_bytes_sent >= 0 { | ||
Ok(num_bytes_sent as usize) | ||
} else { | ||
Err( | ||
NdkMediaError::from_status(ffi::media_status_t(num_bytes_sent as c_int)) | ||
.unwrap_err(), | ||
) | ||
} | ||
} | ||
} | ||
|
||
impl<'a> Drop for MidiInputPort<'a> { | ||
fn drop(&mut self) { | ||
unsafe { ffi::AMidiInputPort_close(self.as_ptr()) }; | ||
} | ||
} | ||
|
||
pub struct MidiOutputPort<'a> { | ||
inner: NonNull<ffi::AMidiOutputPort>, | ||
_marker: PhantomData<&'a MidiDevice>, | ||
} | ||
|
||
impl<'a> fmt::Debug for MidiOutputPort<'a> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("MidiOutputPort") | ||
.field("inner", &self.inner) | ||
.finish() | ||
} | ||
} | ||
|
||
impl<'a> MidiOutputPort<'a> { | ||
fn from_ptr(inner: NonNull<ffi::AMidiOutputPort>) -> Self { | ||
Self { | ||
inner, | ||
_marker: PhantomData, | ||
} | ||
} | ||
|
||
fn as_ptr(&self) -> *mut ffi::AMidiOutputPort { | ||
self.inner.as_ptr() | ||
} | ||
|
||
/// Receives the next pending MIDI message. | ||
/// | ||
/// To retrieve all pending messages, the client should repeatedly call this method until it | ||
/// returns `Some(_)`. | ||
/// | ||
/// Note that this is a non-blocking call. If there are no Midi messages are available, the | ||
/// function returns `None` immediately (for 0 messages received). | ||
pub fn receive(&self, buffer: &mut [u8]) -> Result<(MidiOpcode, i64)> { | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let mut opcode = 0i32; | ||
let mut timestamp = 0i64; | ||
let mut num_bytes_received: ffi::size_t = 0; | ||
let result = unsafe { | ||
ffi::AMidiOutputPort_receive( | ||
self.as_ptr(), | ||
&mut opcode, | ||
buffer.as_mut_ptr(), | ||
buffer.len() as ffi::size_t, | ||
&mut num_bytes_received, | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
&mut timestamp, | ||
) | ||
}; | ||
|
||
if result < 0 { | ||
Err(NdkMediaError::from_status(ffi::media_status_t(result as c_int)).unwrap_err()) | ||
} else if result == 0 { | ||
MarijnS95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ok((MidiOpcode::NoMessage, timestamp)) | ||
} else if opcode as c_uint == ffi::AMIDI_OPCODE_DATA { | ||
Ok((MidiOpcode::Data(num_bytes_received as usize), timestamp)) | ||
} else { | ||
Ok((MidiOpcode::Flush, timestamp)) | ||
} | ||
} | ||
} | ||
|
||
impl<'a> Drop for MidiOutputPort<'a> { | ||
fn drop(&mut self) { | ||
unsafe { ffi::AMidiOutputPort_close(self.as_ptr()) }; | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.