Skip to content

Commit

Permalink
audio: rename AudioErrorResult to AudioResult and make `non_exhau…
Browse files Browse the repository at this point in the history
…stive` (#441)

Using the new `num_enum` `catch_all` we can now automatically implement
the `match` statement and get rid of one superfluous `AudioError`
variant.
  • Loading branch information
MarijnS95 authored Oct 14, 2023
1 parent a7ce644 commit 0792dba
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 56 deletions.
1 change: 1 addition & 0 deletions ndk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
- Add `DataSpace` type and relevant functions on `Bitmap` and `NativeWindow`. (#438)
- bitmap: Add `Bitmap::compress()` and `Bitmap::compress_raw()` functions. (#440)
- **Breaking:** Turn `BitmapError` into a `non_exhaustive` `enum`. (#440)
- **Breaking:** audio: Rename `AudioErrorResult` to `AudioResult` and turn into a `non_exhaustive` `enum`. (#441)

# 0.7.0 (2022-07-24)

Expand Down
95 changes: 41 additions & 54 deletions ndk/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::{
ptr::NonNull,
};

use num_enum::{IntoPrimitive, TryFromPrimitive};
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive};
use thiserror::Error;

use crate::utils::abort_on_panic;
Expand Down Expand Up @@ -353,9 +353,10 @@ pub enum AudioCallbackResult {
}

#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
#[non_exhaustive]
#[doc(alias = "aaudio_result_t")]
pub enum AudioErrorResult {
pub enum AudioResult {
#[doc(alias = "AAUDIO_ERROR_BASE")]
Base = ffi::AAUDIO_ERROR_BASE,
/// The audio device was disconnected. This could occur, for example, when headphones
Expand Down Expand Up @@ -409,58 +410,44 @@ pub enum AudioErrorResult {
/// The requested sample rate was not supported.
#[doc(alias = "AAUDIO_ERROR_INVALID_RATE")]
InvalidRate = ffi::AAUDIO_ERROR_INVALID_RATE,
// Use the OK discriminant, as no-one will be able to call `as i32` and only has access to the
// constants via `From` provided by `IntoPrimitive` which reads the contained value.
#[num_enum(catch_all)]
Unknown(i32) = ffi::AAUDIO_OK,
}

impl AudioErrorResult {
impl AudioResult {
#[doc(alias = "AAudio_convertStreamStateToText")]
pub fn to_text(self) -> Cow<'static, str> {
let ptr = unsafe {
CStr::from_ptr(ffi::AAudio_convertStreamStateToText(
self as ffi::aaudio_result_t,
))
};
let ptr = unsafe { CStr::from_ptr(ffi::AAudio_convertStreamStateToText(self.into())) };
ptr.to_string_lossy()
}

/// Returns [`Ok`] on [`ffi::AAUDIO_OK`], [`Err`] otherwise (including positive values).
///
/// Note that some known error codes (currently only for `AMediaCodec`) are positive.
pub(crate) fn from_result(status: ffi::aaudio_result_t) -> Result<(), Self> {
match status {
ffi::AAUDIO_OK => Ok(()),
x => Err(Self::from(x)),
}
}
}

#[derive(Debug, Error)]
pub enum AudioError {
#[error("error Audio result ({0:?})")]
ErrorResult(AudioErrorResult),
#[error("unknown AAudio error result ({0})")]
UnknownResult(i32),
ErrorResult(AudioResult),
#[error("unsupported AAudio result value received ({0})")]
UnsupportedValue(i32),
}

impl AudioError {
pub(crate) fn from_result<T>(
result: ffi::aaudio_result_t,
on_success: impl FnOnce() -> T,
) -> Result<T> {
use AudioErrorResult::*;
let result = match result {
value if value >= 0 => return Ok(on_success()),
ffi::AAUDIO_ERROR_BASE => Base,
ffi::AAUDIO_ERROR_DISCONNECTED => Disconnected,
ffi::AAUDIO_ERROR_ILLEGAL_ARGUMENT => IllegalArgument,
ffi::AAUDIO_ERROR_INTERNAL => Internal,
ffi::AAUDIO_ERROR_INVALID_STATE => InvalidState,
ffi::AAUDIO_ERROR_INVALID_HANDLE => InvalidHandle,
ffi::AAUDIO_ERROR_UNIMPLEMENTED => Unimplemented,
ffi::AAUDIO_ERROR_UNAVAILABLE => Unavailable,
ffi::AAUDIO_ERROR_NO_FREE_HANDLES => NoFreeHandles,
ffi::AAUDIO_ERROR_NO_MEMORY => NoMemory,
ffi::AAUDIO_ERROR_NULL => Null,
ffi::AAUDIO_ERROR_TIMEOUT => Timeout,
ffi::AAUDIO_ERROR_WOULD_BLOCK => WouldBlock,
ffi::AAUDIO_ERROR_INVALID_FORMAT => InvalidFormat,
ffi::AAUDIO_ERROR_OUT_OF_RANGE => OutOfRange,
ffi::AAUDIO_ERROR_NO_SERVICE => NoService,
ffi::AAUDIO_ERROR_INVALID_RATE => InvalidRate,
_ => return Err(AudioError::UnknownResult(result)),
};
Err(AudioError::ErrorResult(result))
/// Returns [`Ok`] on [`ffi::AAUDIO_OK`], [`Err`] otherwise (including positive values).
///
/// Note that some known error codes (currently only for `AMediaCodec`) are positive.
pub(crate) fn from_result(status: ffi::aaudio_result_t) -> Result<()> {
AudioResult::from_result(status).map_err(Self::ErrorResult)
}
}

Expand All @@ -469,7 +456,7 @@ pub type Result<T, E = AudioError> = std::result::Result<T, E>;
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> ffi::aaudio_result_t) -> Result<T> {
let mut result = MaybeUninit::uninit();
let status = with_ptr(result.as_mut_ptr());
AudioError::from_result(status, || unsafe { result.assume_init() })
AudioError::from_result(status).map(|()| unsafe { result.assume_init() })
}

fn enum_return_value<T: TryFrom<u32>>(return_value: i32) -> Result<T> {
Expand Down Expand Up @@ -754,7 +741,7 @@ impl AudioStreamBuilder {
data_callback: None,
error_callback: None,
};
let err = AudioError::from_result(error, || ()).unwrap_err();
let err = AudioError::from_result(error).unwrap_err();
(*callback)(&stream, err);
std::mem::forget(stream);
})
Expand Down Expand Up @@ -984,7 +971,7 @@ impl Drop for AudioStreamBuilder {
#[doc(alias = "AAudioStreamBuilder_delete")]
fn drop(&mut self) {
let status = unsafe { ffi::AAudioStreamBuilder_delete(self.as_ptr()) };
AudioError::from_result(status, || ()).unwrap();
AudioError::from_result(status).unwrap();
}
}

Expand Down Expand Up @@ -1209,12 +1196,12 @@ impl AudioStream {
/// It can also be used to align a recorded stream with a playback stream.
///
/// Timestamps are only valid when the stream is in `Started` state.
/// [`InvalidState`][AudioErrorResult::InvalidState] will be returned
/// [`InvalidState`][AudioResult::InvalidState] will be returned
/// if the stream is not started.
/// Note that because [`AudioStream::request_start()`] is asynchronous,
/// timestamps will not be valid until a short time after calling
/// [`AudioStream::request_start()`].
/// So [`InvalidState`][AudioErrorResult::InvalidState] should not be
/// So [`InvalidState`][AudioResult::InvalidState] should not be
/// considered a fatal error.
/// Just try calling again later.
///
Expand Down Expand Up @@ -1296,7 +1283,7 @@ impl AudioStream {
) -> Result<u32> {
let result = ffi::AAudioStream_read(self.as_ptr(), buffer, num_frames, timeout_nanoseconds);

AudioError::from_result(result, || result as u32)
AudioError::from_result(result).map(|()| result as u32)
}

/// Asynchronous request for the stream to flush.
Expand All @@ -1306,11 +1293,11 @@ impl AudioStream {
/// After this call the state will be in [`Flushing`][AudioStreamState::Flushing] or
/// [`Flushed`][AudioStreamState::Flushed].
///
/// This will return [`Unimplemented`][AudioErrorResult::Unimplemented] for input streams.
/// This will return [`Unimplemented`][AudioResult::Unimplemented] for input streams.
#[doc(alias = "AAudioStream_requestFlush")]
pub fn request_flush(&self) -> Result<()> {
let result = unsafe { ffi::AAudioStream_requestFlush(self.as_ptr()) };
AudioError::from_result(result, || ())
AudioError::from_result(result)
}

/// Asynchronous request for the stream to pause.
Expand All @@ -1319,12 +1306,12 @@ impl AudioStream {
/// After this call the state will be in [`Pausing`][AudioStreamState::Pausing] or
/// [`Paused`][AudioStreamState::Paused].
///
/// This will return [`Unimplemented`][AudioErrorResult::Unimplemented] for input streams.
/// This will return [`Unimplemented`][AudioResult::Unimplemented] for input streams.
/// For input streams use [`AudioStream::request_stop()`].
#[doc(alias = "AAudioStream_requestPause")]
pub fn request_pause(&self) -> Result<()> {
let result = unsafe { ffi::AAudioStream_requestPause(self.as_ptr()) };
AudioError::from_result(result, || ())
AudioError::from_result(result)
}

/// Asynchronously request to start playing the stream. For output streams, one should
Expand All @@ -1335,7 +1322,7 @@ impl AudioStream {
#[doc(alias = "AAudioStream_requestStart")]
pub fn request_start(&self) -> Result<()> {
let result = unsafe { ffi::AAudioStream_requestStart(self.as_ptr()) };
AudioError::from_result(result, || ())
AudioError::from_result(result)
}

/// Asynchronous request for the stream to stop.
Expand All @@ -1345,7 +1332,7 @@ impl AudioStream {
#[doc(alias = "AAudioStream_requestStop")]
pub fn request_stop(&self) -> Result<()> {
let result = unsafe { ffi::AAudioStream_requestStop(self.as_ptr()) };
AudioError::from_result(result, || ())
AudioError::from_result(result)
}

/// This can be used to adjust the latency of the buffer by changing
Expand All @@ -1366,7 +1353,7 @@ impl AudioStream {
#[doc(alias = "AAudioStream_setBufferSizeInFrames")]
pub fn set_buffer_size_in_frames(&self, num_frames: i32) -> Result<i32> {
let result = unsafe { ffi::AAudioStream_setBufferSizeInFrames(self.as_ptr(), num_frames) };
AudioError::from_result(result, || result)
AudioError::from_result(result).map(|()| result)
}

/// Wait until the current state no longer matches the input state.
Expand Down Expand Up @@ -1423,14 +1410,14 @@ impl AudioStream {
let result =
ffi::AAudioStream_write(self.as_ptr(), buffer, num_frames, timeout_nanoseconds);

AudioError::from_result(result, || result as u32)
AudioError::from_result(result).map(|()| result as u32)
}
}

impl Drop for AudioStream {
#[doc(alias = "AAudioStream_close")]
fn drop(&mut self) {
let status = unsafe { ffi::AAudioStream_close(self.as_ptr()) };
AudioError::from_result(status, || ()).unwrap();
AudioError::from_result(status).unwrap();
}
}
35 changes: 33 additions & 2 deletions ndk/src/media_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,69 @@ pub type Result<T, E = MediaError> = std::result::Result<T, E>;
/// Media Status codes for [`media_status_t`](https://developer.android.com/ndk/reference/group/media#group___media_1ga009a49041fe39f7bdc6d8b5cddbe760c)
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
#[doc(alias = "media_status_t")]
#[non_exhaustive]
pub enum MediaError {
#[doc(alias = "AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE")]
CodecErrorInsufficientResource = ffi::media_status_t::AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE.0,
#[doc(alias = "AMEDIACODEC_ERROR_RECLAIMED")]
CodecErrorReclaimed = ffi::media_status_t::AMEDIACODEC_ERROR_RECLAIMED.0,
#[doc(alias = "AMEDIA_ERROR_UNKNOWN")]
ErrorUnknown = ffi::media_status_t::AMEDIA_ERROR_UNKNOWN.0,
#[doc(alias = "AMEDIA_ERROR_MALFORMED")]
ErrorMalformed = ffi::media_status_t::AMEDIA_ERROR_MALFORMED.0,
#[doc(alias = "AMEDIA_ERROR_UNSUPPORTED")]
ErrorUnsupported = ffi::media_status_t::AMEDIA_ERROR_UNSUPPORTED.0,
#[doc(alias = "AMEDIA_ERROR_INVALID_OBJECT")]
ErrorInvalidObject = ffi::media_status_t::AMEDIA_ERROR_INVALID_OBJECT.0,
#[doc(alias = "AMEDIA_ERROR_INVALID_PARAMETER")]
ErrorInvalidParameter = ffi::media_status_t::AMEDIA_ERROR_INVALID_PARAMETER.0,
#[doc(alias = "AMEDIA_ERROR_INVALID_OPERATION")]
ErrorInvalidOperation = ffi::media_status_t::AMEDIA_ERROR_INVALID_OPERATION.0,
#[doc(alias = "AMEDIA_ERROR_END_OF_STREAM")]
ErrorEndOfStream = ffi::media_status_t::AMEDIA_ERROR_END_OF_STREAM.0,
#[doc(alias = "AMEDIA_ERROR_IO")]
ErrorIo = ffi::media_status_t::AMEDIA_ERROR_IO.0,
#[doc(alias = "AMEDIA_ERROR_WOULD_BLOCK")]
ErrorWouldBlock = ffi::media_status_t::AMEDIA_ERROR_WOULD_BLOCK.0,
#[doc(alias = "AMEDIA_DRM_ERROR_BASE")]
DrmErrorBase = ffi::media_status_t::AMEDIA_DRM_ERROR_BASE.0,
#[doc(alias = "AMEDIA_DRM_NOT_PROVISIONED")]
DrmNotProvisioned = ffi::media_status_t::AMEDIA_DRM_NOT_PROVISIONED.0,
#[doc(alias = "AMEDIA_DRM_RESOURCE_BUSY")]
DrmResourceBusy = ffi::media_status_t::AMEDIA_DRM_RESOURCE_BUSY.0,
#[doc(alias = "AMEDIA_DRM_DEVICE_REVOKED")]
DrmDeviceRevoked = ffi::media_status_t::AMEDIA_DRM_DEVICE_REVOKED.0,
#[doc(alias = "AMEDIA_DRM_SHORT_BUFFER")]
DrmShortBuffer = ffi::media_status_t::AMEDIA_DRM_SHORT_BUFFER.0,
#[doc(alias = "AMEDIA_DRM_SESSION_NOT_OPENED")]
DrmSessionNotOpened = ffi::media_status_t::AMEDIA_DRM_SESSION_NOT_OPENED.0,
#[doc(alias = "AMEDIA_DRM_TAMPER_DETECTED")]
DrmTamperDetected = ffi::media_status_t::AMEDIA_DRM_TAMPER_DETECTED.0,
#[doc(alias = "AMEDIA_DRM_VERIFY_FAILED")]
DrmVerifyFailed = ffi::media_status_t::AMEDIA_DRM_VERIFY_FAILED.0,
#[doc(alias = "AMEDIA_DRM_NEED_KEY")]
DrmNeedKey = ffi::media_status_t::AMEDIA_DRM_NEED_KEY.0,
#[doc(alias = "AMEDIA_DRM_LICENSE_EXPIRED")]
DrmLicenseExpired = ffi::media_status_t::AMEDIA_DRM_LICENSE_EXPIRED.0,
#[doc(alias = "AMEDIA_IMGREADER_ERROR_BASE")]
ImgreaderErrorBase = ffi::media_status_t::AMEDIA_IMGREADER_ERROR_BASE.0,
#[doc(alias = "AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE")]
ImgreaderNoBufferAvailable = ffi::media_status_t::AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE.0,
#[doc(alias = "AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED")]
ImgreaderMaxImagesAcquired = ffi::media_status_t::AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED.0,
#[doc(alias = "AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE")]
ImgreaderCannotLockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE.0,
#[doc(alias = "AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE")]
ImgreaderCannotUnlockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE.0,
#[doc(alias = "AMEDIA_IMGREADER_IMAGE_NOT_LOCKED")]
ImgreaderImageNotLocked = ffi::media_status_t::AMEDIA_IMGREADER_IMAGE_NOT_LOCKED.0,
// Use the OK discriminant, assuming no-one calls `as i32` and only uses the generated `From` implementation via `IntoPrimitive`
/// This error code is unknown to the [`ndk`][crate] crate. Please report an issue if you
/// believe this code needs to be added to our mapping.
// Use the OK discriminant, as no-one will be able to call `as i32` and only has access to the
// constants via `From` provided by `IntoPrimitive` which reads the contained value.
#[num_enum(catch_all)]
Unknown(i32) = 0,
Unknown(i32) = ffi::media_status_t::AMEDIA_OK.0,
}

impl fmt::Display for MediaError {
Expand Down

0 comments on commit 0792dba

Please sign in to comment.