Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"37f745acc58963e23a84c92062f1c4756d836d0a7065901716393ba8c70f6c63","Cargo.toml":"a07c2bab87490c0a4bdbbe012c6e50a0429a39eb6b91aac9464e405a8ff15e0f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.md":"8c4745e3b80c37344049dd53a957eef2d1568a371197b336fef546202450d210","examples/feedback.rs":"658fcf9c501af8496e140e36ca80274464f802aea2471cb32253695f4543dbd0","examples/feedback_interleaved.rs":"3535254406c25cf1379157ae669675307f5089fe01843fe9a86254f2b53f38f9","examples/sine.rs":"ebbe1e277ff861eed02100626410eea6ad7eb3ec78130b8029e940283a1fa1d3","examples/sine_advanced.rs":"aa103ba72ea776f58322bdce3b5fb52fa0a481233b26070b137862fc8b85d702","src/audio_unit/audio_format.rs":"eba9078d42888bf72999e4e9c054ddfa95c59110a7c488ae6f9d26e6899ff7ed","src/audio_unit/macos_helpers.rs":"b6b9a82ee1f862e60d36f46b3a1639f2ff1a3d571ba37b482d035d9baa9cc14f","src/audio_unit/mod.rs":"c764e74335c63c25b0472db355ed62f63372412a726349bfc9540b8fbc440209","src/audio_unit/render_callback.rs":"e5a3ad8bed9c40d8e1397f5d8af1523acb835284a6ec5e1f061bb8c7e00a98ae","src/audio_unit/sample_format.rs":"7e1f68f80bba141e125dc9d38fe035c9a279d3aa653b080c82e995449833ffef","src/audio_unit/stream_format.rs":"0718538c0e1193cdb372ca370d97bf4ea407854c4923327d511c8d5c1d639b03","src/audio_unit/types.rs":"8ba9ffae8b15bcbf8cfaec81e3e2957de69f90c581675cb50b6b3f7dab84cf1f","src/error.rs":"aef09a20881811833b624f2fb35eb493bb95c29f16c6e125715bfbf68493bad9","src/lib.rs":"9d262c8b3a2a0642e09f348e829cc23a932d65b376773c62953fc09939d058c8"},"package":"321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace"}

254
vendor/coreaudio-rs/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,254 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "bindgen"
version = "0.68.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
dependencies = [
"bitflags 2.4.0",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "coreaudio-rs"
version = "0.11.3"
dependencies = [
"bitflags 1.3.2",
"core-foundation-sys",
"coreaudio-sys",
]
[[package]]
name = "coreaudio-sys"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8478e5bdad14dce236b9898ea002eabfa87cbe14f0aa538dbe3b6a4bec4332d"
dependencies = [
"bindgen",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "proc-macro2"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaac441002f822bc9705a681810a4dd2963094b9ca0ddc41cb963a4c189189ea"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5011c7e263a695dc8ca064cddb722af1be54e517a280b12a5356f98366899e5d"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "shlex"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

66
vendor/coreaudio-rs/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,66 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "coreaudio-rs"
version = "0.11.3"
authors = [
"mitchmindtree <mitchell.nordine@gmail.com>",
"yupferris <jake@fusetools.com>",
]
description = "A friendly rust interface for Apple's CoreAudio API."
homepage = "https://github.com/RustAudio/coreaudio-rs"
readme = "README.md"
keywords = [
"core",
"audio",
"unit",
"osx",
"ios",
]
license = "MIT/Apache-2.0"
repository = "https://github.com/RustAudio/coreaudio-rs.git"
[package.metadata.docs.rs]
all-features = true
default-target = "x86_64-apple-darwin"
targets = [
"x86_64-apple-darwin",
"x86_64-apple-ios",
]
[lib]
name = "coreaudio"
[dependencies.bitflags]
version = "1.0"
[dependencies.core-foundation-sys]
version = "0.8.3"
[dependencies.coreaudio-sys]
version = "0.2"
default-features = false
[features]
audio_toolbox = ["coreaudio-sys/audio_toolbox"]
audio_unit = ["coreaudio-sys/audio_unit"]
core_audio = ["coreaudio-sys/core_audio"]
core_midi = ["coreaudio-sys/core_midi"]
default = [
"audio_toolbox",
"audio_unit",
"core_audio",
"open_al",
"core_midi",
]
open_al = ["coreaudio-sys/open_al"]

201
vendor/coreaudio-rs/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
vendor/coreaudio-rs/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2015
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

7
vendor/coreaudio-rs/README.md vendored Normal file
View File

@@ -0,0 +1,7 @@
# coreaudio-rs [![Actions Status](https://github.com/rustaudio/coreaudio-rs/workflows/coreaudio-rs/badge.svg)](https://github.com/rustaudio/coreaudio-rs/actions) [![Crates.io](https://img.shields.io/crates/v/coreaudio-rs.svg)](https://crates.io/crates/coreaudio-rs) [![Crates.io](https://img.shields.io/crates/l/coreaudio-rs.svg)](https://github.com/RustAudio/coreaudio-rs/blob/master/LICENSE-MIT) [![docs.rs](https://docs.rs/coreaudio-rs/badge.svg)](https://docs.rs/coreaudio-rs/)
A friendly rust interface for [Apple's Core Audio API](https://developer.apple.com/library/ios/documentation/MusicAudio/Conceptual/CoreAudioOverview/CoreAudioEssentials/CoreAudioEssentials.html).
This crate aims to expose and wrap the functionality of the original C API in a zero-cost, safe, Rust-esque manner.
If you just want direct access to the unsafe bindings, use [coreaudio-sys](https://crates.io/crates/coreaudio-sys).

139
vendor/coreaudio-rs/examples/feedback.rs vendored Normal file
View File

@@ -0,0 +1,139 @@
//! A basic input + output stream example, copying the mic input stream to the default output stream
extern crate coreaudio;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
use coreaudio::audio_unit::macos_helpers::{audio_unit_from_device_id, get_default_device_id};
use coreaudio::audio_unit::render_callback::{self, data};
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
use coreaudio::sys::*;
const SAMPLE_RATE: f64 = 44100.0;
type S = f32;
const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
// type S = i32; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I32;
// type S = i16; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
fn main() -> Result<(), coreaudio::Error> {
let mut input_audio_unit =
audio_unit_from_device_id(get_default_device_id(true).unwrap(), true)?;
let mut output_audio_unit =
audio_unit_from_device_id(get_default_device_id(false).unwrap(), false)?;
let format_flag = match SAMPLE_FORMAT {
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
LinearPcmFlags::IS_SIGNED_INTEGER
}
_ => {
unimplemented!("Other formats are not implemented for this example.");
}
};
// Using IS_NON_INTERLEAVED everywhere because data::Interleaved is commented out / not implemented
let in_stream_format = StreamFormat {
sample_rate: SAMPLE_RATE,
sample_format: SAMPLE_FORMAT,
flags: format_flag | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
// audio_unit.set_input_callback is hardcoded to 1 buffer, and when using non_interleaved
// we are forced to 1 channel
channels: 1,
};
let out_stream_format = StreamFormat {
sample_rate: SAMPLE_RATE,
sample_format: SAMPLE_FORMAT,
flags: format_flag | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
// you can change this to 1
channels: 2,
};
println!("input={:#?}", &in_stream_format);
println!("output={:#?}", &out_stream_format);
println!("input_asbd={:#?}", &in_stream_format.to_asbd());
println!("output_asbd={:#?}", &out_stream_format.to_asbd());
let id = kAudioUnitProperty_StreamFormat;
let asbd = in_stream_format.to_asbd();
input_audio_unit.set_property(id, Scope::Output, Element::Input, Some(&asbd))?;
let asbd = out_stream_format.to_asbd();
output_audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
let buffer_left = Arc::new(Mutex::new(VecDeque::<S>::new()));
let producer_left = buffer_left.clone();
let consumer_left = buffer_left.clone();
let buffer_right = Arc::new(Mutex::new(VecDeque::<S>::new()));
let producer_right = buffer_right.clone();
let consumer_right = buffer_right.clone();
// seed roughly 1 second of data to create a delay in the feedback loop for easier testing
for buffer in vec![buffer_left, buffer_right] {
let mut buffer = buffer.lock().unwrap();
for _ in 0..(out_stream_format.sample_rate as i32) {
buffer.push_back(0 as S);
}
}
type Args = render_callback::Args<data::NonInterleaved<S>>;
input_audio_unit.set_input_callback(move |args| {
let Args {
num_frames,
mut data,
..
} = args;
// Print the number of frames the callback provides.
// Included to aid understanding, don't use println and other things
// that may block for an unknown amount of time inside the callback
// of a real application.
println!("input cb {} frames", num_frames);
let buffer_left = producer_left.lock().unwrap();
let buffer_right = producer_right.lock().unwrap();
let mut buffers = vec![buffer_left, buffer_right];
for i in 0..num_frames {
for (ch, channel) in data.channels_mut().enumerate() {
let value: S = channel[i];
buffers[ch].push_back(value);
}
}
Ok(())
})?;
input_audio_unit.start()?;
output_audio_unit.set_render_callback(move |args: Args| {
let Args {
num_frames,
mut data,
..
} = args;
// Print the number of frames the callback requests.
// Included to aid understanding, don't use println and other things
// that may block for an unknown amount of time inside the callback
// of a real application.
println!("output cb {} frames", num_frames);
let buffer_left = consumer_left.lock().unwrap();
let buffer_right = consumer_right.lock().unwrap();
let mut buffers = vec![buffer_left, buffer_right];
for i in 0..num_frames {
// Default other channels to copy value from first channel as a fallback
let zero: S = 0 as S;
let f: S = *buffers[0].front().unwrap_or(&zero);
for (ch, channel) in data.channels_mut().enumerate() {
let sample: S = buffers[ch].pop_front().unwrap_or(f);
channel[i] = sample;
}
}
Ok(())
})?;
output_audio_unit.start()?;
std::thread::sleep(std::time::Duration::from_millis(100000));
Ok(())
}

View File

@@ -0,0 +1,152 @@
//! A basic input + output stream example, copying the mic input stream to the default output stream
extern crate coreaudio;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
use coreaudio::audio_unit::macos_helpers::{
audio_unit_from_device_id, get_default_device_id, get_device_name, RateListener,
};
use coreaudio::audio_unit::render_callback::{self, data};
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
use coreaudio::sys::*;
const SAMPLE_RATE: f64 = 44100.0;
type S = f32;
const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
// type S = i32; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I32;
// type S = i16; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
fn main() -> Result<(), coreaudio::Error> {
let input_device_id = get_default_device_id(true).unwrap();
let output_device_id = get_default_device_id(false).unwrap();
println!(
"Input device: {}",
get_device_name(input_device_id).unwrap()
);
println!(
"Output device: {}",
get_device_name(output_device_id).unwrap()
);
let mut input_audio_unit = audio_unit_from_device_id(input_device_id, true)?;
let mut output_audio_unit = audio_unit_from_device_id(output_device_id, false)?;
let format_flag = match SAMPLE_FORMAT {
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
}
_ => {
unimplemented!("Please use one of the packed formats");
}
};
let in_stream_format = StreamFormat {
sample_rate: SAMPLE_RATE,
sample_format: SAMPLE_FORMAT,
flags: format_flag,
channels: 2,
};
let out_stream_format = StreamFormat {
sample_rate: SAMPLE_RATE,
sample_format: SAMPLE_FORMAT,
flags: format_flag,
channels: 2,
};
println!("input={:#?}", &in_stream_format);
println!("output={:#?}", &out_stream_format);
println!("input_asbd={:#?}", &in_stream_format.to_asbd());
println!("output_asbd={:#?}", &out_stream_format.to_asbd());
let id = kAudioUnitProperty_StreamFormat;
let asbd = in_stream_format.to_asbd();
input_audio_unit.set_property(id, Scope::Output, Element::Input, Some(&asbd))?;
let asbd = out_stream_format.to_asbd();
output_audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
let buffer_left = Arc::new(Mutex::new(VecDeque::<S>::new()));
let producer_left = buffer_left.clone();
let consumer_left = buffer_left.clone();
let buffer_right = Arc::new(Mutex::new(VecDeque::<S>::new()));
let producer_right = buffer_right.clone();
let consumer_right = buffer_right.clone();
// Register a rate listener for playback
let mut listener_pb = RateListener::new(output_device_id, None);
listener_pb.register()?;
// Register a rate listener for capture
let mut listener_cap = RateListener::new(input_device_id, None);
listener_cap.register()?;
// seed roughly 1 second of data to create a delay in the feedback loop for easier testing
for buffer in vec![buffer_left, buffer_right] {
let mut buffer = buffer.lock().unwrap();
for _ in 0..(out_stream_format.sample_rate as i32) {
buffer.push_back(0 as S);
}
}
type Args = render_callback::Args<data::Interleaved<S>>;
input_audio_unit.set_input_callback(move |args| {
let Args {
num_frames, data, ..
} = args;
// Print the number of frames the callback requests.
// Included to aid understanding, don't use println and other things
// that may block for an unknown amount of time inside the callback
// of a real application.
println!("input cb {} frames", num_frames);
let buffer_left = producer_left.lock().unwrap();
let buffer_right = producer_right.lock().unwrap();
let mut buffers = vec![buffer_left, buffer_right];
for i in 0..num_frames {
for channel in 0..2 {
let value: S = data.buffer[2 * i + channel];
buffers[channel].push_back(value);
}
}
Ok(())
})?;
input_audio_unit.start()?;
output_audio_unit.set_render_callback(move |args: Args| {
let Args {
num_frames, data, ..
} = args;
// Print the number of frames the callback requests.
println!("output cb {} frames", num_frames);
let buffer_left = consumer_left.lock().unwrap();
let buffer_right = consumer_right.lock().unwrap();
let mut buffers = vec![buffer_left, buffer_right];
for i in 0..num_frames {
// Default other channels to copy value from first channel as a fallback
let zero: S = 0 as S;
let f: S = *buffers[0].front().unwrap_or(&zero);
for channel in 0..2 {
let sample: S = buffers[channel].pop_front().unwrap_or(f);
data.buffer[2 * i + channel] = sample;
}
}
Ok(())
})?;
output_audio_unit.start()?;
for _ in 0..1000 {
std::thread::sleep(std::time::Duration::from_millis(100));
if listener_cap.get_nbr_values() > 0 {
println!("capture rate change: {:?}", listener_cap.drain_values());
}
if listener_pb.get_nbr_values() > 0 {
println!("playback rate change: {:?}", listener_pb.drain_values());
}
}
Ok(())
}

73
vendor/coreaudio-rs/examples/sine.rs vendored Normal file
View File

@@ -0,0 +1,73 @@
//! A basic output stream example, using an Output AudioUnit to generate a sine wave.
extern crate coreaudio;
use coreaudio::audio_unit::render_callback::{self, data};
use coreaudio::audio_unit::{AudioUnit, IOType, SampleFormat};
use std::f64::consts::PI;
struct SineWaveGenerator {
time: f64,
/// generated frequency in Hz
freq: f64,
/// magnitude of generated signal
volume: f64,
}
impl SineWaveGenerator {
fn new(freq: f64, volume: f64) -> Self {
SineWaveGenerator {
time: 0.,
freq,
volume,
}
}
}
impl Iterator for SineWaveGenerator {
type Item = f32;
fn next(&mut self) -> Option<f32> {
self.time += 1. / 44_100.;
let output = ((self.freq * self.time * PI * 2.).sin() * self.volume) as f32;
Some(output)
}
}
fn main() -> Result<(), coreaudio::Error> {
let frequency_hz = 440.;
let volume = 0.15;
let mut samples = SineWaveGenerator::new(frequency_hz, volume);
// Construct an Output audio unit that delivers audio to the default output device.
let mut audio_unit = AudioUnit::new(IOType::DefaultOutput)?;
// Read the input format. This is counterintuitive, but it's the format used when sending
// audio data to the AudioUnit representing the output device. This is separate from the
// format the AudioUnit later uses to send the data to the hardware device.
let stream_format = audio_unit.input_stream_format()?;
println!("{:#?}", &stream_format);
// For this example, our sine wave expects `f32` data.
assert!(SampleFormat::F32 == stream_format.sample_format);
type Args = render_callback::Args<data::NonInterleaved<f32>>;
audio_unit.set_render_callback(move |args| {
let Args {
num_frames,
mut data,
..
} = args;
for i in 0..num_frames {
let sample = samples.next().unwrap();
for channel in data.channels_mut() {
channel[i] = sample;
}
}
Ok(())
})?;
audio_unit.start()?;
std::thread::sleep(std::time::Duration::from_millis(3000));
Ok(())
}

View File

@@ -0,0 +1,217 @@
//! An output stream example showing more advanced usage.
//! Tries to use hog mode to get exclusive access to the device.
extern crate coreaudio;
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
use coreaudio::audio_unit::macos_helpers::{
audio_unit_from_device_id, find_matching_physical_format, get_default_device_id,
get_hogging_pid, get_supported_physical_stream_formats, set_device_physical_stream_format,
set_device_sample_rate, toggle_hog_mode, AliveListener, RateListener,
};
use coreaudio::audio_unit::render_callback::{self, data};
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
use coreaudio::sys::kAudioUnitProperty_StreamFormat;
use std::f64::consts::PI;
use std::process;
const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
// type S = i32; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I32;
// type S = i16; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
const SAMPLE_RATE: f64 = 44100.0;
const INTERLEAVED: bool = true;
struct SineWaveGenerator {
time: f64,
/// generated frequency in Hz
freq: f64,
/// magnitude of generated signal
volume: f64,
}
impl SineWaveGenerator {
fn new(freq: f64, volume: f64) -> Self {
SineWaveGenerator {
time: 0.,
freq,
volume,
}
}
}
impl Iterator for SineWaveGenerator {
type Item = f32;
fn next(&mut self) -> Option<f32> {
self.time += 1. / SAMPLE_RATE;
let output = ((self.freq * self.time * PI * 2.).sin() * self.volume) as f32;
Some(output)
}
}
fn main() -> Result<(), coreaudio::Error> {
let frequency_hz_l = 1000.;
let frequency_hz_r = 1200.;
let volume = 0.95;
let mut samples_l = SineWaveGenerator::new(frequency_hz_l, volume);
let mut samples_r = SineWaveGenerator::new(frequency_hz_r, volume);
// Construct an Output audio unit that delivers audio to the default output device.
let audio_unit_id = get_default_device_id(false).unwrap();
let mut audio_unit = audio_unit_from_device_id(audio_unit_id, false)?;
let pid = get_hogging_pid(audio_unit_id)?;
if pid != -1 {
println!("Device is owned by another process with pid {}!", pid);
} else {
println!("Device is free, trying to get exclusive access..");
let new_pid = toggle_hog_mode(audio_unit_id)?;
let process_id = process::id();
if new_pid == process_id as i32 {
println!("We have exclusive access.");
} else {
println!(
"Could not get exclusive access. Process pid: {}, new pid value: {}",
process_id, new_pid
);
}
}
let mut format_flag = match SAMPLE_FORMAT {
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
}
_ => {
unimplemented!("Please use one of the packed formats");
}
};
if !INTERLEAVED {
format_flag = format_flag | LinearPcmFlags::IS_NON_INTERLEAVED;
}
let stream_format = StreamFormat {
sample_rate: SAMPLE_RATE,
sample_format: SAMPLE_FORMAT,
flags: format_flag,
// you can change this to 1
channels: 2,
};
println!("stream format={:#?}", &stream_format);
println!("asbd={:#?}", &stream_format.to_asbd());
// Lets print all supported formats, disabled for now since it often crashes.
println!("All supported formats");
let formats = get_supported_physical_stream_formats(audio_unit_id)?;
for fmt in formats {
println!("{:?}", &fmt);
}
// set the sample rate. This isn't actually needed since the sample rate
// will anyway be changed when setting the sample format later.
// Keeping it here as an example.
//println!("set device sample rate");
//set_device_sample_rate(audio_unit_id, SAMPLE_RATE)?;
println!("setting hardware (physical) format");
let hw_stream_format = StreamFormat {
sample_rate: SAMPLE_RATE,
sample_format: SampleFormat::I16,
flags: LinearPcmFlags::empty(),
channels: 2,
};
let hw_asbd = find_matching_physical_format(audio_unit_id, hw_stream_format)
.ok_or(coreaudio::Error::UnsupportedStreamFormat)?;
println!("asbd: {:?}", hw_asbd);
// Note that using a StreamFormat here is convenient, but it only supports a few sample formats.
// Setting the format to for example 24 bit integers requires using an ASBD.
set_device_physical_stream_format(audio_unit_id, hw_asbd)?;
println!("write audio unit StreamFormat property");
let id = kAudioUnitProperty_StreamFormat;
let asbd = stream_format.to_asbd();
audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
// For this example, our sine wave expects `f32` data.
assert!(SampleFormat::F32 == stream_format.sample_format);
// Register rate and alive listeners
let mut rate_listener = RateListener::new(audio_unit_id, None);
rate_listener.register()?;
let mut alive_listener = AliveListener::new(audio_unit_id);
alive_listener.register()?;
if INTERLEAVED {
println!("Register interleaved callback");
type Args = render_callback::Args<data::Interleaved<f32>>;
audio_unit.set_render_callback(move |args| {
let Args {
num_frames, data, ..
} = args;
// Print the number of frames the callback requests.
// Included to aid understanding, don't use println and other things
// that may block for an unknown amount of time inside the callback
// of a real application.
println!("frames: {}", num_frames);
for i in 0..num_frames {
let sample_l = samples_l.next().unwrap();
let sample_r = samples_r.next().unwrap();
data.buffer[2 * i] = sample_l;
data.buffer[2 * i + 1] = sample_r;
}
Ok(())
})?;
} else {
println!("Register non-interleaved callback");
type Args = render_callback::Args<data::NonInterleaved<f32>>;
audio_unit.set_render_callback(move |args| {
let Args {
num_frames,
mut data,
..
} = args;
for i in 0..num_frames {
let sample_l = samples_l.next().unwrap();
let sample_r = samples_r.next().unwrap();
let mut channels = data.channels_mut();
let left = channels.next().unwrap();
left[i] = sample_l;
let right = channels.next().unwrap();
right[i] = sample_r;
}
Ok(())
})?;
}
audio_unit.start()?;
for _ in 0..100 {
std::thread::sleep(std::time::Duration::from_millis(100));
// print all sample change events
println!("rate events: {:?}", rate_listener.copy_values());
println!("alive state: {}", alive_listener.is_alive());
}
// Release exclusive access, not really needed as the process exits anyway after this.
let owner_pid = get_hogging_pid(audio_unit_id)?;
let process_id = process::id();
if owner_pid == process_id as i32 {
println!("Releasing exclusive access");
let new_pid = toggle_hog_mode(audio_unit_id)?;
if new_pid == -1 {
println!("Exclusive access released.");
} else {
println!(
"Could not release exclusive access. Process pid: {}, new pid value: {}",
process_id, new_pid
);
}
}
Ok(())
}

View File

@@ -0,0 +1,488 @@
//! Typification of the various AudioFormat codes and flags offered by the Core Audio API.
//!
//! See the Core Audio Data Types Reference
//! [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Data_Format_Identifiers) for more info.
use std::os::raw::c_uint;
/// A type-safe representation of both the `AudioFormatId` and their associated flags.
#[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum AudioFormat {
/// Linear PCM; a non-compressed audio data format with one frame per packet.
///
/// **Available** in OS X v10.0 and later.
LinearPCM(LinearPcmFlags), // = 1819304813,
/// An AC-3 codec.
///
/// **Available** in OS X v10.2 and later.
AC3, // = 1633889587,
/// AC-3 codec that provides data packaged for transport over an IEC 60958 compliant digital
/// audio interface.
///
/// **Available** in OS X v10.2 and later.
F60958AC3(StandardFlags), // = 1667326771,
/// Apple's implementation of the IMA 4:1 ADPCM codec.
///
/// **Available** in OS X v10.2 and later.
AppleIMA4, // = 1768775988,
/// MPEG-4 AAC codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4AAC(Mpeg4ObjectId), // = 1633772320,
/// MPEG-4 CELP codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4CELP(Mpeg4ObjectId), // = 1667591280,
/// MPEG-4 HVXC codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4HVXC(Mpeg4ObjectId), // = 1752594531,
/// MPEG-4 TwinVQ codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4TwinVQ(Mpeg4ObjectId), // = 1953986161,
/// MACE 3:1.
///
/// **Available** in OS X v10.3 and later.
MACE3, // = 1296122675,
/// MACE 6:1.
///
/// **Available** in OS X v10.3 and later.
MACE6, // = 1296122678,
/// μLaw 2:1.
///
/// **Available** in OS X v10.3 and later.
ULaw, // = 1970037111,
/// aLaw 2:1.
///
/// **Available** in OS X v10.3 and later.
ALaw, // = 1634492791,
/// QDesign Music.
///
/// **Available** in OS X v10.3 and later.
QDesign, // = 1363430723,
/// QDesign2 Music.
///
/// **Available** in OS X v10.3 and later.
QDesign2, // = 1363430706,
/// QUALCOMM PureVoice.
///
/// **Available** in OS X v10.3 and later.
QUALCOMM, // = 1365470320,
/// MPEG-1/2, Layer 1 audio.
///
/// **Available** in OS X v10.3 and later.
MPEGLayer1, // = 778924081,
/// MPEG-1/2, Layer 2 audio.
///
/// **Available** in OS X v10.3 and later.
MPEGLayer2, // = 778924082,
/// MPEG-1/2, Layer 3 audio.
///
/// **Available** in OS X v10.3 and later.
MPEGLayer3, // = 778924083,
/// A stream of IOAudioTimeStamp structures.
///
/// **Available** in OS X v10.2 and later.
TimeCode(AudioTimeStampFlags), // = 1953066341,
/// A stream of MIDIPacketList structures where the time stamps in the MIDIPacket structures
/// are sample offsets in the stream. The `sample_rate` field in the **StreamFormat** structure
/// is used to describe how time is passed in this kind of stream.
///
/// An audio unit that receives or generates this stream can use this sample rate together with
/// the number of frames it is rendering.
///
/// The sample offsets within the MIDIPacketList can be used to define the time for any MIDI
/// event within the list.
///
/// **Available** in OS X v10.2 and later.
///
/// TODO: Review whether or not this audio format should indicate some fundamental change
/// within the **StreamFormat**.
MIDIStream, // = 1835623529,
/// A "side-chain" of f32 data that can be fed or generated by an audio unit and that is used
/// to send a high density of parameter value control information.
///
/// An audio unit typically runs a parameter value stream at either the sample rate of the
/// audio unit's audio data, or some integer quotient of this (i.e. a half or a third of the
/// sample rate of the audio).
///
/// The `sample_rate` field in the **StreamFormat** type describes this relationship.
///
/// **Available** in OS X v10.2 and later.
ParameterValueStream, // = 1634760307,
/// Apple Lossless format.
///
/// **Available** in OS X v10.3 and later.
AppleLossless(AppleLosslessFlags), // = 1634492771,
/// MPEG-4 High Efficiency AAC audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_HE, // = 1633772392,
/// MPEG-4 AAC Low Delay audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_LD, // = 1633772396,
/// MPEG-4 AAC Enhanced Low Delay audio object.
///
/// **Available** in OS X v10.7 and later.
MPEG4AAC_ELD, // = 1633772389,
/// MPEG-4 AAC Enhanced Low Delay audio object with SBR (spectral band replication) extension
/// layer.
///
/// **Available** in OS X v10.7 and later.
MPEG4AAC_ELD_SBR, // = 1633772390,
MPEG4AAC_ELD_V2, // = 1633772391,
/// MPEG-4 High Efficiency AAC Version 2 audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_HE_V2, // = 1633772400,
/// MPEG-4 Apatial Audio audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_Spatial, // = 1633772403,
/// The AMR (adaptive Multi-Rate) narrow band speech codec.
///
/// **Available** in OS X v10.5 and later.
AMR, // = 1935764850,
AMR_WB, // = 1935767394,
/// The codec used for Audible, Inc. audio books.
///
/// **Available** in OS X v10.6 and later.
Audible, // = 1096107074,
/// The iLBC (internet Low Bitrate Codec) narrow band cpeech codec.
///
/// **Available** in OS X v10.6 and later.
iLBC, // = 1768710755,
/// DVI/Intel IMA ADPCM - ACM code 17.
///
/// **Available** in OS X v10.6 and later.
DVIIntelIMA, // = 1836253201,
/// Microsoft GSM 6.10 - ACM code 49.
///
/// **Available** in OS X v10.6 and later.
MicrosoftGSM, // = 1836253233,
/// The format defined by the AES3-2003 standard.
///
/// Adopted into MXF and MPEG-2 containers and SDTI transport streams with SMPTE specs
/// 203M-2002 and 331M-2000.
AES3, // = 1634038579,
}
impl AudioFormat {
/// Convert from the FFI C format and flags to a typesafe Rust enum representation.
pub fn from_format_and_flag(format: c_uint, flag: Option<u32>) -> Option<AudioFormat> {
match (format, flag) {
(1819304813, Some(i)) => Some(AudioFormat::LinearPCM(
LinearPcmFlags::from_bits_truncate(i),
)),
(1633889587, _) => Some(AudioFormat::AC3),
(1667326771, Some(i)) => {
Some(AudioFormat::F60958AC3(StandardFlags::from_bits_truncate(i)))
}
(1768775988, _) => Some(AudioFormat::AppleIMA4),
(1633772320, Some(i)) => Some(AudioFormat::MPEG4AAC(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1667591280, Some(i)) => Some(AudioFormat::MPEG4CELP(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1752594531, Some(i)) => Some(AudioFormat::MPEG4HVXC(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1953986161, Some(i)) => Some(AudioFormat::MPEG4TwinVQ(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1296122675, _) => Some(AudioFormat::MACE3),
(1296122678, _) => Some(AudioFormat::MACE6),
(1970037111, _) => Some(AudioFormat::ULaw),
(1634492791, _) => Some(AudioFormat::ALaw),
(1363430723, _) => Some(AudioFormat::QDesign),
(1363430706, _) => Some(AudioFormat::QDesign2),
(1365470320, _) => Some(AudioFormat::QUALCOMM),
(778924081, _) => Some(AudioFormat::MPEGLayer1),
(778924082, _) => Some(AudioFormat::MPEGLayer2),
(778924083, _) => Some(AudioFormat::MPEGLayer3),
(1953066341, Some(i)) => Some(AudioFormat::TimeCode(
AudioTimeStampFlags::from_bits_truncate(i),
)),
(1835623529, _) => Some(AudioFormat::MIDIStream),
(1634760307, _) => Some(AudioFormat::ParameterValueStream),
(1634492771, Some(i)) => Some(AudioFormat::AppleLossless(
AppleLosslessFlags::from_bits_truncate(i),
)),
(1633772392, _) => Some(AudioFormat::MPEG4AAC_HE),
(1633772396, _) => Some(AudioFormat::MPEG4AAC_LD),
(1633772389, _) => Some(AudioFormat::MPEG4AAC_ELD),
(1633772390, _) => Some(AudioFormat::MPEG4AAC_ELD_SBR),
(1633772391, _) => Some(AudioFormat::MPEG4AAC_ELD_V2),
(1633772400, _) => Some(AudioFormat::MPEG4AAC_HE_V2),
(1633772403, _) => Some(AudioFormat::MPEG4AAC_Spatial),
(1935764850, _) => Some(AudioFormat::AMR),
(1935767394, _) => Some(AudioFormat::AMR_WB),
(1096107074, _) => Some(AudioFormat::Audible),
(1768710755, _) => Some(AudioFormat::iLBC),
(1836253201, _) => Some(AudioFormat::DVIIntelIMA),
(1836253233, _) => Some(AudioFormat::MicrosoftGSM),
(1634038579, _) => Some(AudioFormat::AES3),
_ => None,
}
}
/// Convert from the Rust enum to the C format and flag.
pub fn as_format_and_flag(&self) -> (c_uint, Option<u32>) {
match *self {
AudioFormat::LinearPCM(flag) => (1819304813, Some(flag.bits())),
AudioFormat::AC3 => (1633889587, None),
AudioFormat::F60958AC3(flag) => (1667326771, Some(flag.bits())),
AudioFormat::AppleIMA4 => (1768775988, None),
AudioFormat::MPEG4AAC(flag) => (1633772320, Some(flag as u32)),
AudioFormat::MPEG4CELP(flag) => (1667591280, Some(flag as u32)),
AudioFormat::MPEG4HVXC(flag) => (1752594531, Some(flag as u32)),
AudioFormat::MPEG4TwinVQ(flag) => (1953986161, Some(flag as u32)),
AudioFormat::MACE3 => (1296122675, None),
AudioFormat::MACE6 => (1296122678, None),
AudioFormat::ULaw => (1970037111, None),
AudioFormat::ALaw => (1634492791, None),
AudioFormat::QDesign => (1363430723, None),
AudioFormat::QDesign2 => (1363430706, None),
AudioFormat::QUALCOMM => (1365470320, None),
AudioFormat::MPEGLayer1 => (778924081, None),
AudioFormat::MPEGLayer2 => (778924082, None),
AudioFormat::MPEGLayer3 => (778924083, None),
AudioFormat::TimeCode(flag) => (1953066341, Some(flag.bits())),
AudioFormat::MIDIStream => (1835623529, None),
AudioFormat::ParameterValueStream => (1634760307, None),
AudioFormat::AppleLossless(flag) => (1634492771, Some(flag.bits())),
AudioFormat::MPEG4AAC_HE => (1633772392, None),
AudioFormat::MPEG4AAC_LD => (1633772396, None),
AudioFormat::MPEG4AAC_ELD => (1633772389, None),
AudioFormat::MPEG4AAC_ELD_SBR => (1633772390, None),
AudioFormat::MPEG4AAC_ELD_V2 => (1633772391, None),
AudioFormat::MPEG4AAC_HE_V2 => (1633772400, None),
AudioFormat::MPEG4AAC_Spatial => (1633772403, None),
AudioFormat::AMR => (1935764850, None),
AudioFormat::AMR_WB => (1935767394, None),
AudioFormat::Audible => (1096107074, None),
AudioFormat::iLBC => (1768710755, None),
AudioFormat::DVIIntelIMA => (1836253201, None),
AudioFormat::MicrosoftGSM => (1836253233, None),
AudioFormat::AES3 => (1634038579, None),
}
}
}
bitflags! {
/// Standard flags for use in the **F60958AC3** **AudioFormat** variant.
///
/// Note: In the original Core Audio API these are consolidated with what we have named the
/// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
/// have chosen to separate these for greater type safety and clearer compatibility with
/// the **AudioFormat** type.
///
/// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
pub struct StandardFlags: u32 {
/// Set for floating point, clear for integer.
///
/// **Available** in OS X v10.2 and later.
const IS_FLOAT = 1;
/// Set for big endian, clear for little endian.
///
/// **Available** in OS X v10.2 and later.
const IS_BIG_ENDIAN = 2;
/// Set for signed integer, clear for unsigned integer.
///
/// Note: This is only valid if `IS_FLOAT` is clear.
///
/// **Available** in OS X v10.2 and later.
const IS_SIGNED_INTEGER = 4;
/// Set if the sample bits occupy the entire available bits for the channel, clear if they
/// are high- or low-aligned within the channel.
///
/// **Available** in OS X v10.2 and later.
const IS_PACKED = 8;
/// Set if the sample bits are placed into the high bits of the channel, clear for low bit
/// placement.
///
/// Note: This is only valid if `IS_PACKED` is clear.
///
/// **Available** in OS X v10.2 and later.
const IS_ALIGNED_HIGH = 16;
/// Set if the sample for each channel are located contiguously and the channels are laid
/// out end to end.
///
/// Clear if the samples for each frame are laid out contiguously and the frames laid out
/// end to end.
///
/// **Available** in OS X v10.2 and later.
const IS_NON_INTERLEAVED = 32;
/// Set to indicate when a format is nonmixable.
///
/// Note: that this flag is only used when interacting with the HAL's stream format
/// information. It is **not** valid for any other use.
///
/// **Available** in OS X v10.3 and later.
const IS_NON_MIXABLE = 64;
}
}
bitflags! {
/// Flags for use within the **LinearPCM** **AudioFormat**.
///
/// Note: In the original Core Audio API these are consolidated with what we have named the
/// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
/// have chosen to separate these for greater type safety and clearer compatibility with
/// the **AudioFormat** type.
///
/// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
pub struct LinearPcmFlags: u32 {
/// Synonmyn for the **IS_FLOAT** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_FLOAT = 1;
/// Synonmyn for the **IS_BIG_ENDIAN** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_BIG_ENDIAN = 2;
/// Synonmyn for the **IS_SIGNED_INTEGER** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_SIGNED_INTEGER = 4;
/// Synonmyn for the **IS_PACKED** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_PACKED = 8;
/// Synonmyn for the **IS_ALIGNED_HIGH** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_ALIGNED_HIGH = 16;
/// Synonmyn for the **IS_NON_INTERLEAVED** **StandardFlags**.
///
/// **Available** in OS X v10.2 and later.
const IS_NON_INTERLEAVED = 32;
/// Synonmyn for the **IS_NON_MIXABLE** **StandardFlags**.
///
/// **Available** in OS X v10.3 and later.
const IS_NON_MIXABLE = 64;
/// The linear PCM flags contain a 6-bit bitfield indicating that an integer format is to
/// be interpreted as fixed point.
///
/// The value indicates the number of bits are used to represent the fractional portion of
/// each sample value.
///
/// This constant indicates the bit position (counting from the right) of the bitfield in
/// `mFormatFlags` field.
///
/// TODO: Review whether or not this flag indicates that we need to treat LinearPCM format
/// uniquely in some way.
///
/// **Available** in OS X v10.6 and later.
const FLAGS_SAMPLE_FRACTION_SHIFT = 7;
/// The number of fractional bits.
///
/// `== (<other_flags> & FLAGS_SAMPLE_FRACTION_MASK) >> FLAGS_SAMPLE_FRACTION_SHIFT`
///
/// **Available** in OS X v10.6 and later.
const FLAGS_SAMPLE_FRACTION_MASK = 8064;
}
}
bitflags! {
/// Flags set for Apple Lossless data.
///
/// **Available** in OS X v10.3 and later.
///
/// Note: In the original Core Audio API these are consolidated with what we have named the
/// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
/// have chosen to separate these for greater type safety and clearer compatibility with
/// the **AudioFormat** type.
///
/// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
pub struct AppleLosslessFlags: u32 {
/// Sourced from 16 bit native endian signed integer data.
const BIT_16_SOURCE_DATA = 1;
/// Sourced from 20 bit native endian signed integer data aligned high in 24 bits.
const BIT_20_SOURCE_DATA = 2;
/// Sourced from 24 bit native endian signed integer data.
const BIT_24_SOURCE_DATA = 3;
/// Sourced from 32 bit native endian signed integer data.
const BIT_32_SOURCE_DATA = 4;
}
}
/// "Used in the `mFormatFlags` field of an `AudioStreamBasicDescription` structure that
/// describes an MPEG-4 audio stream to specify the type of MPEG-4 audio data.
///
/// **Available** in OS X v10.3 and later.
///
/// **Deprecated** in OS X v10.5.
///
/// Note: This type was originally represented using a bitflag field in the original API, however
/// there is only ever one flag set at a time. Thus, we use an enum as a more accurate,
/// user-friendly, type-safe representation.
///
/// Original documenation
/// [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/MPEG_4_Audio_Object_Type_Constants).
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Mpeg4ObjectId {
/// Advanced audio coding; the baisc MPEG-4 technology.
AAC_Main = 1,
/// Lossless coding; provides compression with no loss of quality.
AAC_LC = 2,
/// Scalable sampling rate; provides different sampling frequencies for different targets.
AAC_SSR = 3,
/// Long term prediction; reduces redundancy in a coded signal.
AAC_LTP = 4,
/// Spectral band replication; reconstructs high-frequency content from lower frequencies
/// and side information.
AAC_SBR = 5,
/// Scalable lossless coding.
AAC_Scalable = 6,
/// Transform-domain weighted interleaved vector quantization; an audio codec optimised for
/// audio coding at ultra low bit rates around 8kbit/s.
TwinVQ = 7,
/// Code Excited Linear Prdiction; a narrow-band/wide-band speech codec.
CELP = 8,
/// Harmonic Vector Excitation Coding; a very-low bit-rate parametric speech codec.
HVXC = 9,
}
impl Mpeg4ObjectId {
/// Create an Mpeg4ObjectId from a u32.
pub fn from_u32(u: u32) -> Option<Mpeg4ObjectId> {
match u {
1 => Some(Mpeg4ObjectId::AAC_Main),
2 => Some(Mpeg4ObjectId::AAC_LC),
3 => Some(Mpeg4ObjectId::AAC_SSR),
4 => Some(Mpeg4ObjectId::AAC_LTP),
5 => Some(Mpeg4ObjectId::AAC_SBR),
6 => Some(Mpeg4ObjectId::AAC_Scalable),
7 => Some(Mpeg4ObjectId::TwinVQ),
8 => Some(Mpeg4ObjectId::CELP),
9 => Some(Mpeg4ObjectId::HVXC),
_ => None,
}
}
}
bitflags! {
/// "These flags indicate the valuid fields in an AudioTimeStamp structure."
///
/// **Available** in OS X v10.0 and later.
///
/// Original Documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Time_Stamp_Flags).
pub struct AudioTimeStampFlags: u32 {
/// The sample frame time is valid.
const SAMPLE_TIME_VALID = 1;
/// The host time is valid.
const HOST_TIME_VALID = 2;
/// The rate scalar is valid.
const RATE_SCALAR_VALID = 4;
/// The world clock time is valid.
const WORLD_CLOCK_TIME_VALID = 8;
/// The SMPTE time is valid.
const SMPTE_TIME_VALID = 16;
}
}

View File

@@ -0,0 +1,885 @@
/// This is a collection of helper functions for performing common tasks on macOS.
/// These functions are only implemented for macOS, not iOS.
use crate::error::Error;
use std::collections::VecDeque;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use std::ptr::null;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Sender};
use std::sync::Mutex;
use std::time::Duration;
use std::{mem, thread};
use core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPtr, CFStringRef};
use sys;
use sys::pid_t;
use sys::{
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceIsAlive,
kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyHogMode,
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput,
kAudioDevicePropertyStreamConfiguration, kAudioHardwareNoError,
kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice,
kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster,
kAudioObjectPropertyElementWildcard, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput, kAudioObjectSystemObject,
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO,
kAudioStreamPropertyAvailablePhysicalFormats, kAudioStreamPropertyPhysicalFormat,
kCFStringEncodingUTF8, AudioDeviceID, AudioObjectAddPropertyListener,
AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID,
AudioObjectPropertyAddress, AudioObjectPropertyScope, AudioObjectRemovePropertyListener,
AudioObjectSetPropertyData, AudioStreamBasicDescription, AudioStreamRangedDescription,
AudioValueRange, OSStatus,
};
use crate::audio_unit::audio_format::{AudioFormat, LinearPcmFlags};
use crate::audio_unit::sample_format::SampleFormat;
use crate::audio_unit::stream_format::StreamFormat;
use crate::audio_unit::{AudioUnit, Element, IOType, Scope};
/// Helper function to get the device id of the default input or output device.
pub fn get_default_device_id(input: bool) -> Option<AudioDeviceID> {
let selector = if input {
kAudioHardwarePropertyDefaultInputDevice
} else {
kAudioHardwarePropertyDefaultOutputDevice
};
let property_address = AudioObjectPropertyAddress {
mSelector: selector,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let audio_device_id: AudioDeviceID = 0;
let data_size = mem::size_of::<AudioDeviceID>();
let status = unsafe {
AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&audio_device_id as *const _ as *mut _,
)
};
if status != kAudioHardwareNoError as i32 {
return None;
}
Some(audio_device_id)
}
/// Find the device id for a device name.
pub fn get_device_id_from_name(name: &str) -> Option<AudioDeviceID> {
if let Ok(all_ids) = get_audio_device_ids() {
return all_ids
.iter()
.find(|id| get_device_name(**id).unwrap_or_else(|_| "".to_string()) == name)
.copied();
}
None
}
/// Create an AudioUnit instance from a device id.
pub fn audio_unit_from_device_id(
device_id: AudioDeviceID,
input: bool,
) -> Result<AudioUnit, Error> {
let mut audio_unit = AudioUnit::new(IOType::HalOutput)?;
if input {
// Enable input processing.
let enable_input = 1u32;
audio_unit.set_property(
kAudioOutputUnitProperty_EnableIO,
Scope::Input,
Element::Input,
Some(&enable_input),
)?;
// Disable output processing.
let disable_output = 0u32;
audio_unit.set_property(
kAudioOutputUnitProperty_EnableIO,
Scope::Output,
Element::Output,
Some(&disable_output),
)?;
}
audio_unit.set_property(
kAudioOutputUnitProperty_CurrentDevice,
Scope::Global,
Element::Output,
Some(&device_id),
)?;
Ok(audio_unit)
}
/// List all audio device ids on the system.
pub fn get_audio_device_ids_for_scope(scope: Scope) -> Result<Vec<AudioDeviceID>, Error> {
let dev_scope = match scope {
Scope::Input => kAudioObjectPropertyScopeInput,
Scope::Output => kAudioObjectPropertyScopeOutput,
_ => kAudioObjectPropertyScopeGlobal,
};
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDevices,
mScope: dev_scope,
mElement: kAudioObjectPropertyElementMaster,
};
macro_rules! try_status_or_return {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}
let data_size = 0u32;
let status = unsafe {
AudioObjectGetPropertyDataSize(
kAudioObjectSystemObject,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
)
};
try_status_or_return!(status);
let device_count = data_size / mem::size_of::<AudioDeviceID>() as u32;
let mut audio_devices = vec![];
audio_devices.reserve_exact(device_count as usize);
unsafe { audio_devices.set_len(device_count as usize) };
let status = unsafe {
AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
audio_devices.as_mut_ptr() as *mut _,
)
};
try_status_or_return!(status);
Ok(audio_devices)
}
pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
get_audio_device_ids_for_scope(Scope::Global)
}
#[test]
fn test_get_audio_device_ids() {
let _ = get_audio_device_ids().expect("Failed to get audio device ids");
}
#[test]
fn test_get_audio_device_ids_for_scope() {
for scope in &[
Scope::Global,
Scope::Input,
Scope::Output,
Scope::Group,
Scope::Part,
Scope::Note,
Scope::Layer,
Scope::LayerItem,
] {
let _ = get_audio_device_ids_for_scope(*scope).expect("Failed to get audio device ids");
}
}
/// does this device support input / ouptut?
pub fn get_audio_device_supports_scope(devid: AudioDeviceID, scope: Scope) -> Result<bool, Error> {
let dev_scope: AudioObjectPropertyScope = match scope {
Scope::Input => kAudioObjectPropertyScopeInput,
Scope::Output => kAudioObjectPropertyScopeOutput,
_ => kAudioObjectPropertyScopeGlobal,
};
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyStreamConfiguration,
mScope: dev_scope,
mElement: kAudioObjectPropertyElementWildcard,
};
macro_rules! try_status_or_return {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}
let data_size = 0u32;
let status = unsafe {
AudioObjectGetPropertyDataSize(
devid,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
)
};
try_status_or_return!(status);
let mut bfrs: Vec<u8> = Vec::with_capacity(data_size as usize);
let buffers = bfrs.as_mut_ptr() as *mut sys::AudioBufferList;
unsafe {
let status = AudioObjectGetPropertyData(
devid,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
buffers as *mut _,
);
if status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown(status));
}
for i in 0..(*buffers).mNumberBuffers {
let buf = (*buffers).mBuffers[i as usize];
if buf.mNumberChannels > 0 {
return Ok(true);
}
}
}
Ok(false)
}
/// Get the device name for a device id.
pub fn get_device_name(device_id: AudioDeviceID) -> Result<String, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceNameCFString,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster,
};
macro_rules! try_status_or_return {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}
let device_name: CFStringRef = null();
let data_size = mem::size_of::<CFStringRef>();
let c_str = unsafe {
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&device_name as *const _ as *mut _,
);
try_status_or_return!(status);
let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8);
if c_string.is_null() {
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&device_name as *const _ as *mut _,
);
try_status_or_return!(status);
let mut buf: [i8; 255] = [0; 255];
let result = CFStringGetCString(
device_name,
buf.as_mut_ptr(),
buf.len() as _,
kCFStringEncodingUTF8,
);
if result == 0 {
return Err(Error::Unknown(result as i32));
}
let name: &CStr = CStr::from_ptr(buf.as_ptr());
return Ok(name.to_str().unwrap().to_owned());
}
CStr::from_ptr(c_string as *mut _)
};
Ok(c_str.to_string_lossy().into_owned())
}
/// Change the sample rate of a device.
/// Adapted from CPAL.
pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result<(), Error> {
// Check whether or not we need to change the device sample rate to suit the one specified for the stream.
unsafe {
// Get the current sample rate.
let mut property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let sample_rate: f64 = 0.0;
let data_size = mem::size_of::<f64>() as u32;
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&sample_rate as *const _ as *mut _,
);
Error::from_os_status(status)?;
// If the requested sample rate is different to the device sample rate, update the device.
if sample_rate as u32 != new_rate as u32 {
// Get available sample rate ranges.
property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
let data_size = 0u32;
let status = AudioObjectGetPropertyDataSize(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
);
Error::from_os_status(status)?;
let n_ranges = data_size as usize / mem::size_of::<AudioValueRange>();
let mut ranges: Vec<AudioValueRange> = vec![];
ranges.reserve_exact(n_ranges as usize);
ranges.set_len(n_ranges);
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
ranges.as_mut_ptr() as *mut _,
);
Error::from_os_status(status)?;
// Now that we have the available ranges, pick the one matching the desired rate.
let new_rate_integer = new_rate as u32;
let maybe_index = ranges.iter().position(|r| {
r.mMinimum as u32 == new_rate_integer && r.mMaximum as u32 == new_rate_integer
});
let range_index = match maybe_index {
None => return Err(Error::UnsupportedSampleRate),
Some(i) => i,
};
// Update the property selector to specify the nominal sample rate.
property_address.mSelector = kAudioDevicePropertyNominalSampleRate;
// Add a listener to know when the sample rate changes.
// Since the listener implements Drop, we don't need to manually unregister this later.
let (sender, receiver) = channel();
let mut listener = RateListener::new(device_id, Some(sender));
listener.register()?;
// Finally, set the sample rate.
let status = AudioObjectSetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
data_size,
&ranges[range_index] as *const _ as *const _,
);
Error::from_os_status(status)?;
// Wait for the reported_rate to change.
//
// This sometimes takes up to half a second, timeout after 2 sec to have a little margin.
let timer = ::std::time::Instant::now();
loop {
if let Ok(reported_rate) = receiver.recv_timeout(Duration::from_millis(100)) {
if new_rate as usize == reported_rate as usize {
break;
}
}
if timer.elapsed() > Duration::from_secs(2) {
return Err(Error::UnsupportedSampleRate);
}
}
};
Ok(())
}
}
/// Find the closest match of the physical formats to the provided `StreamFormat`.
/// This function will pick the first format it finds that supports the provided sample format, rate and number of channels.
/// The provided format flags in the `StreamFormat` are ignored.
pub fn find_matching_physical_format(
device_id: AudioDeviceID,
stream_format: StreamFormat,
) -> Option<AudioStreamBasicDescription> {
if let Ok(all_formats) = get_supported_physical_stream_formats(device_id) {
let requested_samplerate = stream_format.sample_rate as usize;
let requested_bits = stream_format.sample_format.size_in_bits();
let requested_float = stream_format.sample_format == SampleFormat::F32;
let requested_channels = stream_format.channels;
for fmt in all_formats {
let min_rate = fmt.mSampleRateRange.mMinimum as usize;
let max_rate = fmt.mSampleRateRange.mMaximum as usize;
let rate = fmt.mFormat.mSampleRate as usize;
let channels = fmt.mFormat.mChannelsPerFrame;
if let Some(AudioFormat::LinearPCM(flags)) = AudioFormat::from_format_and_flag(
fmt.mFormat.mFormatID,
Some(fmt.mFormat.mFormatFlags),
) {
let is_float = flags.contains(LinearPcmFlags::IS_FLOAT);
let is_int = flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER);
if is_int && is_float {
// Probably never occurs, check just in case
continue;
}
if requested_float && !is_float {
// Wrong number type
continue;
}
if !requested_float && !is_int {
// Wrong number type
continue;
}
if requested_bits != fmt.mFormat.mBitsPerChannel {
// Wrong number of bits
continue;
}
if requested_channels > channels {
// Too few channels
continue;
}
if rate == requested_samplerate
|| (requested_samplerate >= min_rate && requested_samplerate <= max_rate)
{
return Some(fmt.mFormat);
}
}
}
}
None
}
/// Change the physical stream format (sample rate and format) of a device.
pub fn set_device_physical_stream_format(
device_id: AudioDeviceID,
new_asbd: AudioStreamBasicDescription,
) -> Result<(), Error> {
unsafe {
// Get the current format.
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let maybe_asbd: mem::MaybeUninit<AudioStreamBasicDescription> = mem::MaybeUninit::zeroed();
let data_size = mem::size_of::<AudioStreamBasicDescription>() as u32;
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&maybe_asbd as *const _ as *mut _,
);
Error::from_os_status(status)?;
let asbd = maybe_asbd.assume_init();
if !asbds_are_equal(&asbd, &new_asbd) {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let reported_asbd: mem::MaybeUninit<AudioStreamBasicDescription> =
mem::MaybeUninit::zeroed();
let reported_asbd = reported_asbd.assume_init();
let status = AudioObjectSetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
data_size,
&new_asbd as *const _ as *const _,
);
Error::from_os_status(status)?;
// Wait for the reported format to change.
// This can take up to half a second, but we timeout after 2 sec just in case.
let timer = ::std::time::Instant::now();
loop {
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&reported_asbd as *const _ as *mut _,
);
Error::from_os_status(status)?;
if asbds_are_equal(&reported_asbd, &new_asbd) {
break;
}
thread::sleep(Duration::from_millis(5));
if timer.elapsed() > Duration::from_secs(2) {
return Err(Error::UnsupportedStreamFormat);
}
}
}
Ok(())
}
}
/// Helper to check if two ASBDs are equal.
fn asbds_are_equal(
left: &AudioStreamBasicDescription,
right: &AudioStreamBasicDescription,
) -> bool {
left.mSampleRate as u32 == right.mSampleRate as u32
&& left.mFormatID == right.mFormatID
&& left.mFormatFlags == right.mFormatFlags
&& left.mBytesPerPacket == right.mBytesPerPacket
&& left.mFramesPerPacket == right.mFramesPerPacket
&& left.mBytesPerFrame == right.mBytesPerFrame
&& left.mChannelsPerFrame == right.mChannelsPerFrame
&& left.mBitsPerChannel == right.mBitsPerChannel
}
/// Get a vector with all supported physical formats as AudioBasicRangedDescriptions.
pub fn get_supported_physical_stream_formats(
device_id: AudioDeviceID,
) -> Result<Vec<AudioStreamRangedDescription>, Error> {
// Get available formats.
let mut property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let allformats = unsafe {
property_address.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
let mut data_size = 0u32;
let status = AudioObjectGetPropertyDataSize(
device_id,
&property_address as *const _,
0,
null(),
&mut data_size as *mut _,
);
Error::from_os_status(status)?;
let n_formats = data_size as usize / mem::size_of::<AudioStreamRangedDescription>();
let mut formats: Vec<AudioStreamRangedDescription> = vec![];
formats.reserve_exact(n_formats as usize);
formats.set_len(n_formats);
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
formats.as_mut_ptr() as *mut _,
);
Error::from_os_status(status)?;
formats
};
Ok(allformats)
}
/// Changing the sample rate is an asynchonous process.
/// A RateListener can be used to get notified when the rate is changed.
pub struct RateListener {
pub queue: Mutex<VecDeque<f64>>,
sync_channel: Option<Sender<f64>>,
device_id: AudioDeviceID,
property_address: AudioObjectPropertyAddress,
rate_listener: Option<
unsafe extern "C" fn(u32, u32, *const AudioObjectPropertyAddress, *mut c_void) -> i32,
>,
}
impl Drop for RateListener {
fn drop(&mut self) {
let _ = self.unregister();
}
}
impl RateListener {
/// Create a new RateListener for the given AudioDeviceID.
/// If an `std::sync::mpsc::Sender` is provided, then events will be pushed to that channel.
/// If not, they will instead be stored in an internal queue that will need to be polled.
/// The listener must be registered by calling `register()` in order to start receiving notifications.
pub fn new(device_id: AudioDeviceID, sync_channel: Option<Sender<f64>>) -> RateListener {
// Add our sample rate change listener callback.
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let queue = Mutex::new(VecDeque::new());
RateListener {
queue,
sync_channel,
device_id,
property_address,
rate_listener: None,
}
}
/// Register this listener to receive notifications.
pub fn register(&mut self) -> Result<(), Error> {
unsafe extern "C" fn rate_listener(
device_id: AudioObjectID,
_n_addresses: u32,
_properties: *const AudioObjectPropertyAddress,
self_ptr: *mut ::std::os::raw::c_void,
) -> OSStatus {
let self_ptr: &mut RateListener = &mut *(self_ptr as *mut RateListener);
let rate: f64 = 0.0;
let data_size = mem::size_of::<f64>();
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let result = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&rate as *const _ as *mut _,
);
if let Some(sender) = &self_ptr.sync_channel {
sender.send(rate).unwrap();
} else {
let mut queue = self_ptr.queue.lock().unwrap();
queue.push_back(rate);
}
result
}
// Add our sample rate change listener callback.
let status = unsafe {
AudioObjectAddPropertyListener(
self.device_id,
&self.property_address as *const _,
Some(rate_listener),
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.rate_listener = Some(rate_listener);
Ok(())
}
/// Unregister this listener to stop receiving notifications.
pub fn unregister(&mut self) -> Result<(), Error> {
if self.rate_listener.is_some() {
let status = unsafe {
AudioObjectRemovePropertyListener(
self.device_id,
&self.property_address as *const _,
self.rate_listener,
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.rate_listener = None;
}
Ok(())
}
/// Get the number of sample rate values received (equals the number of change events).
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
pub fn get_nbr_values(&self) -> usize {
self.queue.lock().unwrap().len()
}
/// Copy all received values to a Vec. The latest value is the last element.
/// The internal buffer is preserved.
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
pub fn copy_values(&self) -> Vec<f64> {
self.queue
.lock()
.unwrap()
.iter()
.copied()
.collect::<Vec<f64>>()
}
/// Get all received values as a Vec. The latest value is the last element.
/// This clears the internal buffer.
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
pub fn drain_values(&mut self) -> Vec<f64> {
self.queue.lock().unwrap().drain(..).collect::<Vec<f64>>()
}
}
/// An AliveListener is used to get notified when a device is disconnected.
pub struct AliveListener {
alive: Box<AtomicBool>,
device_id: AudioDeviceID,
property_address: AudioObjectPropertyAddress,
alive_listener: Option<
unsafe extern "C" fn(u32, u32, *const AudioObjectPropertyAddress, *mut c_void) -> i32,
>,
}
impl Drop for AliveListener {
fn drop(&mut self) {
let _ = self.unregister();
}
}
impl AliveListener {
/// Create a new AliveListener for the given AudioDeviceID.
/// The listener must be registered by calling `register()` in order to start receiving notifications.
pub fn new(device_id: AudioDeviceID) -> AliveListener {
// Add our listener callback.
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceIsAlive,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
AliveListener {
alive: Box::new(AtomicBool::new(true)),
device_id,
property_address,
alive_listener: None,
}
}
/// Register this listener to receive notifications.
pub fn register(&mut self) -> Result<(), Error> {
unsafe extern "C" fn alive_listener(
device_id: AudioObjectID,
_n_addresses: u32,
_properties: *const AudioObjectPropertyAddress,
self_ptr: *mut ::std::os::raw::c_void,
) -> OSStatus {
let self_ptr: &mut AliveListener = &mut *(self_ptr as *mut AliveListener);
let alive: u32 = 0;
let data_size = mem::size_of::<u32>();
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceIsAlive,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let result = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&alive as *const _ as *mut _,
);
self_ptr.alive.store(alive > 0, Ordering::SeqCst);
result
}
// Add our listener callback.
let status = unsafe {
AudioObjectAddPropertyListener(
self.device_id,
&self.property_address as *const _,
Some(alive_listener),
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.alive_listener = Some(alive_listener);
Ok(())
}
/// Unregister this listener to stop receiving notifications
pub fn unregister(&mut self) -> Result<(), Error> {
if self.alive_listener.is_some() {
let status = unsafe {
AudioObjectRemovePropertyListener(
self.device_id,
&self.property_address as *const _,
self.alive_listener,
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.alive_listener = None;
}
Ok(())
}
/// Check if the device is still alive.
pub fn is_alive(&self) -> bool {
self.alive.load(Ordering::SeqCst)
}
}
/// Helper for hog mode (exclusive access).
/// Get the pid of the process that currently owns exclusive access to a device.
/// A pid value of -1 means no process owns exclusive access.
pub fn get_hogging_pid(device_id: AudioDeviceID) -> Result<pid_t, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyHogMode,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let pid = unsafe {
let temp_pid: pid_t = 0;
let data_size = mem::size_of::<pid_t>();
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&temp_pid as *const _ as *mut _,
);
Error::from_os_status(status)?;
temp_pid
};
Ok(pid)
}
/// Helper for hog mode (exclusive access).
/// Toggle hog mode for a device.
/// If no process owns exclusive access, then the calling process takes ownership.
/// If the calling process already has ownership, this is released.
/// If another process owns access, then nothing will happen.
/// Returns the pid of the new owning process.
/// A pid value of -1 means no process owns exclusive access.
pub fn toggle_hog_mode(device_id: AudioDeviceID) -> Result<pid_t, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyHogMode,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let pid = unsafe {
let temp_pid: pid_t = -1;
let data_size = mem::size_of::<pid_t>();
let status = AudioObjectSetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
data_size as u32,
&temp_pid as *const _ as *mut _,
);
Error::from_os_status(status)?;
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&temp_pid as *const _ as *mut _,
);
Error::from_os_status(status)?;
temp_pid
};
Ok(pid)
}

View File

@@ -0,0 +1,432 @@
//! This module is an attempt to provide a friendly, rust-esque interface to Apple's Audio Unit API.
//!
//! Learn more about the Audio Unit API [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003278-CH1-SW2)
//! and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
//!
//! TODO: The following are `kAudioUnitSubType`s (along with their const u32) generated by
//! rust-bindgen that we could not find any documentation on:
//!
//! - MIDISynth = 1836284270,
//! - RoundTripAAC = 1918984547,
//! - SpatialMixer = 862217581,
//! - SphericalHeadPanner = 1936746610,
//! - VectorPanner = 1986158963,
//! - SoundFieldPanner = 1634558569,
//! - HRTFPanner = 1752331366,
//! - NetReceive = 1852990326,
//!
//! If you can find documentation on these, please feel free to submit an issue or PR with the
//! fixes!
use crate::error::Error;
use std::mem;
use std::os::raw::{c_uint, c_void};
use std::ptr;
use sys;
pub use self::audio_format::AudioFormat;
pub use self::sample_format::{Sample, SampleFormat};
pub use self::stream_format::StreamFormat;
pub use self::types::{
EffectType, FormatConverterType, GeneratorType, IOType, MixerType, MusicDeviceType, Type,
};
#[cfg(target_os = "macos")]
pub mod macos_helpers;
pub mod audio_format;
pub mod render_callback;
pub mod sample_format;
pub mod stream_format;
pub mod types;
/// The input and output **Scope**s.
///
/// More info [here](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Scopes)
/// and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
#[derive(Copy, Clone, Debug)]
pub enum Scope {
Global = 0,
Input = 1,
Output = 2,
Group = 3,
Part = 4,
Note = 5,
Layer = 6,
LayerItem = 7,
}
/// Represents the **Input** and **Output** **Element**s.
///
/// These are used when specifying which **Element** we're setting the properties of.
#[derive(Copy, Clone, Debug)]
pub enum Element {
Output = 0,
Input = 1,
}
/// A rust representation of the sys::AudioUnit, including a pointer to the current rendering callback.
///
/// Find the original Audio Unit Programming Guide [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
pub struct AudioUnit {
instance: sys::AudioUnit,
maybe_render_callback: Option<*mut render_callback::InputProcFnWrapper>,
maybe_input_callback: Option<InputCallback>,
}
struct InputCallback {
// The audio buffer list to which input data is rendered.
buffer_list: *mut sys::AudioBufferList,
callback: *mut render_callback::InputProcFnWrapper,
}
macro_rules! try_os_status {
($expr:expr) => {
Error::from_os_status($expr)?
};
}
impl AudioUnit {
/// Construct a new AudioUnit with any type that may be automatically converted into
/// [**Type**](./enum.Type).
///
/// Here is a list of compatible types:
///
/// - [**Type**](./types/enum.Type)
/// - [**IOType**](./types/enum.IOType)
/// - [**MusicDeviceType**](./types/enum.MusicDeviceType)
/// - [**GeneratorType**](./types/enum.GeneratorType)
/// - [**FormatConverterType**](./types/enum.FormatConverterType)
/// - [**EffectType**](./types/enum.EffectType)
/// - [**MixerType**](./types/enum.MixerType)
///
/// To construct the **AudioUnit** with some component flags, see
/// [**AudioUnit::new_with_flags**](./struct.AudioUnit#method.new_with_flags).
///
/// Note: the `AudioUnit` is constructed with the `kAudioUnitManufacturer_Apple` Manufacturer
/// Identifier, as this is the only Audio Unit Manufacturer Identifier documented by Apple in
/// the AudioUnit reference (see [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Manufacturer_Identifier)).
pub fn new<T>(ty: T) -> Result<AudioUnit, Error>
where
T: Into<Type>,
{
AudioUnit::new_with_flags(ty, 0, 0)
}
/// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given
/// component flags and mask.
pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
where
T: Into<Type>,
{
const MANUFACTURER_IDENTIFIER: u32 = sys::kAudioUnitManufacturer_Apple;
let au_type: Type = ty.into();
let sub_type_u32 = match au_type.as_subtype_u32() {
Some(u) => u,
None => return Err(Error::NoKnownSubtype),
};
// A description of the audio unit we desire.
let desc = sys::AudioComponentDescription {
componentType: au_type.as_u32() as c_uint,
componentSubType: sub_type_u32 as c_uint,
componentManufacturer: MANUFACTURER_IDENTIFIER,
componentFlags: flags,
componentFlagsMask: mask,
};
unsafe {
// Find the default audio unit for the description.
//
// From the "Audio Unit Hosting Guide for iOS":
//
// Passing NULL to the first parameter of AudioComponentFindNext tells this function to
// find the first system audio unit matching the description, using a system-defined
// ordering. If you instead pass a previously found audio unit reference in this
// parameter, the function locates the next audio unit matching the description.
let component = sys::AudioComponentFindNext(ptr::null_mut(), &desc as *const _);
if component.is_null() {
return Err(Error::NoMatchingDefaultAudioUnitFound);
}
// Create an instance of the default audio unit using the component.
let mut instance_uninit = mem::MaybeUninit::<sys::AudioUnit>::uninit();
try_os_status!(sys::AudioComponentInstanceNew(
component,
instance_uninit.as_mut_ptr() as *mut sys::AudioUnit
));
let instance: sys::AudioUnit = instance_uninit.assume_init();
// Initialise the audio unit!
try_os_status!(sys::AudioUnitInitialize(instance));
Ok(AudioUnit {
instance,
maybe_render_callback: None,
maybe_input_callback: None,
})
}
}
/// On successful initialization, the audio formats for input and output are valid
/// and the audio unit is ready to render. During initialization, an audio unit
/// allocates memory according to the maximum number of audio frames it can produce
/// in response to a single render call.
///
/// Usually, the state of an audio unit (such as its I/O formats and memory allocations)
/// cannot be changed while an audio unit is initialized.
pub fn initialize(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioUnitInitialize(self.instance));
}
Ok(())
}
/// Before you change an initialize audio units processing characteristics,
/// such as its input or output audio data format or its sample rate, you must
/// first uninitialize it. Calling this function deallocates the audio units resources.
///
/// After calling this function, you can reconfigure the audio unit and then call
/// AudioUnitInitialize to reinitialize it.
pub fn uninitialize(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioUnitUninitialize(self.instance));
}
Ok(())
}
/// Sets the value for some property of the **AudioUnit**.
///
/// To clear an audio unit property value, set the data paramater with `None::<()>`.
///
/// Clearing properties only works for those properties that do not have a default value.
///
/// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
/// - **maybe_data**: The value that you want to apply to the property.
pub fn set_property<T>(
&mut self,
id: u32,
scope: Scope,
elem: Element,
maybe_data: Option<&T>,
) -> Result<(), Error> {
set_property(self.instance, id, scope, elem, maybe_data)
}
/// Gets the value of an **AudioUnit** property.
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> {
get_property(self.instance, id, scope, elem)
}
/// Starts an I/O **AudioUnit**, which in turn starts the audio unit processing graph that it is
/// connected to.
///
/// **Available** in OS X v10.0 and later.
pub fn start(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioOutputUnitStart(self.instance));
}
Ok(())
}
/// Stops an I/O **AudioUnit**, which in turn stops the audio unit processing graph that it is
/// connected to.
///
/// **Available** in OS X v10.0 and later.
pub fn stop(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioOutputUnitStop(self.instance));
}
Ok(())
}
/// Set the **AudioUnit**'s sample rate.
///
/// **Available** in iOS 2.0 and later.
pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> {
let id = sys::kAudioUnitProperty_SampleRate;
self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate))
}
/// Get the **AudioUnit**'s sample rate.
pub fn sample_rate(&self) -> Result<f64, Error> {
let id = sys::kAudioUnitProperty_SampleRate;
self.get_property(id, Scope::Input, Element::Output)
}
/// Sets the current **StreamFormat** for the AudioUnit.
///
/// Core Audio uses slightly different defaults depending on the platform.
///
/// From the Core Audio Overview:
///
/// > The canonical formats in Core Audio are as follows:
/// >
/// > - iOS input and output: Linear PCM with 16-bit integer samples.
/// > - iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit
/// fixed-point samples
/// > - Mac input and output: Linear PCM with 32-bit floating point samples.
/// > - Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit
/// floating-point
pub fn set_stream_format(
&mut self,
stream_format: StreamFormat,
scope: Scope,
) -> Result<(), Error> {
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = stream_format.to_asbd();
self.set_property(id, scope, Element::Output, Some(&asbd))
}
/// Return the current Stream Format for the AudioUnit.
pub fn stream_format(&self, scope: Scope) -> Result<StreamFormat, Error> {
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = self.get_property(id, scope, Element::Output)?;
StreamFormat::from_asbd(asbd)
}
/// Return the current output Stream Format for the AudioUnit.
pub fn output_stream_format(&self) -> Result<StreamFormat, Error> {
self.stream_format(Scope::Output)
}
/// Return the current input Stream Format for the AudioUnit.
pub fn input_stream_format(&self) -> Result<StreamFormat, Error> {
self.stream_format(Scope::Input)
}
}
unsafe impl Send for AudioUnit {}
impl Drop for AudioUnit {
fn drop(&mut self) {
unsafe {
use crate::error;
// We don't want to panic in `drop`, so we'll ignore returned errors.
//
// A user should explicitly terminate the `AudioUnit` if they want to handle errors (we
// still need to provide a way to actually do that).
self.stop().ok();
error::Error::from_os_status(sys::AudioUnitUninitialize(self.instance)).ok();
self.free_render_callback();
self.free_input_callback();
error::Error::from_os_status(sys::AudioComponentInstanceDispose(self.instance)).ok();
}
}
}
/// Sets the value for some property of the **AudioUnit**.
///
/// To clear an audio unit property value, set the data paramater with `None::<()>`.
///
/// Clearing properties only works for those properties that do not have a default value.
///
/// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **au**: The AudioUnit instance.
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
/// - **maybe_data**: The value that you want to apply to the property.
pub fn set_property<T>(
au: sys::AudioUnit,
id: u32,
scope: Scope,
elem: Element,
maybe_data: Option<&T>,
) -> Result<(), Error> {
let (data_ptr, size) = maybe_data
.map(|data| {
let ptr = data as *const _ as *const c_void;
let size = ::std::mem::size_of::<T>() as u32;
(ptr, size)
})
.unwrap_or_else(|| (::std::ptr::null(), 0));
let scope = scope as c_uint;
let elem = elem as c_uint;
unsafe {
try_os_status!(sys::AudioUnitSetProperty(
au, id, scope, elem, data_ptr, size
))
}
Ok(())
}
/// Gets the value of an **AudioUnit** property.
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **au**: The AudioUnit instance.
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
pub fn get_property<T>(
au: sys::AudioUnit,
id: u32,
scope: Scope,
elem: Element,
) -> Result<T, Error> {
let scope = scope as c_uint;
let elem = elem as c_uint;
let mut size = ::std::mem::size_of::<T>() as u32;
unsafe {
let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
let data_ptr = data_uninit.as_mut_ptr() as *mut _ as *mut c_void;
let size_ptr = &mut size as *mut _;
try_os_status!(sys::AudioUnitGetProperty(
au, id, scope, elem, data_ptr, size_ptr
));
let data: T = data_uninit.assume_init();
Ok(data)
}
}
/// Gets the value of a specified audio session property.
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **id**: The identifier of the property.
#[cfg(target_os = "ios")]
pub fn audio_session_get_property<T>(id: u32) -> Result<T, Error> {
let mut size = ::std::mem::size_of::<T>() as u32;
unsafe {
let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
let data_ptr = data_uninit.as_mut_ptr() as *mut _ as *mut c_void;
let size_ptr = &mut size as *mut _;
try_os_status!(sys::AudioSessionGetProperty(id, size_ptr, data_ptr));
let data: T = data_uninit.assume_init();
Ok(data)
}
}

View File

@@ -0,0 +1,776 @@
use super::audio_format::LinearPcmFlags;
use super::{AudioUnit, Element, Scope};
use crate::error::{self, Error};
use std::mem;
use std::os::raw::c_void;
use std::slice;
use sys;
pub use self::action_flags::ActionFlags;
pub use self::data::Data;
/// When `set_render_callback` is called, a closure of this type will be used to wrap the given
/// render callback function.
///
/// This allows the user to provide a custom, more rust-esque callback function type that takes
/// greater advantage of rust's type safety.
pub type InputProcFn = dyn FnMut(
*mut sys::AudioUnitRenderActionFlags,
*const sys::AudioTimeStamp,
sys::UInt32,
sys::UInt32,
*mut sys::AudioBufferList,
) -> sys::OSStatus;
/// This type allows us to safely wrap a boxed `RenderCallback` to use within the input proc.
pub struct InputProcFnWrapper {
callback: Box<InputProcFn>,
}
/// Arguments given to the render callback function.
#[derive(Debug)]
pub struct Args<D> {
/// A type wrapping the the buffer that matches the expected audio format.
pub data: D,
/// Timing information for the callback.
pub time_stamp: sys::AudioTimeStamp,
/// TODO
pub bus_number: u32,
/// The number of frames in the buffer as `usize` for easier indexing.
pub num_frames: usize,
/// Flags for configuring audio unit rendering.
///
/// This parameter lets a callback provide various hints to the audio unit.
///
/// For example: if there is no audio to process, we can insert the `OUTPUT_IS_SILENCE` flag to
/// indicate to the audio unit that the buffer does not need to be processed.
pub flags: action_flags::Handle,
}
/// Format specific render callback data.
pub mod data {
use super::super::Sample;
use super::super::StreamFormat;
use crate::audio_unit::audio_format::LinearPcmFlags;
use std::marker::PhantomData;
use std::slice;
use sys;
/// Audio data wrappers specific to the `AudioUnit`'s `AudioFormat`.
pub trait Data {
/// Check whether or not the stream format matches this type of data.
fn does_stream_format_match(stream_format: &StreamFormat) -> bool;
/// We must be able to construct Self from arguments given to the `input_proc`.
/// # Safety
/// TODO document how to use this function safely.
unsafe fn from_input_proc_args(num_frames: u32, io_data: *mut sys::AudioBufferList)
-> Self;
}
/// A raw pointer to the audio data so that the user may handle it themselves.
#[derive(Debug)]
pub struct Raw {
pub data: *mut sys::AudioBufferList,
}
impl Data for Raw {
fn does_stream_format_match(_: &StreamFormat) -> bool {
true
}
unsafe fn from_input_proc_args(
_num_frames: u32,
io_data: *mut sys::AudioBufferList,
) -> Self {
Raw { data: io_data }
}
}
/// An interleaved linear PCM buffer with samples of type `S`.
pub struct Interleaved<S: 'static> {
/// The audio buffer.
pub buffer: &'static mut [S],
pub channels: usize,
sample_format: PhantomData<S>,
}
/// An interleaved linear PCM buffer with samples stored as plain bytes.
pub struct InterleavedBytes<S: 'static> {
/// The audio buffer.
pub buffer: &'static mut [u8],
pub channels: usize,
sample_format: PhantomData<S>,
}
/// A wrapper around the pointer to the `mBuffers` array.
pub struct NonInterleaved<S> {
/// The list of audio buffers.
buffers: &'static mut [sys::AudioBuffer],
/// The number of frames in each channel.
frames: usize,
sample_format: PhantomData<S>,
}
/// An iterator produced by a `NonInterleaved`, yielding a reference to each channel.
pub struct Channels<'a, S: 'a> {
buffers: slice::Iter<'a, sys::AudioBuffer>,
frames: usize,
sample_format: PhantomData<S>,
}
/// An iterator produced by a `NonInterleaved`, yielding a mutable reference to each channel.
pub struct ChannelsMut<'a, S: 'a> {
buffers: slice::IterMut<'a, sys::AudioBuffer>,
frames: usize,
sample_format: PhantomData<S>,
}
unsafe impl<S> Send for NonInterleaved<S> where S: Send {}
impl<'a, S> Iterator for Channels<'a, S> {
type Item = &'a [S];
#[allow(non_snake_case)]
fn next(&mut self) -> Option<Self::Item> {
self.buffers.next().map(
|&sys::AudioBuffer {
mNumberChannels,
mData,
..
}| {
let len = mNumberChannels as usize * self.frames;
let ptr = mData as *mut S;
unsafe { slice::from_raw_parts(ptr, len) }
},
)
}
}
impl<'a, S> Iterator for ChannelsMut<'a, S> {
type Item = &'a mut [S];
#[allow(non_snake_case)]
fn next(&mut self) -> Option<Self::Item> {
self.buffers.next().map(
|&mut sys::AudioBuffer {
mNumberChannels,
mData,
..
}| {
let len = mNumberChannels as usize * self.frames;
let ptr = mData as *mut S;
unsafe { slice::from_raw_parts_mut(ptr, len) }
},
)
}
}
impl<S> NonInterleaved<S> {
/// An iterator yielding a reference to each channel in the array.
pub fn channels(&self) -> Channels<S> {
Channels {
buffers: self.buffers.iter(),
frames: self.frames,
sample_format: PhantomData,
}
}
/// An iterator yielding a mutable reference to each channel in the array.
pub fn channels_mut(&mut self) -> ChannelsMut<S> {
ChannelsMut {
buffers: self.buffers.iter_mut(),
frames: self.frames,
sample_format: PhantomData,
}
}
}
// Implementation for a non-interleaved linear PCM audio format.
impl<S> Data for NonInterleaved<S>
where
S: Sample,
{
fn does_stream_format_match(stream_format: &StreamFormat) -> bool {
stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED)
&& S::sample_format().does_match_flags(stream_format.flags)
}
#[allow(non_snake_case)]
unsafe fn from_input_proc_args(frames: u32, io_data: *mut sys::AudioBufferList) -> Self {
let ptr = (*io_data).mBuffers.as_ptr() as *mut sys::AudioBuffer;
let len = (*io_data).mNumberBuffers as usize;
let buffers = slice::from_raw_parts_mut(ptr, len);
NonInterleaved {
buffers,
frames: frames as usize,
sample_format: PhantomData,
}
}
}
// Implementation for an interleaved linear PCM audio format.
impl<S> Data for Interleaved<S>
where
S: Sample,
{
fn does_stream_format_match(stream_format: &StreamFormat) -> bool {
!stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED)
&& S::sample_format().does_match_flags(stream_format.flags)
}
#[allow(non_snake_case)]
unsafe fn from_input_proc_args(frames: u32, io_data: *mut sys::AudioBufferList) -> Self {
// // We're expecting a single interleaved buffer which will be the first in the array.
let sys::AudioBuffer {
mNumberChannels,
mDataByteSize,
mData,
} = (*io_data).mBuffers[0];
// // Ensure that the size of the data matches the size of the sample format
// // multiplied by the number of frames.
// //
// // TODO: Return an Err instead of `panic`ing.
let buffer_len = frames as usize * mNumberChannels as usize;
let expected_size = ::std::mem::size_of::<S>() * buffer_len;
assert!(mDataByteSize as usize == expected_size);
let buffer: &mut [S] = {
let buffer_ptr = mData as *mut S;
slice::from_raw_parts_mut(buffer_ptr, buffer_len)
};
Interleaved {
buffer,
channels: mNumberChannels as usize,
sample_format: PhantomData,
}
}
}
// Implementation for an interleaved linear PCM audio format using plain bytes.
impl<S> Data for InterleavedBytes<S>
where
S: Sample,
{
fn does_stream_format_match(stream_format: &StreamFormat) -> bool {
!stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED)
&& S::sample_format().does_match_flags(stream_format.flags)
}
#[allow(non_snake_case)]
unsafe fn from_input_proc_args(frames: u32, io_data: *mut sys::AudioBufferList) -> Self {
// // We're expecting a single interleaved buffer which will be the first in the array.
let sys::AudioBuffer {
mNumberChannels,
mDataByteSize,
mData,
} = (*io_data).mBuffers[0];
// // Ensure that the size of the data matches the size of the sample format
// // multiplied by the number of frames.
// //
// // TODO: Return an Err instead of `panic`ing.
let buffer_len = frames as usize * mNumberChannels as usize;
let expected_size = ::std::mem::size_of::<S>() * buffer_len;
assert!(mDataByteSize as usize == expected_size);
let buffer: &mut [u8] = {
let buffer_ptr = mData as *mut u8;
slice::from_raw_parts_mut(buffer_ptr, mDataByteSize as usize)
};
InterleavedBytes {
buffer,
channels: mNumberChannels as usize,
sample_format: PhantomData,
}
}
}
}
pub mod action_flags {
use std::fmt;
use sys;
bitflags! {
pub struct ActionFlags: u32 {
/// Called on a render notification Proc, which is called either before or after the
/// render operation of the audio unit. If this flag is set, the proc is being called
/// before the render operation is performed.
///
/// **Available** in OS X v10.0 and later.
const PRE_RENDER = sys::kAudioUnitRenderAction_PreRender;
/// Called on a render notification Proc, which is called either before or after the
/// render operation of the audio unit. If this flag is set, the proc is being called
/// after the render operation is completed.
///
/// **Available** in OS X v10.0 and later.
const POST_RENDER = sys::kAudioUnitRenderAction_PostRender;
/// This flag can be set in a render input callback (or in the audio unit's render
/// operation itself) and is used to indicate that the render buffer contains only
/// silence. It can then be used by the caller as a hint to whether the buffer needs to
/// be processed or not.
///
/// **Available** in OS X v10.2 and later.
const OUTPUT_IS_SILENCE = sys::kAudioUnitRenderAction_OutputIsSilence;
/// This is used with offline audio units (of type 'auol'). It is used when an offline
/// unit is being preflighted, which is performed prior to when the actual offline
/// rendering actions are performed. It is used for those cases where the offline
/// process needs it (for example, with an offline unit that normalizes an audio file,
/// it needs to see all of the audio data first before it can perform its
/// normalization).
///
/// **Available** in OS X v10.3 and later.
const OFFLINE_PREFLIGHT = sys::kAudioOfflineUnitRenderAction_Preflight;
/// Once an offline unit has been successfully preflighted, it is then put into its
/// render mode. This flag is set to indicate to the audio unit that it is now in that
/// state and that it should perform processing on the input data.
///
/// **Available** in OS X v10.3 and later.
const OFFLINE_RENDER = sys::kAudioOfflineUnitRenderAction_Render;
/// This flag is set when an offline unit has completed either its preflight or
/// performed render operation.
///
/// **Available** in OS X v10.3 and later.
const OFFLINE_COMPLETE = sys::kAudioOfflineUnitRenderAction_Complete;
/// If this flag is set on the post-render call an error was returned by the audio
/// unit's render operation. In this case, the error can be retrieved through the
/// `lastRenderError` property and the audio data in `ioData` handed to the post-render
/// notification will be invalid.
///
/// **Available** in OS X v10.5 and later.
const POST_RENDER_ERROR = sys::kAudioUnitRenderAction_PostRenderError;
/// If this flag is set, then checks that are done on the arguments provided to render
/// are not performed. This can be useful to use to save computation time in situations
/// where you are sure you are providing the correct arguments and structures to the
/// various render calls.
///
/// **Available** in OS X v10.7 and later.
const DO_NOT_CHECK_RENDER_ARGS = sys::kAudioUnitRenderAction_DoNotCheckRenderArgs;
}
}
/// A safe handle around the `AudioUnitRenderActionFlags` pointer provided by the render
/// callback.
///
/// This type lets a callback provide various hints to the audio unit.
///
/// For example: if there is no audio to process, we can insert the `OUTPUT_IS_SILENCE` flag to
/// indicate to the audio unit that the buffer does not need to be processed.
pub struct Handle {
ptr: *mut sys::AudioUnitRenderActionFlags,
}
impl fmt::Debug for Handle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.ptr.is_null() {
write!(f, "{:?}", self.ptr)
} else {
unsafe { write!(f, "{:?}", *self.ptr) }
}
}
}
impl Handle {
/// Retrieve the current state of the `ActionFlags`.
pub fn get(&self) -> ActionFlags {
ActionFlags::from_bits_truncate(unsafe { *self.ptr })
}
fn set(&mut self, flags: ActionFlags) {
unsafe { *self.ptr = flags.bits() }
}
/// The raw value of the flags currently stored.
pub fn bits(&self) -> u32 {
self.get().bits()
}
/// Returns `true` if no flags are currently stored.
pub fn is_empty(&self) -> bool {
self.get().is_empty()
}
/// Returns `true` if all flags are currently stored.
pub fn is_all(&self) -> bool {
self.get().is_all()
}
/// Returns `true` if there are flags common to both `self` and `other`.
pub fn intersects(&self, other: ActionFlags) -> bool {
self.get().intersects(other)
}
/// Returns `true` if all of the flags in `other` are contained within `self`.
pub fn contains(&self, other: ActionFlags) -> bool {
self.get().contains(other)
}
/// Insert the specified flags in-place.
pub fn insert(&mut self, other: ActionFlags) {
let mut flags = self.get();
flags.insert(other);
self.set(flags);
}
/// Remove the specified flags in-place.
pub fn remove(&mut self, other: ActionFlags) {
let mut flags = self.get();
flags.remove(other);
self.set(flags);
}
/// Toggles the specified flags in-place.
pub fn toggle(&mut self, other: ActionFlags) {
let mut flags = self.get();
flags.toggle(other);
self.set(flags);
}
/// Wrap the given pointer with a `Handle`.
pub fn from_ptr(ptr: *mut sys::AudioUnitRenderActionFlags) -> Self {
Handle { ptr }
}
}
unsafe impl Send for Handle {}
impl ::std::fmt::Display for ActionFlags {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(
f,
"{:?}",
match self.bits() {
sys::kAudioUnitRenderAction_PreRender => "PRE_RENDER",
sys::kAudioUnitRenderAction_PostRender => "POST_RENDER",
sys::kAudioUnitRenderAction_OutputIsSilence => "OUTPUT_IS_SILENCE",
sys::kAudioOfflineUnitRenderAction_Preflight => "OFFLINE_PREFLIGHT",
sys::kAudioOfflineUnitRenderAction_Render => "OFFLINE_RENDER",
sys::kAudioOfflineUnitRenderAction_Complete => "OFFLINE_COMPLETE",
sys::kAudioUnitRenderAction_PostRenderError => "POST_RENDER_ERROR",
sys::kAudioUnitRenderAction_DoNotCheckRenderArgs => "DO_NOT_CHECK_RENDER_ARGS",
_ => "<Unknown ActionFlags>",
}
)
}
}
}
impl AudioUnit {
/// Pass a render callback (aka "Input Procedure") to the **AudioUnit**.
pub fn set_render_callback<F, D>(&mut self, mut f: F) -> Result<(), Error>
where
F: FnMut(Args<D>) -> Result<(), ()> + 'static,
D: Data,
{
// First, we'll retrieve the stream format so that we can ensure that the given callback
// format matches the audio unit's format.
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = self.get_property(id, Scope::Input, Element::Output)?;
let stream_format = super::StreamFormat::from_asbd(asbd)?;
// If the stream format does not match, return an error indicating this.
if !D::does_stream_format_match(&stream_format) {
return Err(Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat);
}
// Here, we call the given render callback function within a closure that matches the
// arguments of the required coreaudio "input_proc".
//
// This allows us to take advantage of rust's type system and provide format-specific
// `Args` types which can be checked at compile time.
let input_proc_fn = move |io_action_flags: *mut sys::AudioUnitRenderActionFlags,
in_time_stamp: *const sys::AudioTimeStamp,
in_bus_number: sys::UInt32,
in_number_frames: sys::UInt32,
io_data: *mut sys::AudioBufferList|
-> sys::OSStatus {
let args = unsafe {
let data = D::from_input_proc_args(in_number_frames, io_data);
let flags = action_flags::Handle::from_ptr(io_action_flags);
Args {
data,
time_stamp: *in_time_stamp,
flags,
bus_number: in_bus_number as u32,
num_frames: in_number_frames as usize,
}
};
match f(args) {
Ok(()) => 0,
Err(()) => error::Error::Unspecified.as_os_status(),
}
};
let input_proc_fn_wrapper = Box::new(InputProcFnWrapper {
callback: Box::new(input_proc_fn),
});
// Setup render callback. Notice that we relinquish ownership of the Callback
// here so that it can be used as the C render callback via a void pointer.
// We do however store the *mut so that we can convert back to a Box<InputProcFnWrapper>
// within our AudioUnit's Drop implementation (otherwise it would leak).
let input_proc_fn_wrapper_ptr = Box::into_raw(input_proc_fn_wrapper) as *mut c_void;
let render_callback = sys::AURenderCallbackStruct {
inputProc: Some(input_proc),
inputProcRefCon: input_proc_fn_wrapper_ptr,
};
self.set_property(
sys::kAudioUnitProperty_SetRenderCallback,
Scope::Input,
Element::Output,
Some(&render_callback),
)?;
self.free_render_callback();
self.maybe_render_callback = Some(input_proc_fn_wrapper_ptr as *mut InputProcFnWrapper);
Ok(())
}
/// Pass an input callback (aka "Input Procedure") to the **AudioUnit**.
pub fn set_input_callback<F, D>(&mut self, mut f: F) -> Result<(), Error>
where
F: FnMut(Args<D>) -> Result<(), ()> + 'static,
D: Data,
{
// First, we'll retrieve the stream format so that we can ensure that the given callback
// format matches the audio unit's format.
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = self.get_property(id, Scope::Output, Element::Input)?;
let stream_format = super::StreamFormat::from_asbd(asbd)?;
// If the stream format does not match, return an error indicating this.
if !D::does_stream_format_match(&stream_format) {
return Err(Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat);
}
// Interleaved or non-interleaved?
let non_interleaved = stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED);
// Pre-allocate a buffer list for input stream.
//
// First, get the current buffer size for pre-allocating the `AudioBuffer`s.
#[cfg(target_os = "macos")]
let mut buffer_frame_size: u32 = {
let id = sys::kAudioDevicePropertyBufferFrameSize;
let buffer_frame_size: u32 = self.get_property(id, Scope::Global, Element::Output)?;
buffer_frame_size
};
#[cfg(target_os = "ios")]
let mut buffer_frame_size: u32 = {
let id = sys::kAudioSessionProperty_CurrentHardwareIOBufferDuration;
let seconds: f32 = super::audio_session_get_property(id)?;
let id = sys::kAudioSessionProperty_CurrentHardwareSampleRate;
let sample_rate: f64 = super::audio_session_get_property(id)?;
(sample_rate * seconds as f64).round() as u32
};
let sample_bytes = stream_format.sample_format.size_in_bytes();
let n_channels = stream_format.channels;
if non_interleaved && n_channels > 1 {
return Err(Error::NonInterleavedInputOnlySupportsMono);
}
let data_byte_size = buffer_frame_size * sample_bytes as u32 * n_channels;
let mut data = vec![0u8; data_byte_size as usize];
let mut buffer_capacity = data_byte_size as usize;
let audio_buffer = sys::AudioBuffer {
mDataByteSize: data_byte_size,
mNumberChannels: n_channels,
mData: data.as_mut_ptr() as *mut _,
};
// Relieve ownership of the `Vec` until we're ready to drop the `AudioBufferList`.
// TODO: This leaks the len & capacity fields, since only the buffer pointer is released
mem::forget(data);
let audio_buffer_list = Box::new(sys::AudioBufferList {
mNumberBuffers: 1,
mBuffers: [audio_buffer],
});
// Relinquish ownership of the audio buffer list. Instead, we'll store a raw pointer and
// convert it back into a `Box` when `free_input_callback` is next called.
let audio_buffer_list_ptr = Box::into_raw(audio_buffer_list);
// Here, we call the given input callback function within a closure that matches the
// arguments of the required coreaudio "input_proc".
//
// This allows us to take advantage of rust's type system and provide format-specific
// `Args` types which can be checked at compile time.
let audio_unit = self.instance;
let input_proc_fn = move |io_action_flags: *mut sys::AudioUnitRenderActionFlags,
in_time_stamp: *const sys::AudioTimeStamp,
in_bus_number: sys::UInt32,
in_number_frames: sys::UInt32,
_io_data: *mut sys::AudioBufferList|
-> sys::OSStatus {
// If the buffer size has changed, ensure the AudioBuffer is the correct size.
if buffer_frame_size != in_number_frames {
unsafe {
// Retrieve the up-to-date stream format.
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd =
match super::get_property(audio_unit, id, Scope::Output, Element::Input) {
Err(err) => return err.as_os_status(),
Ok(asbd) => asbd,
};
let stream_format = match super::StreamFormat::from_asbd(asbd) {
Err(err) => return err.as_os_status(),
Ok(fmt) => fmt,
};
let sample_bytes = stream_format.sample_format.size_in_bytes();
let n_channels = stream_format.channels;
let data_byte_size =
in_number_frames as usize * sample_bytes * n_channels as usize;
let ptr = (*audio_buffer_list_ptr).mBuffers.as_ptr() as *mut sys::AudioBuffer;
let len = (*audio_buffer_list_ptr).mNumberBuffers as usize;
let buffers: &mut [sys::AudioBuffer] = slice::from_raw_parts_mut(ptr, len);
let old_capacity = buffer_capacity;
for buffer in buffers {
let current_len = buffer.mDataByteSize as usize;
let audio_buffer_ptr = buffer.mData as *mut u8;
let mut vec: Vec<u8> =
Vec::from_raw_parts(audio_buffer_ptr, current_len, old_capacity);
vec.resize(data_byte_size, 0u8);
buffer_capacity = vec.capacity();
buffer.mData = vec.as_mut_ptr() as *mut _;
buffer.mDataByteSize = data_byte_size as u32;
mem::forget(vec);
}
}
buffer_frame_size = in_number_frames;
}
unsafe {
let status = sys::AudioUnitRender(
audio_unit,
io_action_flags,
in_time_stamp,
in_bus_number,
in_number_frames,
audio_buffer_list_ptr,
);
if status != 0 {
return status;
}
}
let args = unsafe {
let data = D::from_input_proc_args(in_number_frames, audio_buffer_list_ptr);
let flags = action_flags::Handle::from_ptr(io_action_flags);
Args {
data,
time_stamp: *in_time_stamp,
flags,
bus_number: in_bus_number as u32,
num_frames: in_number_frames as usize,
}
};
match f(args) {
Ok(()) => 0,
Err(()) => error::Error::Unspecified.as_os_status(),
}
};
let input_proc_fn_wrapper = Box::new(InputProcFnWrapper {
callback: Box::new(input_proc_fn),
});
// Setup input callback. Notice that we relinquish ownership of the Callback
// here so that it can be used as the C render callback via a void pointer.
// We do however store the *mut so that we can convert back to a Box<InputProcFnWrapper>
// within our AudioUnit's Drop implementation (otherwise it would leak).
let input_proc_fn_wrapper_ptr = Box::into_raw(input_proc_fn_wrapper) as *mut c_void;
let render_callback = sys::AURenderCallbackStruct {
inputProc: Some(input_proc),
inputProcRefCon: input_proc_fn_wrapper_ptr,
};
self.set_property(
sys::kAudioOutputUnitProperty_SetInputCallback,
Scope::Global,
Element::Output,
Some(&render_callback),
)?;
let input_callback = super::InputCallback {
buffer_list: audio_buffer_list_ptr,
callback: input_proc_fn_wrapper_ptr as *mut InputProcFnWrapper,
};
self.free_input_callback();
self.maybe_input_callback = Some(input_callback);
Ok(())
}
/// Retrieves ownership over the render callback and returns it where it can be re-used or
/// safely dropped.
pub fn free_render_callback(&mut self) -> Option<Box<InputProcFnWrapper>> {
if let Some(callback) = self.maybe_render_callback.take() {
// Here, we transfer ownership of the callback back to the current scope so that it
// is dropped and cleaned up. Without this line, we would leak the Boxed callback.
let callback: Box<InputProcFnWrapper> = unsafe { Box::from_raw(callback) };
return Some(callback);
}
None
}
/// Retrieves ownership over the input callback and returns it where it can be re-used or
/// safely dropped.
pub fn free_input_callback(&mut self) -> Option<Box<InputProcFnWrapper>> {
if let Some(input_callback) = self.maybe_input_callback.take() {
let super::InputCallback {
buffer_list,
callback,
} = input_callback;
unsafe {
// Take ownership over the AudioBufferList in order to safely free it.
let buffer_list: Box<sys::AudioBufferList> = Box::from_raw(buffer_list);
// Free the allocated data from the individual audio buffers.
let ptr = buffer_list.mBuffers.as_ptr() as *const sys::AudioBuffer;
let len = buffer_list.mNumberBuffers as usize;
let buffers: &[sys::AudioBuffer] = slice::from_raw_parts(ptr, len);
for &buffer in buffers {
let ptr = buffer.mData as *mut u8;
let len = buffer.mDataByteSize as usize;
let cap = len;
let _ = Vec::from_raw_parts(ptr, len, cap);
}
// Take ownership over the callback so that it can be freed.
let callback: Box<InputProcFnWrapper> = Box::from_raw(callback);
return Some(callback);
}
}
None
}
}
/// Callback procedure that will be called each time our audio_unit requests audio.
extern "C" fn input_proc(
in_ref_con: *mut c_void,
io_action_flags: *mut sys::AudioUnitRenderActionFlags,
in_time_stamp: *const sys::AudioTimeStamp,
in_bus_number: sys::UInt32,
in_number_frames: sys::UInt32,
io_data: *mut sys::AudioBufferList,
) -> sys::OSStatus {
let wrapper = in_ref_con as *mut InputProcFnWrapper;
unsafe {
(*(*wrapper).callback)(
io_action_flags,
in_time_stamp,
in_bus_number,
in_number_frames,
io_data,
)
}
}

View File

@@ -0,0 +1,103 @@
use super::audio_format::{self, LinearPcmFlags};
/// Dynamic representation of audio data sample format.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SampleFormat {
/// 32-bit float.
F32,
/// 32-bit signed integer.
I32,
/// 24-bit signed integer. Can be packed or not depending on the flags.
I24,
/// 16-bit signed integer.
I16,
/// 8-bit signed integer.
I8,
}
impl SampleFormat {
/// Check if the format flags are appropriate for the given format.
pub fn does_match_flags(&self, flags: audio_format::LinearPcmFlags) -> bool {
let is_float = flags.contains(LinearPcmFlags::IS_FLOAT);
let is_signed_integer = flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER);
let is_packed = flags.contains(LinearPcmFlags::IS_PACKED);
match *self {
SampleFormat::F32 => is_float && !is_signed_integer && is_packed,
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
is_signed_integer && !is_float && is_packed
}
SampleFormat::I24 => is_signed_integer && !is_float,
}
}
/// Convert format flags and bits_per_sample to a SampleFormat.
pub fn from_flags_and_bits_per_sample(
flags: audio_format::LinearPcmFlags,
bits_per_sample: u32,
) -> Option<Self> {
let packed = flags.contains(LinearPcmFlags::IS_PACKED);
let sample_format = if flags.contains(LinearPcmFlags::IS_FLOAT) {
match (bits_per_sample, packed) {
(32, true) => SampleFormat::F32,
_ => return None,
}
} else if flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER) {
match (bits_per_sample, packed) {
(8, true) => SampleFormat::I8,
(16, true) => SampleFormat::I16,
(24, _) => SampleFormat::I24,
(32, true) => SampleFormat::I32,
_ => return None,
}
} else {
// TODO: Check whether or not we need to consider other formats, like unsigned ints.
return None;
};
Some(sample_format)
}
/// Return the size of one sample in bytes, assuming that the format is packed.
pub fn size_in_bytes(&self) -> usize {
use std::mem::size_of;
match *self {
SampleFormat::F32 => size_of::<f32>(),
SampleFormat::I32 => size_of::<i32>(),
SampleFormat::I24 => 3 * size_of::<u8>(),
SampleFormat::I16 => size_of::<i16>(),
SampleFormat::I8 => size_of::<i8>(),
}
}
/// Return the number of valid bits for one sample.
pub fn size_in_bits(&self) -> u32 {
match *self {
SampleFormat::F32 => 32,
SampleFormat::I32 => 32,
SampleFormat::I24 => 24,
SampleFormat::I16 => 16,
SampleFormat::I8 => 8,
}
}
}
/// Audio data sample types.
pub trait Sample {
/// Dynamic representation of audio data sample format.
fn sample_format() -> SampleFormat;
}
/// Simplified implementation of the `Sample` trait for sample types.
/// This is only implemented for the sample types that map directly to a numeric type.
macro_rules! impl_sample {
($($T:ident $format:ident),* $(,)*) => {
$(
impl Sample for $T {
fn sample_format() -> SampleFormat {
SampleFormat::$format
}
}
)*
}
}
impl_sample!(f32 F32, i32 I32, i16 I16, i8 I8);

View File

@@ -0,0 +1,144 @@
//! A rustification of the `AudioStreamBasicDescription` type.
//!
//! Find the original `AudioStreamBasicDescription` reference [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/c/tdef/AudioStreamBasicDescription).
use super::audio_format::AudioFormat;
use super::audio_format::LinearPcmFlags;
use super::SampleFormat;
use crate::error::{self, Error};
use sys;
/// A representation of the AudioStreamBasicDescription specifically for use with the AudioUnit API.
///
/// By using a type specific to the audio unit API, we can remove a lot of unnecessary boilerplate
/// that is normally associated with the AudioStreamBasicDescription.
///
/// Seeing as `LinearPCM` data (the `AudioFormat` used by the `AudioUnit` API) implies a single
/// frame per packet, we can infer many of the fields in an ASBD from the sample type.
///
/// `bytes_per_packet = size_of::<S>()`
/// `bytes_per_frame = size_of::<S>()`
/// `frames_per_packet` = 1
/// `bits_per_channel = size_of::<S>()` / channels_per_frame * 8
///
/// > A *packet* is a collection of one or more contiguous frames. In linear PCM audio, a packet is
/// always a single frame.
///
/// [from *Core Audio Overview*](https://developer.apple.com/library/ios/documentation/MusicAudio/Conceptual/CoreAudioOverview/WhatisCoreAudio/WhatisCoreAudio.html)
///
/// > The canonical formats in Core Audio are as follows:
/// >
/// > - iOS input and output: Linear PCM with 16-bit integer samples.
/// > - iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit
/// fixed-point samples
/// > - Mac input and output: Linear PCM with 32-bit floating point samples.
/// > - Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit floating
/// point samples.
#[derive(Copy, Clone, Debug)]
pub struct StreamFormat {
/// The number of frames of audio data per second used to represent a signal.
pub sample_rate: f64,
/// The sample format used to represent the audio data.
///
/// In OS X, Core Audio expects audio data to be in native-endian, 32-bit floating-point,
/// linear PCM format.
///
/// iOS uses integer and fixed-point audio data. The result is faster calculations and less
/// battery drain when processing audio. iOS provides a Converter audio unit and inclues the
/// interfaces from Audio Converter Services (TODO: look into exposing this).
pub sample_format: SampleFormat,
/// The format flags for the given StreamFormat.
pub flags: super::audio_format::LinearPcmFlags,
/// The number of channels.
pub channels: u32,
}
impl StreamFormat {
/// Convert an AudioStreamBasicDescription into a StreamFormat.
///
/// Note: `audio_unit::StreamFormat` exclusively uses the `LinearPCM` `AudioFormat`. This is as
/// specified in the documentation:
///
/// > Specify kAudioFormatLinearPCM for the mFormatID field. Audio units use uncompressed audio
/// data, so this is the correct format identifier to use whenever you work with audio units.
///
/// [*Audio Unit Hosting Guide for iOS*](https://developer.apple.com/library/ios/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html)
///
/// Returns an `Error` if the `AudioFormat` inferred by the ASBD is not `LinearPCM`.
///
/// Returns an `Error` if the sample format of the asbd cannot be matched to a format supported by SampleFormat.
#[allow(non_snake_case)]
pub fn from_asbd(asbd: sys::AudioStreamBasicDescription) -> Result<StreamFormat, Error> {
const NOT_SUPPORTED: Error = Error::AudioUnit(error::audio_unit::Error::FormatNotSupported);
let sys::AudioStreamBasicDescription {
mSampleRate,
mFormatID,
mFormatFlags,
mBytesPerFrame: _,
mChannelsPerFrame,
mBitsPerChannel,
..
} = asbd;
// Retrieve the LinearPCM flags.
let flags = match AudioFormat::from_format_and_flag(mFormatID, Some(mFormatFlags)) {
Some(AudioFormat::LinearPCM(flags)) => flags,
_ => return Err(NOT_SUPPORTED),
};
// Determine the `SampleFormat` to use.
let sample_format =
match SampleFormat::from_flags_and_bits_per_sample(flags, mBitsPerChannel) {
Some(sample_format) => sample_format,
None => return Err(NOT_SUPPORTED),
};
let channels = mChannelsPerFrame;
Ok(StreamFormat {
sample_rate: mSampleRate,
flags,
sample_format,
channels,
})
}
/// Convert a StreamFormat into an AudioStreamBasicDescription.
/// Note that this function assumes that only packed formats are used.
/// This only affects I24, since all other formats supported by `StreamFormat`
/// are always packed.
pub fn to_asbd(self) -> sys::AudioStreamBasicDescription {
let StreamFormat {
sample_rate,
flags,
sample_format,
channels,
} = self;
let (format, maybe_flag) =
AudioFormat::LinearPCM(flags | LinearPcmFlags::IS_PACKED).as_format_and_flag();
let flag = maybe_flag.unwrap_or(::std::u32::MAX - 2147483647);
let non_interleaved = flags.contains(LinearPcmFlags::IS_NON_INTERLEAVED);
let bytes_per_frame = if non_interleaved {
sample_format.size_in_bytes() as u32
} else {
sample_format.size_in_bytes() as u32 * channels
};
const FRAMES_PER_PACKET: u32 = 1;
let bytes_per_packet = bytes_per_frame * FRAMES_PER_PACKET;
let bits_per_channel = sample_format.size_in_bits();
sys::AudioStreamBasicDescription {
mSampleRate: sample_rate,
mFormatID: format,
mFormatFlags: flag,
mBytesPerPacket: bytes_per_packet,
mFramesPerPacket: FRAMES_PER_PACKET,
mBytesPerFrame: bytes_per_frame,
mChannelsPerFrame: channels,
mBitsPerChannel: bits_per_channel,
mReserved: 0,
}
}
}

View File

@@ -0,0 +1,445 @@
//! Core Audio's various const audio unit types identifiers represented as typesafe enums.
//!
//! Oirginal documentation [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Types).
/// Represents the different kinds of Audio Units that are available.
///
/// Original documentation [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Types).
#[derive(Copy, Clone, Debug)]
pub enum Type {
/// Provides input, output, or both input and output simultaneously.
///
/// It can be used as the head of an audio unit processing graph.
///
/// **Available** in OS X v10.2 and later.
IO(IOType),
/// An instrument unit can be used as a software musical instrument, such as a sampler or
/// synthesizer.
///
/// It responds to MIDI (Musical Instrument Digital Interface) control signals and can create
/// notes.
///
/// **Available** in OS X v10.2 and later.
MusicDevice(MusicDeviceType),
/// An effect unit that can respond to MIDI control messages, typically through a mapping of
/// MIDI messages to parameters of the audio unit's DSP algorithm.
///
/// **Available** in OS X v10.2 and later.
MusicEffect,
/// A format converter unit can transform audio formats, such as performing sample rate
/// conversion.
///
/// A format converter is also appropriate for dferred rendering and for effects such as
/// varispeed.
///
/// A format converter unit can ask for as much or as little audio input as it needs to produce
/// a given output, while still completing its rendering within the time represented by the
/// output buffer.
///
/// For effect-like format converters, such as pitch shifters, it is common to provide both a
/// real-time and an offline version. OS X, for example, includes Time-Pitch and Varispeed
/// audio units in both real-time and offline versions.
///
/// **Available** in OS X v10.2 and later.
FormatConverter(FormatConverterType),
/// An effect unit repeatedly processes a number of audio input samples to produce the same
/// number of audio output samples.
///
/// Most commonly, an effect unit has a single input and a single output.
///
/// Some effects take side-chain inputs as well.
///
/// Effect units can be run offline, such as to process a file without playing it, but are
/// expected to run in real-time.
///
/// **Available** in OS X v10.2 and later.
Effect(EffectType),
/// A mixer unit takes a number of input channels and mixes them to provide one or more output
/// channels.
///
/// For example, the **StereoMixer** **SubType** in OS X takes multiple mono or stereo inputs
/// and produces a single stereo output.
///
/// **Available** in OS X v10.2 and later.
Mixer(MixerType),
/// A panner unit is a specialised effect unit that distributes one or more channels in a
/// single input to one or more channels in a single output.
///
/// Panner units must support a set of standard audio unit parameters that specify panning
/// coordinates.
///
/// **Available** in OS X v10.3 and later.
Panner,
/// A generator unit provides audio output that has no audio input.
///
/// This audio unit type is appropriate for a tone generator.
///
/// Unlike an instrument unit, a generator unit does not have a control input.
///
/// **Available** in OS X v10.3 and later.
Generator(GeneratorType),
/// An offline effect unit provides digital signal processing of a sort that cannot proceed in
/// real-time.
///
/// For example, level normalisation requires examination of an entire sound, beginning to end,
/// before the normalisation factor can be calculated.
///
/// As such, offline effect units also have a notion of a priming stage that can be performed
/// before the actual rendering/processing phase is executed.
///
/// **Available** in OS X v10.3 and later.
OfflineEffect,
/// FIXME: Could not find any documenation for this type - it seems it was added very recently
/// (around 2013) and Apple's documentation doesn't seem to have updated to include it.
MidiProcessor,
}
impl Type {
/// Convert the `Type` to its associated `u32` for compatibility with original API.
pub fn as_u32(&self) -> u32 {
match *self {
Type::IO(_) => 1635086197,
Type::MusicDevice(_) => 1635085685,
Type::MusicEffect => 1635085670,
Type::FormatConverter(_) => 1635083875,
Type::Effect(_) => 1635083896,
Type::Mixer(_) => 1635085688,
Type::Panner => 1635086446,
Type::Generator(_) => 1635084142,
Type::OfflineEffect => 1635086188,
Type::MidiProcessor => 1635085673,
}
}
/// Convert the `Type` to the const `u32` that is associated with its subtype.
pub fn as_subtype_u32(&self) -> Option<u32> {
match *self {
Type::IO(ty) => Some(ty as u32),
Type::MusicDevice(ty) => Some(ty as u32),
Type::FormatConverter(ty) => Some(ty as u32),
Type::Effect(ty) => Some(ty as u32),
Type::Mixer(ty) => Some(ty as u32),
Type::Generator(ty) => Some(ty as u32),
_ => None,
}
}
}
impl From<EffectType> for Type {
fn from(ty: EffectType) -> Self {
Type::Effect(ty)
}
}
impl From<FormatConverterType> for Type {
fn from(ty: FormatConverterType) -> Self {
Type::FormatConverter(ty)
}
}
impl From<MixerType> for Type {
fn from(ty: MixerType) -> Self {
Type::Mixer(ty)
}
}
impl From<GeneratorType> for Type {
fn from(ty: GeneratorType) -> Self {
Type::Generator(ty)
}
}
impl From<MusicDeviceType> for Type {
fn from(ty: MusicDeviceType) -> Self {
Type::MusicDevice(ty)
}
}
impl From<IOType> for Type {
fn from(ty: IOType) -> Self {
Type::IO(ty)
}
}
/// Effect (digital signal processing) audio unit subtypes for audio units provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum EffectType {
/// An audio unit that enforces an upper dynamic limit on an audio signal.
///
/// **Available** in OS X v10.2 and later.
PeakLimiter = 1819112562,
/// An audio unit that provides dynamic compression or expansion.
///
/// **Available** in OS X v10.3 and later.
DynamicsProcessor = 1684237680,
/// An audio unit that passes frequencies below a specified cutoff frequency and blocks
/// frequencies above that cutoff frequency.
///
/// **Available** in OS X v10.2 and later.
LowPassFilter = 1819304307,
/// An audio unit that passes frequencies above a specified cutoff frequency and blocks
/// frequencies below that cutoff frequency.
///
/// **Available** in OS X v10.2 and later.
HighPassFilter = 1752195443,
/// An audio unit that passes frequencies between specified upper and lower cutoff frequencies,
/// and blocks frequencies outside that band.
///
/// **Available** in OS X v10.2 and later.
BandPassFilter = 1651532147,
/// An audio unit suitable for implementing a treble control in an audio playback or recording
/// system.
///
/// **Available** in OS X v10.2 and later.
HighShelfFilter = 1752393830,
/// An audio unit suitable for implementing a bass control in an audio playback or recording
/// system.
///
/// **Available** in OS X v10.2 and later.
LowShelfFilter = 1819502694,
/// An audio unit that provides a filter whose center frequency, boost/cut level, and Q can be
/// adjusted.
///
/// **Available** in OS X v10.2 and later.
ParametricEQ = 1886217585,
/// An audio unit that provides a distortion effect.
///
/// **Available** in OS X v10.5 and later.
Distortion = 1684632436,
/// An audio unit that introduces a time delay to a signal.
///
/// **Available** in OS X v10.2 and later.
Delay = 1684368505,
/// An audio unit that provides a time delay for a specified number of samples.
///
/// **Available** in OS X v10.4 and later.
SampleDelay = 1935961209,
/// An audio unit that provides a 10- or 31-band graphic equalizer.
///
/// Available in OS X v10.2 and later.
GraphicEQ = 1735550321,
/// An audio unit that provides four-bands of dynamic compression or expansion.
///
/// **Available** in OS X v10.3 and later.
MultiBandCompressor = 1835232624,
/// An audio unit that provides a reverberation effect that can be used to simulate a variety
/// of acoustic spaces.
///
/// **Available** in OS X v10.2 and later.
MatrixReverb = 1836213622,
/// An audio unit for modifying the pitch of a signal.
///
/// **Available** in OS X v10.4 and later.
Pitch = 1953329268,
/// An audio unit that provides a combination of five filters: low-frequency, three
/// mid-frequencies, and high-frequency.
///
/// **Available** in OS X v10.4 and later.
AUFilter = 1718185076,
/// An audio unit for use in conjunction with a kAudioUnitSubType_NetReceive audio unit for
/// sending audio across a network or from one application to another.
///
/// **Available** in OS X v10.4 and later.
NetSend = 1853058660,
/// An audio unit that detects gaps between segments of speech and fills the gaps with a short
/// tone, simulating the sound of a walkie-talkie communication device.
///
/// **Available** in OS X v10.5 and later.
RogerBeep = 1919903602,
/// A multi-band equalizer with specifiable filter type for each band.
///
/// **Available** in OS X v10.9 and later.
NBandEQ = 1851942257,
}
/// Audio data format converter audio unit subtypes for **AudioUnit**s provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FormatConverterType {
/// An audio unit that uses an audio converter to do linear PCM conversions, such as changes to
/// sample rate, bit depth, or interleaving.
///
/// **Available** in OS X v10.2 and later.
AUConverter = 1668247158,
/// An audio unit that can be used to have independent control of both playback rate and pitch.
///
/// In OS X it provides a generic view, so it can be used in both a UI and programmatic
/// context.
///
/// It also comes in an offline version for processing audio files.
///
/// **Available** in OS X v10.7 and later.
NewTimePitch = 1853191280,
/// An audio unit that can provide independent control of playback rate and pitch. This subtype
/// provides a generic view, making it suitable for UI and programmatic context. OS X provides
/// realtime and offline audio units of this subtype.
///
/// **Available** in OS X v10.3 and later.
TimePitch = 1953329268,
/// An audio unit that acquires audio input from a separate thread than the thread on which its
/// render method is called.
///
/// You can use this subtype to introduce multiple threads into an audio unit processing graph.
///
/// There is a delay, equal to the buffer size, introduced between the audio input and output.
///
/// **Available** in OS X v10.4 and later.
DeferredRenderer = 1684366962,
/// An audio unit with one input bus and two output buses. The audio unit duplicates the input
/// signal to each of its two output buses.
///
/// **Available** in OS X v10.4 and later.
Splitter = 1936747636,
/// An audio unit with two input buses and one output bus. The audio unit merges the two input
/// signals to the single output.
///
/// **Available** in OS X v10.4 and later.
Merger = 1835364967,
/// An audio unit that can control playback rate. As the playback rate increases, so does
/// pitch.
///
/// This subtype provides a generic view, making it suitable for UI and programmatic context.
///
/// OS X provides realtime and offline audio units of this subtype.
///
/// **Available** in OS X v10.3 and later.
Varispeed = 1986097769,
/// **Available** in OS X v10.9 and later.
AUiPodTimeOther = 1768977519,
}
/// Audio mixing **AudioUnit** subtypes for **AudioUnit**s provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MixerType {
/// An audio unit that can have any number of input buses, with any number of channels on each
/// input bus, and one output bus.
///
/// In OS X, the output bus can have any number of channels.
///
/// In iPhone OS, the output bus always has two channels.
///
/// **Available** in OS X v10.5 and later.
MultiChannelMixer = 1835232632,
/// An audio unit that can have any number of input buses, each of which is mono or stereo, and
/// one stereo output bus.
///
/// **Available** in OS X v10.2 and later.
StereoMixer = 1936554098,
/// An audio unit that can have any number of input buses and one output bus.
///
/// Each input bus can be mono, in which case it can be panned using 3D coordinates and
/// parameters.
///
/// Stereo input buses pass directly through to the output.
///
/// Four-channel ambisonic inputs are rendered to the output configuration.
///
/// The single output bus can be configured with 2, 4, 5, 6, 7 or 8 channels.
///
/// **Available** in OS X v10.3 and later.
///
/// **Deprecated** in OS X v10.10.
Mixer3D = 862219640,
/// An audio unit that can have any number of input and output buses with any number of
/// channels on each bus.
///
/// You configure the mix using a matrix of channels with a separate input level control for
/// each channel.
///
/// The audio unit also provides individual level control for each
/// input-channel-to-output-channel combination, as well as level control for each output
/// channel.
///
/// Finally, the audio unit provides a global level control for the matrix as a whole.
///
/// **Available** in OS X v10.3 and later.
MatrixMixer = 1836608888,
}
/// Audio units that serve as sound sources.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum GeneratorType {
/// A generator unit that can be used to schedule slices of audio to be played at specified
/// times.
///
/// The audio is scheduled using the time stamps for the render operation and can be scheduled
/// from any thread.
///
/// **Available** in OS X v10.4 and later.
ScheduledSoundPlayer = 1936945260,
/// A generator unit that is used to play a file. In OS X it presents a custom UI so can be
/// used in a UI context as well as in a programmatic context.
///
/// **Available** in OS X v10.4 and later.
AudioFilePlayer = 1634103404,
}
/// Audio units that can be played as musical instruments via MIDI control.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MusicDeviceType {
/// A multitimbral instrument unit that can use sample banks in either DLS or SoundFont
/// formats.
///
/// It fully supports GM-MIDI and the basic extensions of GS-MIDI
///
/// **Available** in OS X v10.2 and later.
DLSSynth = 1684828960,
/// A monotimbral instrument unit that functions a a sampler-synthesizer and supports full
/// interactive editing of its state.
///
/// **Available** in OS X v10.7 and later.
Sampler = 1935764848,
}
/// Input/output **AudioUnit** subtypes for **AudioUnit**s provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum IOType {
/// An audio unit that responds to start/stop calls and provides basic services for converting
/// to and from linear PCM formats.
///
/// Use this audio unit when sending the output of an audio processing graph to your
/// application rather than to the output audio hardware. You would typically use the Generic
/// Output unit for offline audio processing. Just like the other I/O units, the Generic Output
/// unit incorporates a Format Converter unit. This lets the Generic Output unit perform format
/// conversion between the stream format used in an audio processing graph and the format you
/// want.
///
/// You can also use a Generic Output unit as the final node in a subgraph that you place into
/// a parent audio processing graph.
///
/// **Available** in OS X v10.2 and later.
GenericOutput = 1734700658,
/// An audio unit that can provides input/output connection to an a specified audio device.
///
/// Bus 0 provides output to the audio device and bus 1 accepts input from the audio device.
///
/// **Available** in OS X v10.2 and later.
HalOutput = 1634230636,
/// A specialized **HalOutput** audio unit that connects to the users selected default device
/// in Sound Preferences.
///
/// **Available** in OS X v10.2 and later.
DefaultOutput = 1684366880,
/// A specialized **HalOutput** audio unit that connects to the users selected device for
/// sound effects, alerts, and other user-interface sounds.
///
/// **Available** in OS X v10.2 and later.
SystemOutput = 1937339168,
/// An audio unit that interfaces to the audio inputs and outputs of iPhone OS devices and
/// provides voice processing features.
///
/// Bus 0 provides output to hardware and bus 1 accepts input from hardware.
///
/// See the [Voice-Processing I/O Audio Unit
/// Properties](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Voice_Processing_I_O_Audio_Unit_Properties)
/// enumeration for the identifiers for this audio units properties.
///
/// **Available** in OS X v10.7 and later.
VoiceProcessingIO = 1987078511,
/// Connects to device hardware for input, output, or simultaneous input and output.
/// Use it for playback, recording, or low-latency simultaneous input and output where echo
/// cancelation is not needed.
///
/// See <https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/UsingSpecificAudioUnits/UsingSpecificAudioUnits.html>
/// **Available** in iOS.
RemoteIO = 1919512419,
}

334
vendor/coreaudio-rs/src/error.rs vendored Normal file
View File

@@ -0,0 +1,334 @@
//! This module is an attempt at rustifying the OSStatus result.
pub use self::audio::Error as AudioError;
pub use self::audio_codec::Error as AudioCodecError;
pub use self::audio_format::Error as AudioFormatError;
pub use self::audio_unit::Error as AudioUnitError;
use sys::OSStatus;
pub mod audio {
use sys::OSStatus;
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unimplemented = -4,
FileNotFound = -43,
FilePermission = -54,
TooManyFilesOpen = -42,
BadFilePath = 561017960,
Param = -50,
MemFull = -108,
Unknown,
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
-4 => Err(Error::Unimplemented),
-43 => Err(Error::FileNotFound),
-54 => Err(Error::FilePermission),
-42 => Err(Error::TooManyFilesOpen),
561017960 => Err(Error::BadFilePath),
-50 => Err(Error::Param),
-108 => Err(Error::MemFull),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::Unimplemented => "Unimplemented",
Error::FileNotFound => "File not found",
Error::FilePermission => "File permission",
Error::TooManyFilesOpen => "Too many files open",
Error::BadFilePath => "Bad file path",
Error::Param => "Param",
Error::MemFull => "Memory full",
Error::Unknown => "An unknown error occurred",
};
write!(f, "{}", description)
}
}
}
pub mod audio_codec {
use sys::OSStatus;
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unspecified = 2003329396,
UnknownProperty = 2003332927,
BadPropertySize = 561211770,
IllegalOperation = 1852797029,
UnsupportedFormat = 560226676,
State = 561214580,
NotEnoughBufferSpace = 560100710,
Unknown,
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
2003329396 => Err(Error::Unspecified),
2003332927 => Err(Error::UnknownProperty),
561211770 => Err(Error::BadPropertySize),
1852797029 => Err(Error::IllegalOperation),
560226676 => Err(Error::UnsupportedFormat),
561214580 => Err(Error::State),
560100710 => Err(Error::NotEnoughBufferSpace),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::Unspecified => "Unspecified",
Error::UnknownProperty => "Unknown property",
Error::BadPropertySize => "Bad property size",
Error::IllegalOperation => "Illegal operation",
Error::UnsupportedFormat => "Unsupported format",
Error::State => "State",
Error::NotEnoughBufferSpace => "Not enough buffer space",
Error::Unknown => "Unknown error occurred",
};
write!(f, "{}", description)
}
}
}
pub mod audio_format {
use sys::OSStatus;
// TODO: Finish implementing these values.
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unspecified, // 'what'
UnsupportedProperty, // 'prop'
BadPropertySize, // '!siz'
BadSpecifierSize, // '!spc'
UnsupportedDataFormat = 1718449215, // 'fmt?'
UnknownFormat, // '!fmt'
Unknown, //
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
1718449215 => Err(Error::UnsupportedDataFormat),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::Unspecified => "An unspecified error",
Error::UnsupportedProperty => "The specified property is not supported",
Error::BadPropertySize => "Bad property size",
Error::BadSpecifierSize => "Bad specifier size",
Error::UnsupportedDataFormat => "The specified data format is not supported",
Error::UnknownFormat => "The specified data format is not a known format",
Error::Unknown => "Unknown error occurred",
};
write!(f, "{}", description)
}
}
}
pub mod audio_unit {
use sys::OSStatus;
#[derive(Copy, Clone, Debug)]
pub enum Error {
InvalidProperty = -10879,
InvalidParameter = -10878,
InvalidElement = -10877,
NoConnection = -10876,
FailedInitialization = -10875,
TooManyFramesToProcess = -10874,
InvalidFile = -10871,
FormatNotSupported = -10868,
Uninitialized = -10867,
InvalidScope = -10866,
PropertyNotWritable = -10865,
CannotDoInCurrentContext = -10863,
InvalidPropertyValue = -10851,
PropertyNotInUse = -10850,
Initialized = -10849,
InvalidOfflineRender = -10848,
Unauthorized = -10847,
Unknown,
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
-10879 => Err(Error::InvalidProperty),
-10878 => Err(Error::InvalidParameter),
-10877 => Err(Error::InvalidElement),
-10876 => Err(Error::NoConnection),
-10875 => Err(Error::FailedInitialization),
-10874 => Err(Error::TooManyFramesToProcess),
-10871 => Err(Error::InvalidFile),
-10868 => Err(Error::FormatNotSupported),
-10867 => Err(Error::Uninitialized),
-10866 => Err(Error::InvalidScope),
-10865 => Err(Error::PropertyNotWritable),
-10863 => Err(Error::CannotDoInCurrentContext),
-10851 => Err(Error::InvalidPropertyValue),
-10850 => Err(Error::PropertyNotInUse),
-10849 => Err(Error::Initialized),
-10848 => Err(Error::InvalidOfflineRender),
-10847 => Err(Error::Unauthorized),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::InvalidProperty => "Invalid property",
Error::InvalidParameter => "Invalid parameter",
Error::InvalidElement => "Invalid element",
Error::NoConnection => "No connection",
Error::FailedInitialization => "Failed initialization",
Error::TooManyFramesToProcess => "Too many frames to process",
Error::InvalidFile => "Invalid file",
Error::FormatNotSupported => "Format not supported",
Error::Uninitialized => "Uninitialized",
Error::InvalidScope => "Invalid scope",
Error::PropertyNotWritable => "Property not writable",
Error::CannotDoInCurrentContext => "Cannot do in current context",
Error::InvalidPropertyValue => "Invalid property value",
Error::PropertyNotInUse => "Property not in use",
Error::Initialized => "Initialized",
Error::InvalidOfflineRender => "Invalid offline render",
Error::Unauthorized => "Unauthorized",
Error::Unknown => "Unknown error occurred",
};
write!(f, "{}", description)
}
}
}
/// A wrapper around all possible Core Audio errors.
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unspecified,
SystemSoundClientMessageTimedOut,
NoMatchingDefaultAudioUnitFound,
RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat,
NoKnownSubtype,
NonInterleavedInputOnlySupportsMono,
UnsupportedSampleRate,
UnsupportedStreamFormat,
Audio(AudioError),
AudioCodec(AudioCodecError),
AudioFormat(AudioFormatError),
AudioUnit(AudioUnitError),
Unknown(OSStatus),
}
impl Error {
/// Convert an OSStatus to a std Rust Result.
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
-1500 => Err(Error::Unspecified),
-1501 => Err(Error::SystemSoundClientMessageTimedOut),
_ => {
match AudioError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioError::Unknown) => (),
Err(err) => return Err(Error::Audio(err)),
}
match AudioCodecError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioCodecError::Unknown) => (),
Err(err) => return Err(Error::AudioCodec(err)),
}
match AudioFormatError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioFormatError::Unknown) => (),
Err(err) => return Err(Error::AudioFormat(err)),
}
match AudioUnitError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioUnitError::Unknown) => (),
Err(err) => return Err(Error::AudioUnit(err)),
}
Err(Error::Unknown(os_status))
}
}
}
/// Convert an Error to an OSStatus.
pub fn as_os_status(&self) -> OSStatus {
match *self {
Error::Unspecified => -1500,
Error::NoMatchingDefaultAudioUnitFound => -1500,
Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat => -1500,
Error::SystemSoundClientMessageTimedOut => -1501,
Error::Audio(err) => err as OSStatus,
Error::AudioCodec(err) => err as OSStatus,
Error::AudioUnit(err) => err as OSStatus,
_ => -1500,
}
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
Error::Unspecified => write!(f, "An unspecified error has occurred"),
Error::NoMatchingDefaultAudioUnitFound => write!(f, "No matching default audio unit found"),
Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat =>
write!(f, "The given render callback buffer format does not match the `AudioUnit` `StreamFormat`"),
Error::SystemSoundClientMessageTimedOut => write!(f, "The system sound client message timed out"),
Error::NoKnownSubtype => write!(f, "The type has no known subtypes"),
Error::NonInterleavedInputOnlySupportsMono => write!(f, "In non-interleaved mode input only supports one channel"),
Error::UnsupportedSampleRate => write!(f, "The requested sample rate is not available"),
Error::UnsupportedStreamFormat => write!(f, "The requested stream format is not available"),
Error::Audio(ref err) => write!(f, "{}", err),
Error::AudioCodec(ref err) => write!(f, "{}", err),
Error::AudioFormat(ref err) => write!(f, "{}", err),
Error::AudioUnit(ref err) => write!(f, "{}", err),
Error::Unknown(_) => write!(f, "An unknown error unknown to the coreaudio-rs API occurred"),
}
}
}

20
vendor/coreaudio-rs/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,20 @@
//! coreaudio-rs
//! ------------
//!
//! A friendly rust interface for Apple's CoreAudio API.
//!
//! Read the CoreAudio Overview [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html).
//!
//! Currently, work has only been started on the [audio_unit](./audio_unit/index.html) module, but
//! eventually we'd like to cover at least the majority of the C API.
#[macro_use]
extern crate bitflags;
extern crate core_foundation_sys;
pub extern crate coreaudio_sys as sys;
pub use error::Error;
#[cfg(feature = "audio_unit")]
pub mod audio_unit;
pub mod error;