commit e8a37a847ba07a5f559e6cbe72399dd0d076a54d Author: James Calligeros Date: Fri Dec 2 13:45:15 2022 +1000 initial commit Signed-off-by: James Calligeros diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..838146b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +*.kate-swp +*/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4899b7c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,88 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "alsa" +version = "0.7.0" +dependencies = [ + "alsa-sys", + "bitflags", + "libc", + "nix", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "configparser" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "half" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554" +dependencies = [ + "crunchy", +] + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "nix" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "speakersafety-2" +version = "0.1.0" +dependencies = [ + "alsa", + "configparser", + "half", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2945862 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "speakersafety-2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +half = "^2.1.0" +alsa = { path = "./alsa" } +configparser = "^3.0.2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bbfedd --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +## Asahi Linux speaker safety daemon + +This is still very much a work in progress, is probably not "proper" Rust, +and almost definitely makes competent developers extremely sad. + +We currently rely on a local version of the `alsa` crate, pending the merge of +bindings to `snd_ctl_elem_value_{read,write}` and `snd_ctl_elem_set_id`. + +## What works +* Parsing config file +* All borrows seem to work fine +* Volume getting/setting + +## Needs improvement +* Probably everything + +## Need to implement +* Daemonise and loop +* Threading (should probably make sure it works as intended first) +* Getting V/ISENSE (pending changes to the codec drivers, we have mock implementations) +* Actually fail safe (see below) + +## On failing safe +We need a way to guarantee safety on _any_ fail condition. The TAS codecs have a safe +mode which cuts all outputs down by 18 dB. This works out to being about half their +full output capabiltiy. It might be worth having the `macaudio` driver start them +explicitly in this mode, and only unlock full output capability with an IOCTL that +can be sent by `speakersafetyd` when it's sure it has started correctly. We would +then of course also need an IOCTL to do the opposite if we encounter a runtime error. + +It was suggested by someone on IRC that this would be conducive to some sort of +keepalive IOCTL, where the driver would automatically put the codecs into safe mode +if it didn't hear from us for a while. This seems like it would suck to implement. + +Like any SLA, it is likely that we will never be able to guarantee 100% safety for all +nonstandard setups. The reference PipeWire DSP graph plus this should be enough for 99% of +users, but I feel at some point those who insist on using Pulse or raw ALSA are just going +to have to put up with a best effort service and accept the (small) risk of this failing. + +## Sundry +The `alsa` crate is Copyright (c) 2015-2021 David Henningsson, and other +contributors. Redistributed under the MIT license. diff --git a/alsa/Cargo.lock b/alsa/Cargo.lock new file mode 100644 index 0000000..abc0fd5 --- /dev/null +++ b/alsa/Cargo.lock @@ -0,0 +1,58 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "alsa" +version = "0.7.0" +dependencies = [ + "alsa-sys", + "bitflags", + "libc", + "nix", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "nix" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" diff --git a/alsa/Cargo.toml b/alsa/Cargo.toml new file mode 100644 index 0000000..0dab4c6 --- /dev/null +++ b/alsa/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "alsa" +version = "0.7.0" +authors = ["David Henningsson "] + +description = "Thin but safe wrappers for ALSA (Linux sound API)" +repository = "https://github.com/diwic/alsa-rs" +documentation = "http://docs.rs/alsa" +keywords = ["ALSA", "audio", "sound"] +license = "Apache-2.0/MIT" +categories = ["multimedia::audio", "api-bindings"] +readme = "README.md" +edition = "2021" +include = ["README.md", "LICENSE-*", "Cargo.toml", "src/"] + +[dependencies] +libc = "0.2" +alsa-sys = "0.3.1" +bitflags = "1.3.2" +nix = { version = "^0.24", default-features = false, features = ["ioctl"] } + +[badges] +is-it-maintained-issue-resolution = { repository = "diwic/alsa-rs" } +is-it-maintained-open-issues = { repository = "diwic/alsa-rs" } diff --git a/alsa/LICENSE-APACHE b/alsa/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/alsa/LICENSE-APACHE @@ -0,0 +1,177 @@ + + 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 diff --git a/alsa/LICENSE-MIT b/alsa/LICENSE-MIT new file mode 100644 index 0000000..993d2f9 --- /dev/null +++ b/alsa/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2015-2021 David Henningsson, and other contributors. + +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. diff --git a/alsa/README.md b/alsa/README.md new file mode 100644 index 0000000..74e801c --- /dev/null +++ b/alsa/README.md @@ -0,0 +1,62 @@ +ALSA bindings for Rust +======================= + +Thin but safe wrappers for [ALSA](https://alsa-project.org), the most +common API for accessing audio devices on Linux. + +[![crates.io](https://img.shields.io/crates/v/alsa.svg)](https://crates.io/crates/alsa) +[![API documentation](https://docs.rs/alsa/badge.svg)](https://docs.rs/alsa) +[![license](https://img.shields.io/crates/l/alsa.svg)](https://crates.io/crates/alsa) + +The ALSA API is rather big, so everything is not covered yet, but expect the following to work: + + * Audio Playback (example in `pcm` module docs) + + * Audio Recording + + * Mixer controls + + * HCtl API (jack detection example in `hctl` module docs) + + * Raw midi + + * Midi sequencer (most of it) + + * Ctl API + + * Device name hints (example in `device_name` module docs) + + * Enumerations of all of the above + + * Poll and/or wait for all of the above + +The following is not yet implemented (mostly because nobody asked for them) : + + * Separate timer API (snd_timer_*) + + * Config API (snd_config_*) + + * Plug-in API + +Quickstart guide / API design: + + * Most functions map 1-to-1 to alsa-lib functions, e g, `ctl::CardInfo::get_id()` is a wrapper around + `snd_ctl_card_info_get_id` and the [alsa-lib documentation](https://www.alsa-project.org/alsa-doc/alsa-lib/) + can be consulted for additional information. + + * Structs are RAII and closed/freed on drop, e g, when a `PCM` struct is dropped, `snd_pcm_close` is called. + + * To read and write buffers, call the `io_*` methods. It will return a separate struct from which you can + read or write, and which can also be used for mmap (if supported by the driver). + + * Error handling - most alsa-lib functions can return errors, so the return value from these is a `Result`. + + * Enumeration of cards, devices etc is done through structs implementing `Iterator`. + + * Many structs implement `poll::Descriptors`, to combine with poll or mio. + Or just use `wait` if you don't need non-blocking functionality. + +Notes: + + * To run the tests successfully, there must be a "default" sound card configured. This is usually not a problem when running on normal hardware, but some CI systems, docker images etc, might not have that configured by default. + diff --git a/alsa/examples/record.rs b/alsa/examples/record.rs new file mode 100644 index 0000000..d91fd15 --- /dev/null +++ b/alsa/examples/record.rs @@ -0,0 +1,50 @@ +//! Example that continously reads data and displays its RMS volume. + +use alsa::pcm::*; +use alsa::{Direction, ValueOr, Error}; + +fn start_capture(device: &str) -> Result { + let pcm = PCM::new(device, Direction::Capture, false)?; + { + // For this example, we assume 44100Hz, one channel, 16 bit audio. + let hwp = HwParams::any(&pcm)?; + hwp.set_channels(1)?; + hwp.set_rate(44100, ValueOr::Nearest)?; + hwp.set_format(Format::s16())?; + hwp.set_access(Access::RWInterleaved)?; + pcm.hw_params(&hwp)?; + } + pcm.start()?; + Ok(pcm) +} + +// Calculates RMS (root mean square) as a way to determine volume +fn rms(buf: &[i16]) -> f64 { + if buf.len() == 0 { return 0f64; } + let mut sum = 0f64; + for &x in buf { + sum += (x as f64) * (x as f64); + } + let r = (sum / (buf.len() as f64)).sqrt(); + // Convert value to decibels + 20.0 * (r / (i16::MAX as f64)).log10() +} + + +fn read_loop(pcm: &PCM) -> Result<(), Error> { + let io = pcm.io_i16()?; + let mut buf = [0i16; 8192]; + loop { + // Block while waiting for 8192 samples to be read from the device. + assert_eq!(io.readi(&mut buf)?, buf.len()); + let r = rms(&buf); + println!("RMS: {:.1} dB", r); + } +} + +fn main() { + // The "default" device is usually directed to the sound server process, + // e g PulseAudio or PipeWire. + let capture = start_capture("default").unwrap(); + read_loop(&capture).unwrap(); +} \ No newline at end of file diff --git a/alsa/src/card.rs b/alsa/src/card.rs new file mode 100644 index 0000000..93345f4 --- /dev/null +++ b/alsa/src/card.rs @@ -0,0 +1,54 @@ +//! Sound card enumeration +use libc::{c_int, c_char}; +use super::error::*; +use crate::alsa; +use std::ffi::CStr; + +/// An ALSA sound card, uniquely identified by its index. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Card(c_int); + +/// Iterate over existing sound cards. +pub struct Iter(c_int); + +impl Iter { + pub fn new() -> Iter { Iter(-1) } +} + +impl Iterator for Iter { + type Item = Result; + + fn next(&mut self) -> Option> { + match acheck!(snd_card_next(&mut self.0)) { + Ok(_) if self.0 == -1 => None, + Ok(_) => Some(Ok(Card(self.0))), + Err(e) => Some(Err(e)), + } + } +} + +impl Card { + pub fn new(index: c_int) -> Card { Card(index) } + pub fn from_str(s: &CStr) -> Result { + acheck!(snd_card_get_index(s.as_ptr())).map(Card) + } + pub fn get_name(&self) -> Result { + let mut c: *mut c_char = ::std::ptr::null_mut(); + acheck!(snd_card_get_name(self.0, &mut c)) + .and_then(|_| from_alloc("snd_card_get_name", c)) + } + pub fn get_longname(&self) -> Result { + let mut c: *mut c_char = ::std::ptr::null_mut(); + acheck!(snd_card_get_longname(self.0, &mut c)) + .and_then(|_| from_alloc("snd_card_get_longname", c)) + } + + pub fn get_index(&self) -> c_int { self.0 } +} + +#[test] +fn print_cards() { + for a in Iter::new().map(|a| a.unwrap()) { + println!("Card #{}: {} ({})", a.get_index(), a.get_name().unwrap(), a.get_longname().unwrap()) + } +} diff --git a/alsa/src/chmap.rs b/alsa/src/chmap.rs new file mode 100644 index 0000000..b2c7592 --- /dev/null +++ b/alsa/src/chmap.rs @@ -0,0 +1,148 @@ +use crate::alsa; +use std::{fmt, mem, slice}; +use super::error::*; + +alsa_enum!( + /// [SND_CHMAP_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants + ChmapType, ALL_CHMAP_TYPES[4], + + None = SND_CHMAP_TYPE_NONE, + Fixed = SND_CHMAP_TYPE_FIXED, + Var = SND_CHMAP_TYPE_VAR, + Paired = SND_CHMAP_TYPE_PAIRED, +); + +alsa_enum!( + /// [SND_CHMAP_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants + ChmapPosition, ALL_CHMAP_POSITIONS[33], + + Unknown = SND_CHMAP_UNKNOWN, + NA = SND_CHMAP_NA, + Mono = SND_CHMAP_MONO, + FL = SND_CHMAP_FL, + FR = SND_CHMAP_FR, + RL = SND_CHMAP_RL, + SR = SND_CHMAP_SR, + RC = SND_CHMAP_RC, + FLC = SND_CHMAP_FLC, + FRC = SND_CHMAP_FRC, + RLC = SND_CHMAP_RLC, + RRC = SND_CHMAP_RRC, + FLW = SND_CHMAP_FLW, + FRW = SND_CHMAP_FRW, + FLH = SND_CHMAP_FLH, + FCH = SND_CHMAP_FCH, + FRH = SND_CHMAP_FRH, + TC = SND_CHMAP_TC, + TFL = SND_CHMAP_TFL, + TFR = SND_CHMAP_TFR, + TFC = SND_CHMAP_TFC, + TRL = SND_CHMAP_TRL, + TRR = SND_CHMAP_TRR, + TRC = SND_CHMAP_TRC, + TFLC = SND_CHMAP_TFLC, + TFRC = SND_CHMAP_TFRC, + TSL = SND_CHMAP_TSL, + TSR = SND_CHMAP_TSR, + LLFE = SND_CHMAP_LLFE, + RLFE = SND_CHMAP_RLFE, + BC = SND_CHMAP_BC, + BLC = SND_CHMAP_BLC, + BRC = SND_CHMAP_BRC, +); + +impl fmt::Display for ChmapPosition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = unsafe { alsa::snd_pcm_chmap_long_name(*self as libc::c_uint) }; + let s = from_const("snd_pcm_chmap_long_name", s)?; + write!(f, "{}", s) + } +} + + +/// [snd_pcm_chmap_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) wrapper +pub struct Chmap(*mut alsa::snd_pcm_chmap_t, bool); + +impl Drop for Chmap { + fn drop(&mut self) { if self.1 { unsafe { libc::free(self.0 as *mut libc::c_void) }}} +} + +impl Chmap { + fn set_channels(&mut self, c: libc::c_uint) { unsafe { (*self.0) .channels = c }} + fn as_slice_mut(&mut self) -> &mut [libc::c_uint] { + unsafe { slice::from_raw_parts_mut((*self.0).pos.as_mut_ptr(), (*self.0).channels as usize) } + } + fn as_slice(&self) -> &[libc::c_uint] { + unsafe { slice::from_raw_parts((*self.0).pos.as_ptr(), (*self.0).channels as usize) } + } +} + +impl fmt::Display for Chmap { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut buf: Vec = vec![0; 512]; + acheck!(snd_pcm_chmap_print(self.0, buf.len() as libc::size_t, buf.as_mut_ptr()))?; + let s = from_const("snd_pcm_chmap_print", buf.as_mut_ptr())?; + write!(f, "{}", s) + } +} + +impl<'a> From<&'a [ChmapPosition]> for Chmap { + fn from(a: &'a [ChmapPosition]) -> Chmap { + let p = unsafe { libc::malloc((mem::size_of::() + mem::size_of::() * a.len()) as libc::size_t) }; + if p.is_null() { panic!("Out of memory") } + let mut r = Chmap(p as *mut alsa::snd_pcm_chmap_t, true); + r.set_channels(a.len() as libc::c_uint); + for (i,v) in r.as_slice_mut().iter_mut().enumerate() { *v = a[i] as libc::c_uint } + r + } +} + +impl<'a> From<&'a Chmap> for Vec { + fn from(a: &'a Chmap) -> Vec { + a.as_slice().iter().map(|&v| ChmapPosition::from_c_int(v as libc::c_int, "").unwrap()).collect() + } +} + +pub fn chmap_new(a: *mut alsa::snd_pcm_chmap_t) -> Chmap { Chmap(a, true) } +pub fn chmap_handle(a: &Chmap) -> *mut alsa::snd_pcm_chmap_t { a.0 } + + +/// Iterator over available channel maps - see [snd_pcm_chmap_query_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) +pub struct ChmapsQuery(*mut *mut alsa::snd_pcm_chmap_query_t, isize); + +impl Drop for ChmapsQuery { + fn drop(&mut self) { unsafe { alsa::snd_pcm_free_chmaps(self.0) }} +} + +pub fn chmaps_query_new(a: *mut *mut alsa::snd_pcm_chmap_query_t) -> ChmapsQuery { ChmapsQuery(a, 0) } + +impl Iterator for ChmapsQuery { + type Item = (ChmapType, Chmap); + fn next(&mut self) -> Option { + if self.0.is_null() { return None; } + let p = unsafe { *self.0.offset(self.1) }; + if p.is_null() { return None; } + self.1 += 1; + let t = ChmapType::from_c_int(unsafe { (*p).type_ } as libc::c_int, "snd_pcm_query_chmaps").unwrap(); + let m = Chmap(unsafe { &mut (*p).map }, false); + Some((t, m)) + } +} + + +#[test] +fn chmap_for_first_pcm() { + use super::*; + use std::ffi::CString; + use crate::device_name::HintIter; + let i = HintIter::new(None, &*CString::new("pcm").unwrap()).unwrap(); + for p in i.map(|n| n.name.unwrap()) { + println!("Chmaps for {:?}:", p); + match PCM::open(&CString::new(p).unwrap(), Direction::Playback, false) { + Ok(a) => for c in a.query_chmaps() { + println!(" {:?}, {}", c.0, c.1); + }, + Err(a) => println!(" {}", a) // It's okay to have entries in the name hint array that can't be opened + } + } +} diff --git a/alsa/src/ctl_int.rs b/alsa/src/ctl_int.rs new file mode 100644 index 0000000..aa81dc0 --- /dev/null +++ b/alsa/src/ctl_int.rs @@ -0,0 +1,455 @@ + +use crate::alsa; +use std::ffi::{CStr, CString}; +use super::error::*; +use super::mixer::MilliBel; +use super::Round; +use std::{ptr, mem, fmt, cmp}; +use crate::{Card, poll}; +use std::cell::UnsafeCell; +use libc::{c_uint, c_void, size_t, c_long, c_int, pollfd, c_short}; + +/// We prefer not to allocate for every ElemId, ElemInfo or ElemValue. +/// But we don't know if these will increase in the future or on other platforms. +/// Unfortunately, Rust does not support alloca, so hard-code the sizes for now. + +const ELEM_ID_SIZE: usize = 64; +// const ELEM_VALUE_SIZE: usize = 1224; +// const ELEM_INFO_SIZE: usize = 272; + +/// [snd_ctl_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct Ctl(*mut alsa::snd_ctl_t); + +unsafe impl Send for Ctl {} + +impl Ctl { + /// Wrapper around open that takes a &str instead of a &CStr + pub fn new(c: &str, nonblock: bool) -> Result { + Self::open(&CString::new(c).unwrap(), nonblock) + } + + /// Open does not support async mode (it's not very Rustic anyway) + pub fn open(c: &CStr, nonblock: bool) -> Result { + let mut r = ptr::null_mut(); + let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys + acheck!(snd_ctl_open(&mut r, c.as_ptr(), flags)).map(|_| Ctl(r)) + } + + pub fn from_card(c: &Card, nonblock: bool) -> Result { + let s = format!("hw:{}", c.get_index()); + Ctl::open(&CString::new(s).unwrap(), nonblock) + } + + pub fn card_info(&self) -> Result { CardInfo::new().and_then(|c| + acheck!(snd_ctl_card_info(self.0, c.0)).map(|_| c)) } + + pub fn wait(&self, timeout_ms: Option) -> Result { + acheck!(snd_ctl_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) } + + pub fn get_db_range(&self, id: &ElemId) -> Result<(MilliBel, MilliBel)> { + let mut min: c_long = 0; + let mut max: c_long = 0; + acheck!(snd_ctl_get_dB_range(self.0, elem_id_ptr(id), &mut min, &mut max)) + .map(|_| (MilliBel(min as i64), MilliBel(max as i64))) + } + + pub fn convert_to_db(&self, id: &ElemId, volume: i64) -> Result { + let mut m: c_long = 0; + acheck!(snd_ctl_convert_to_dB(self.0, elem_id_ptr(id), volume as c_long, &mut m)) + .map(|_| (MilliBel(m as i64))) + } + + pub fn convert_from_db(&self, id: &ElemId, mb: MilliBel, dir: Round) -> Result { + let mut m: c_long = 0; + acheck!(snd_ctl_convert_from_dB(self.0, elem_id_ptr(id), mb.0 as c_long, &mut m, dir as c_int)) + .map(|_| m as i64) + } + + pub fn elem_read(&self, val: &mut ElemValue) -> Result<()> { + acheck!(snd_ctl_elem_read(self.0, elem_value_ptr(val))).map(|_| ()) + } + + pub fn elem_write(&self, val: &ElemValue) -> Result<()> { + acheck!(snd_ctl_elem_write(self.0, elem_value_ptr(val))).map(|_| ()) + } + + /// Note: According to alsa-lib documentation, you're also supposed to have functionality for + /// returning whether or not you are subscribed. This does not work in practice, so I'm not + /// including that here. + pub fn subscribe_events(&self, subscribe: bool) -> Result<()> { + acheck!(snd_ctl_subscribe_events(self.0, if subscribe { 1 } else { 0 })).map(|_| ()) + } + + pub fn read(&self) -> Result> { + let e = event_new()?; + acheck!(snd_ctl_read(self.0, e.0)).map(|r| if r == 1 { Some(e) } else { None }) + } +} + +impl Drop for Ctl { + fn drop(&mut self) { unsafe { alsa::snd_ctl_close(self.0) }; } +} + +impl poll::Descriptors for Ctl { + fn count(&self) -> usize { + unsafe { alsa::snd_ctl_poll_descriptors_count(self.0) as usize } + } + fn fill(&self, p: &mut [pollfd]) -> Result { + let z = unsafe { alsa::snd_ctl_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) }; + from_code("snd_ctl_poll_descriptors", z).map(|_| z as usize) + } + fn revents(&self, p: &[pollfd]) -> Result { + let mut r = 0; + let z = unsafe { alsa::snd_ctl_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) }; + from_code("snd_ctl_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short)) + } +} + + +pub fn ctl_ptr(a: &Ctl) -> *mut alsa::snd_ctl_t { a.0 } + +/// [snd_ctl_card_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct CardInfo(*mut alsa::snd_ctl_card_info_t); + +impl Drop for CardInfo { + fn drop(&mut self) { unsafe { alsa::snd_ctl_card_info_free(self.0) }} +} + +impl CardInfo { + fn new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_ctl_card_info_malloc(&mut p)).map(|_| CardInfo(p)) + } + + pub fn get_id(&self) -> Result<&str> { + from_const("snd_ctl_card_info_get_id", unsafe { alsa::snd_ctl_card_info_get_id(self.0) })} + pub fn get_driver(&self) -> Result<&str> { + from_const("snd_ctl_card_info_get_driver", unsafe { alsa::snd_ctl_card_info_get_driver(self.0) })} + pub fn get_components(&self) -> Result<&str> { + from_const("snd_ctl_card_info_get_components", unsafe { alsa::snd_ctl_card_info_get_components(self.0) })} + pub fn get_longname(&self) -> Result<&str> { + from_const("snd_ctl_card_info_get_longname", unsafe { alsa::snd_ctl_card_info_get_longname(self.0) })} + pub fn get_name(&self) -> Result<&str> { + from_const("snd_ctl_card_info_get_name", unsafe { alsa::snd_ctl_card_info_get_name(self.0) })} + pub fn get_mixername(&self) -> Result<&str> { + from_const("snd_ctl_card_info_get_mixername", unsafe { alsa::snd_ctl_card_info_get_mixername(self.0) })} + pub fn get_card(&self) -> Card { Card::new(unsafe { alsa::snd_ctl_card_info_get_card(self.0) })} +} + +alsa_enum!( + /// [SND_CTL_ELEM_IFACE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) constants + ElemIface, ALL_ELEMIFACE[7], + + Card = SND_CTL_ELEM_IFACE_CARD, + Hwdep = SND_CTL_ELEM_IFACE_HWDEP, + Mixer = SND_CTL_ELEM_IFACE_MIXER, + PCM = SND_CTL_ELEM_IFACE_PCM, + Rawmidi = SND_CTL_ELEM_IFACE_RAWMIDI, + Timer = SND_CTL_ELEM_IFACE_TIMER, + Sequencer = SND_CTL_ELEM_IFACE_SEQUENCER, +); + +alsa_enum!( + /// [SND_CTL_ELEM_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) constants + ElemType, ALL_ELEMTYPE[7], + + None = SND_CTL_ELEM_TYPE_NONE, + Boolean = SND_CTL_ELEM_TYPE_BOOLEAN, + Integer = SND_CTL_ELEM_TYPE_INTEGER, + Enumerated = SND_CTL_ELEM_TYPE_ENUMERATED, + Bytes = SND_CTL_ELEM_TYPE_BYTES, + IEC958 = SND_CTL_ELEM_TYPE_IEC958, + Integer64 = SND_CTL_ELEM_TYPE_INTEGER64, +); + +/// [snd_ctl_elem_value_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct ElemValue { + ptr: *mut alsa::snd_ctl_elem_value_t, + etype: ElemType, + count: u32, +} + +impl Drop for ElemValue { + fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_value_free(self.ptr) }; } +} + +pub fn elem_value_ptr(a: &ElemValue) -> *mut alsa::snd_ctl_elem_value_t { a.ptr } + +pub fn elem_value_new(t: ElemType, count: u32) -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_ctl_elem_value_malloc(&mut p)) + .map(|_| ElemValue { ptr: p, etype: t, count }) +} + +impl ElemValue { + + pub fn set_id(&mut self, id: &ElemId) { + unsafe { alsa::snd_ctl_elem_value_set_id(self.ptr, elem_id_ptr(id)) } + } + + // Note: The get_bytes hands out a reference to inside the object. Therefore, we can't treat + // the content as "cell"ed, but must take a "&mut self" (to make sure the reference + // from get_bytes has been dropped when calling a set_* function). + + pub fn get_boolean(&self, idx: u32) -> Option { + if self.etype != ElemType::Boolean || idx >= self.count { None } + else { Some( unsafe { alsa::snd_ctl_elem_value_get_boolean(self.ptr, idx as c_uint) } != 0) } + } + + pub fn set_boolean(&mut self, idx: u32, val: bool) -> Option<()> { + if self.etype != ElemType::Boolean || idx >= self.count { None } + else { unsafe { alsa::snd_ctl_elem_value_set_boolean(self.ptr, idx as c_uint, if val {1} else {0}) }; Some(()) } + } + + pub fn get_integer(&self, idx: u32) -> Option { + if self.etype != ElemType::Integer || idx >= self.count { None } + else { Some( unsafe { alsa::snd_ctl_elem_value_get_integer(self.ptr, idx as c_uint) } as i32) } + } + + pub fn set_integer(&mut self, idx: u32, val: i32) -> Option<()> { + if self.etype != ElemType::Integer || idx >= self.count { None } + else { unsafe { alsa::snd_ctl_elem_value_set_integer(self.ptr, idx as c_uint, val as c_long) }; Some(()) } + } + + pub fn get_integer64(&self, idx: u32) -> Option { + if self.etype != ElemType::Integer64 || idx >= self.count { None } + else { Some( unsafe { alsa::snd_ctl_elem_value_get_integer64(self.ptr, idx as c_uint) } as i64) } + } + + pub fn set_integer64(&mut self, idx: u32, val: i64) -> Option<()> { + if self.etype != ElemType::Integer || idx >= self.count { None } + else { unsafe { alsa::snd_ctl_elem_value_set_integer64(self.ptr, idx as c_uint, val) }; Some(()) } + } + + pub fn get_enumerated(&self, idx: u32) -> Option { + if self.etype != ElemType::Enumerated || idx >= self.count { None } + else { Some( unsafe { alsa::snd_ctl_elem_value_get_enumerated(self.ptr, idx as c_uint) } as u32) } + } + + pub fn set_enumerated(&mut self, idx: u32, val: u32) -> Option<()> { + if self.etype != ElemType::Enumerated || idx >= self.count { None } + else { unsafe { alsa::snd_ctl_elem_value_set_enumerated(self.ptr, idx as c_uint, val as c_uint) }; Some(()) } + } + + pub fn get_byte(&self, idx: u32) -> Option { + if self.etype != ElemType::Bytes || idx >= self.count { None } + else { Some( unsafe { alsa::snd_ctl_elem_value_get_byte(self.ptr, idx as c_uint) } as u8) } + } + + pub fn set_byte(&mut self, idx: u32, val: u8) -> Option<()> { + if self.etype != ElemType::Bytes || idx >= self.count { None } + else { unsafe { alsa::snd_ctl_elem_value_set_byte(self.ptr, idx as c_uint, val) }; Some(()) } + } + + pub fn get_bytes(&self) -> Option<&[u8]> { + if self.etype != ElemType::Bytes { None } + else { Some( unsafe { ::std::slice::from_raw_parts( + alsa::snd_ctl_elem_value_get_bytes(self.ptr) as *const u8, self.count as usize) } ) } + } + + pub fn set_bytes(&mut self, val: &[u8]) -> Option<()> { + if self.etype != ElemType::Bytes || val.len() != self.count as usize { None } + + // Note: the alsa-lib function definition is broken. First, the pointer is declared as mut even + // though it's const, and second, there is a "value" missing between "elem" and "set_bytes". + else { unsafe { alsa::snd_ctl_elem_set_bytes(self.ptr, val.as_ptr() as *mut c_void, val.len() as size_t) }; Some(()) } + } + + /// Creates a new ElemValue. + pub fn new(t: ElemType) -> Result { + // See max length in include/uapi/sound/asound.h in linux kernel for these values + let count = match t { + ElemType::None => 1, + ElemType::Boolean => 128, + ElemType::Integer => 128, + ElemType::Enumerated => 128, + ElemType::Bytes => 512, + ElemType::IEC958 => 1, + ElemType::Integer64 => 64, + }; + // if count > maxcount { return Err(Error::new(Some("ElemValue::new - count too large".into()), 1)) } + let ev = elem_value_new(t, count)?; + unsafe { alsa::snd_ctl_elem_value_clear(elem_value_ptr(&ev)) }; + Ok(ev) + } + +} + +impl fmt::Debug for ElemValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ElemType::*; + write!(f, "ElemValue({:?}", self.etype)?; + for a in 0..self.count { match self.etype { + Boolean => write!(f, ",{:?}", self.get_boolean(a).unwrap()), + Integer => write!(f, ",{:?}", self.get_integer(a).unwrap()), + Integer64 => write!(f, ",{:?}", self.get_integer64(a).unwrap()), + Enumerated => write!(f, ",{:?}", self.get_enumerated(a).unwrap()), + Bytes => write!(f, ",{:?}", self.get_byte(a).unwrap()), + _ => Ok(()), + }?}; + write!(f, ")") + } +} + +/// [snd_ctl_elem_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct ElemInfo(*mut alsa::snd_ctl_elem_info_t); + +pub fn elem_info_ptr(a: &ElemInfo) -> *mut alsa::snd_ctl_elem_info_t { a.0 } + +impl Drop for ElemInfo { + fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_info_free(self.0) }; } +} + +pub fn elem_info_new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_ctl_elem_info_malloc(&mut p)).map(|_| ElemInfo(p)) +} + +impl ElemInfo { + pub fn get_type(&self) -> ElemType { ElemType::from_c_int( + unsafe { alsa::snd_ctl_elem_info_get_type(self.0) } as c_int, "snd_ctl_elem_info_get_type").unwrap() } + pub fn get_count(&self) -> u32 { unsafe { alsa::snd_ctl_elem_info_get_count(self.0) as u32 } } +} + +// +// Non-allocating version of ElemId +// + +/// [snd_ctl_elem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct ElemId(UnsafeCell<[u8; ELEM_ID_SIZE]>); + +pub fn elem_id_new() -> Result { + assert!(unsafe { alsa::snd_ctl_elem_id_sizeof() } as usize <= ELEM_ID_SIZE); + Ok(ElemId(UnsafeCell::new(unsafe { mem::zeroed() }))) +} + +#[inline] +pub fn elem_id_ptr(a: &ElemId) -> *mut alsa::snd_ctl_elem_id_t { a.0.get() as *mut _ as *mut alsa::snd_ctl_elem_id_t } + +unsafe impl Send for ElemId {} + +impl Clone for ElemId { + fn clone(&self) -> Self { + ElemId(UnsafeCell::new(unsafe { *self.0.get() })) + } +} + +// +// Allocating version of ElemId +// + +/* + +/// [snd_ctl_elem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct ElemId(*mut alsa::snd_ctl_elem_id_t); + +impl Drop for ElemId { + fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_id_free(self.0) }; } +} + +pub fn elem_id_new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_ctl_elem_id_malloc(&mut p)).map(|_| ElemId(p)) +} + +pub fn elem_id_ptr(a: &ElemId) -> *mut alsa::snd_ctl_elem_id_t { a.0 } + +*/ + +impl ElemId { + pub fn get_name(&self) -> Result<&str> { + from_const("snd_hctl_elem_id_get_name", unsafe { alsa::snd_ctl_elem_id_get_name(elem_id_ptr(self)) })} + pub fn get_device(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_device(elem_id_ptr(self)) as u32 }} + pub fn get_subdevice(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_subdevice(elem_id_ptr(self)) as u32 }} + pub fn get_numid(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_numid(elem_id_ptr(self)) as u32 }} + pub fn get_index(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_index(elem_id_ptr(self)) as u32 }} + pub fn get_interface(&self) -> ElemIface { ElemIface::from_c_int( + unsafe { alsa::snd_ctl_elem_id_get_interface(elem_id_ptr(self)) } as c_int, "snd_ctl_elem_id_get_interface").unwrap() } + + pub fn set_device(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_device(elem_id_ptr(self), v) }} + pub fn set_subdevice(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_subdevice(elem_id_ptr(self), v) }} + pub fn set_numid(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_numid(elem_id_ptr(self), v) }} + pub fn set_index(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_index(elem_id_ptr(self), v) }} + pub fn set_interface(&mut self, v: ElemIface) { unsafe { alsa::snd_ctl_elem_id_set_interface(elem_id_ptr(self), v as u32) }} + pub fn set_name(&mut self, v: &CStr) { unsafe { alsa::snd_ctl_elem_id_set_name(elem_id_ptr(self), v.as_ptr()) }} + + /// Creates a new ElemId. + /// + /// To ensure safety (i e make sure we never have an invalid interface enum), we need to supply it to the "new" function. + pub fn new(iface: ElemIface) -> Self { + let mut r = elem_id_new().unwrap(); + r.set_interface(iface); + r + } +} + +impl cmp::Eq for ElemId {} + +impl cmp::PartialEq for ElemId { + fn eq(&self, a: &ElemId) -> bool { + self.get_numid() == a.get_numid() && self.get_interface() == a.get_interface() && + self.get_index() == a.get_index() && self.get_device() == a.get_device() && + self.get_subdevice() == a.get_subdevice() && self.get_name().ok() == a.get_name().ok() + } +} + +impl fmt::Debug for ElemId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let index = self.get_index(); + let device = self.get_device(); + let subdevice = self.get_subdevice(); + + write!(f, "ElemId(#{}, {:?}, {:?}", self.get_numid(), self.get_interface(), self.get_name())?; + if index > 0 { write!(f, ", index={}", index)? }; + if device > 0 || subdevice > 0 { write!(f, ", device={}", device)? }; + if subdevice > 0 { write!(f, ", subdevice={}", device)? }; + write!(f, ")") + } +} + +/// [snd_ctl_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct Event(*mut alsa::snd_ctl_event_t); + +impl Drop for Event { + fn drop(&mut self) { unsafe { alsa::snd_ctl_event_free(self.0) }; } +} + +pub fn event_new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_ctl_event_malloc(&mut p)).map(|_| Event(p)) +} + +impl Event { + pub fn get_mask(&self) -> EventMask { EventMask(unsafe { alsa::snd_ctl_event_elem_get_mask(self.0) as u32 })} + pub fn get_id(&self) -> ElemId { + let r = elem_id_new().unwrap(); + unsafe { alsa::snd_ctl_event_elem_get_id(self.0, elem_id_ptr(&r)) }; + r + } +} + + +/// [SND_CTL_EVENT_MASK_XXX](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) bitmask +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub struct EventMask(pub u32); + +impl EventMask { + pub fn remove(&self) -> bool { return self.0 & 0xffffffff == 0xffffffff } + pub fn value(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 0) != 0); } + pub fn info(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 1) != 0); } + pub fn add(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 2) != 0); } + pub fn tlv(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 3) != 0); } +} + +#[test] +fn print_sizeof() { + let elemid = unsafe { alsa::snd_ctl_elem_id_sizeof() } as usize; + let elemvalue = unsafe { alsa::snd_ctl_elem_value_sizeof() } as usize; + let eleminfo = unsafe { alsa::snd_ctl_elem_info_sizeof() } as usize; + + assert!(elemid <= ELEM_ID_SIZE); +// assert!(elemvalue <= ELEM_VALUE_SIZE); +// assert!(eleminfo <= ELEM_INFO_SIZE); + + println!("Elem id: {}, Elem value: {}, Elem info: {}", elemid, elemvalue, eleminfo); +} diff --git a/alsa/src/device_name.rs b/alsa/src/device_name.rs new file mode 100644 index 0000000..965231e --- /dev/null +++ b/alsa/src/device_name.rs @@ -0,0 +1,90 @@ +//! Enumerate devices in the alsa library configuration +//! +//! # Example +//! Print all devices found in various categories. +//! +//! ``` +//! use std::ffi::CString; +//! use alsa::device_name::HintIter; +//! +//! for t in &["pcm", "ctl", "rawmidi", "timer", "seq", "hwdep"] { +//! println!("{} devices:", t); +//! let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap(); +//! for a in i { println!(" {:?}", a) } +//! } +//! ``` + +use std::ptr; +use libc::{c_void, c_int}; +use crate::alsa; +use super::{Card, Direction}; +use super::error::*; +use std::ffi::{CStr, CString}; + +/// [snd_device_name_hint](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +pub struct HintIter(*mut *mut c_void, isize); + +impl Drop for HintIter { + fn drop(&mut self) { unsafe { alsa::snd_device_name_free_hint(self.0); }} +} + +impl HintIter { + /// typical interfaces are: "pcm", "ctl", "rawmidi", "timer", "seq" and "hwdep". + pub fn new(card: Option<&Card>, iface: &CStr) -> Result { + let mut p = ptr::null_mut(); + let cnr = card.map(|c| c.get_index()).unwrap_or(-1) as c_int; + acheck!(snd_device_name_hint(cnr, iface.as_ptr(), &mut p)) + .map(|_| HintIter(p, 0)) + } + + /// A constructor variant that takes the interface as a Rust string slice. + pub fn new_str(card: Option<&Card>, iface: &str) -> Result { + HintIter::new(card, &CString::new(iface).unwrap()) + } +} + +impl Iterator for HintIter { + type Item = Hint; + fn next(&mut self) -> Option { + if self.0.is_null() { return None; } + let p = unsafe { *self.0.offset(self.1) }; + if p.is_null() { return None; } + self.1 += 1; + Some(Hint::new(p)) + } +} + + +/// [snd_device_name_get_hint](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper +#[derive(Debug, Clone)] +pub struct Hint { + pub name: Option, + pub desc: Option, + pub direction: Option, +} + +impl Hint { + fn get_str(p: *const c_void, name: &str) -> Option { + let name = CString::new(name).unwrap(); + let c = unsafe { alsa::snd_device_name_get_hint(p, name.as_ptr()) }; + from_alloc("snd_device_name_get_hint", c).ok() + } + + fn new(p: *const c_void) -> Hint { + let d = Hint::get_str(p, "IOID").and_then(|x| match &*x { + "Input" => Some(Direction::Capture), + "Output" => Some(Direction::Playback), + _ => None, + }); + Hint { name: Hint::get_str(p, "NAME"), desc: Hint::get_str(p, "DESC"), direction: d } + } +} + +#[test] +fn print_hints() { + for t in &["pcm", "ctl", "rawmidi", "timer", "seq", "hwdep"] { + println!("{} devices:", t); + let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap(); + for a in i { println!(" {:?}", a) } + } +} diff --git a/alsa/src/direct.rs b/alsa/src/direct.rs new file mode 100644 index 0000000..239b5bd --- /dev/null +++ b/alsa/src/direct.rs @@ -0,0 +1,5 @@ +//! Functions that bypass alsa-lib and talk directly to the kernel. + +pub mod pcm; + +mod ffi; diff --git a/alsa/src/direct/asound_ioctl.rs b/alsa/src/direct/asound_ioctl.rs new file mode 100644 index 0000000..c145582 --- /dev/null +++ b/alsa/src/direct/asound_ioctl.rs @@ -0,0 +1,6713 @@ +/* automatically generated by rust-bindgen */ + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct __BindgenBitfieldUnit +where + Storage: AsRef<[u8]> + AsMut<[u8]>, +{ + storage: Storage, + align: [Align; 0], +} + +impl __BindgenBitfieldUnit +where + Storage: AsRef<[u8]> + AsMut<[u8]>, +{ + #[inline] + pub fn new(storage: Storage) -> Self { + Self { storage, align: [] } + } + + #[inline] + pub fn get_bit(&self, index: usize) -> bool { + debug_assert!(index / 8 < self.storage.as_ref().len()); + + let byte_index = index / 8; + let byte = self.storage.as_ref()[byte_index]; + + let bit_index = index % 8; + let mask = 1 << bit_index; + + byte & mask == mask + } + + #[inline] + pub fn set_bit(&mut self, index: usize, val: bool) { + debug_assert!(index / 8 < self.storage.as_ref().len()); + + let byte_index = index / 8; + let byte = &mut self.storage.as_mut()[byte_index]; + + let bit_index = index % 8; + let mask = 1 << bit_index; + + if val { + *byte |= mask; + } else { + *byte &= !mask; + } + } + + #[inline] + pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + + let mut val = 0; + + for i in 0..(bit_width as usize) { + if self.get_bit(i + bit_offset) { + val |= 1 << i; + } + } + + val + } + + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + + for i in 0..(bit_width as usize) { + let mask = 1 << i; + let val_bit_is_set = val & mask == mask; + self.set_bit(i + bit_offset, val_bit_is_set); + } + } +} +#[repr(C)] +#[derive(Default)] +pub struct __IncompleteArrayField(::std::marker::PhantomData); +impl __IncompleteArrayField { + #[inline] + pub fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData) + } + #[inline] + pub unsafe fn as_ptr(&self) -> *const T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_mut_ptr(&mut self) -> *mut T { + ::std::mem::transmute(self) + } + #[inline] + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { + ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } +} +impl ::std::fmt::Debug for __IncompleteArrayField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} +impl ::std::clone::Clone for __IncompleteArrayField { + #[inline] + fn clone(&self) -> Self { + Self::new() + } +} +impl ::std::marker::Copy for __IncompleteArrayField {} +pub const __BITS_PER_LONG: u32 = 64; +pub const __FD_SETSIZE: u32 = 1024; +pub const _FEATURES_H: u32 = 1; +pub const _DEFAULT_SOURCE: u32 = 1; +pub const __USE_ISOC11: u32 = 1; +pub const __USE_ISOC99: u32 = 1; +pub const __USE_ISOC95: u32 = 1; +pub const __USE_POSIX_IMPLICITLY: u32 = 1; +pub const _POSIX_SOURCE: u32 = 1; +pub const _POSIX_C_SOURCE: u32 = 200809; +pub const __USE_POSIX: u32 = 1; +pub const __USE_POSIX2: u32 = 1; +pub const __USE_POSIX199309: u32 = 1; +pub const __USE_POSIX199506: u32 = 1; +pub const __USE_XOPEN2K: u32 = 1; +pub const __USE_XOPEN2K8: u32 = 1; +pub const _ATFILE_SOURCE: u32 = 1; +pub const __USE_MISC: u32 = 1; +pub const __USE_ATFILE: u32 = 1; +pub const __USE_FORTIFY_LEVEL: u32 = 0; +pub const _STDC_PREDEF_H: u32 = 1; +pub const __STDC_IEC_559__: u32 = 1; +pub const __STDC_IEC_559_COMPLEX__: u32 = 1; +pub const __STDC_ISO_10646__: u32 = 201505; +pub const __STDC_NO_THREADS__: u32 = 1; +pub const __GNU_LIBRARY__: u32 = 6; +pub const __GLIBC__: u32 = 2; +pub const __GLIBC_MINOR__: u32 = 23; +pub const _SYS_CDEFS_H: u32 = 1; +pub const __WORDSIZE: u32 = 64; +pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1; +pub const __SYSCALL_WORDSIZE: u32 = 64; +pub const _STDLIB_H: u32 = 1; +pub const WNOHANG: u32 = 1; +pub const WUNTRACED: u32 = 2; +pub const WSTOPPED: u32 = 2; +pub const WEXITED: u32 = 4; +pub const WCONTINUED: u32 = 8; +pub const WNOWAIT: u32 = 16777216; +pub const __WNOTHREAD: u32 = 536870912; +pub const __WALL: u32 = 1073741824; +pub const __WCLONE: u32 = 2147483648; +pub const __ENUM_IDTYPE_T: u32 = 1; +pub const __W_CONTINUED: u32 = 65535; +pub const __WCOREFLAG: u32 = 128; +pub const _ENDIAN_H: u32 = 1; +pub const __LITTLE_ENDIAN: u32 = 1234; +pub const __BIG_ENDIAN: u32 = 4321; +pub const __PDP_ENDIAN: u32 = 3412; +pub const __BYTE_ORDER: u32 = 1234; +pub const __FLOAT_WORD_ORDER: u32 = 1234; +pub const LITTLE_ENDIAN: u32 = 1234; +pub const BIG_ENDIAN: u32 = 4321; +pub const PDP_ENDIAN: u32 = 3412; +pub const BYTE_ORDER: u32 = 1234; +pub const _BITS_BYTESWAP_H: u32 = 1; +pub const _BITS_TYPES_H: u32 = 1; +pub const _BITS_TYPESIZES_H: u32 = 1; +pub const __OFF_T_MATCHES_OFF64_T: u32 = 1; +pub const __INO_T_MATCHES_INO64_T: u32 = 1; +pub const __ldiv_t_defined: u32 = 1; +pub const __lldiv_t_defined: u32 = 1; +pub const RAND_MAX: u32 = 2147483647; +pub const EXIT_FAILURE: u32 = 1; +pub const EXIT_SUCCESS: u32 = 0; +pub const _SYS_TYPES_H: u32 = 1; +pub const __clock_t_defined: u32 = 1; +pub const __time_t_defined: u32 = 1; +pub const __clockid_t_defined: u32 = 1; +pub const __timer_t_defined: u32 = 1; +pub const __BIT_TYPES_DEFINED__: u32 = 1; +pub const _SYS_SELECT_H: u32 = 1; +pub const __FD_ZERO_STOS: &'static [u8; 6usize] = b"stosq\0"; +pub const _SIGSET_H_types: u32 = 1; +pub const __timespec_defined: u32 = 1; +pub const _STRUCT_TIMEVAL: u32 = 1; +pub const FD_SETSIZE: u32 = 1024; +pub const _SYS_SYSMACROS_H: u32 = 1; +pub const _BITS_PTHREADTYPES_H: u32 = 1; +pub const __SIZEOF_PTHREAD_ATTR_T: u32 = 56; +pub const __SIZEOF_PTHREAD_MUTEX_T: u32 = 40; +pub const __SIZEOF_PTHREAD_MUTEXATTR_T: u32 = 4; +pub const __SIZEOF_PTHREAD_COND_T: u32 = 48; +pub const __SIZEOF_PTHREAD_CONDATTR_T: u32 = 4; +pub const __SIZEOF_PTHREAD_RWLOCK_T: u32 = 56; +pub const __SIZEOF_PTHREAD_RWLOCKATTR_T: u32 = 8; +pub const __SIZEOF_PTHREAD_BARRIER_T: u32 = 32; +pub const __SIZEOF_PTHREAD_BARRIERATTR_T: u32 = 4; +pub const __have_pthread_attr_t: u32 = 1; +pub const __PTHREAD_MUTEX_HAVE_PREV: u32 = 1; +pub const __PTHREAD_RWLOCK_INT_FLAGS_SHARED: u32 = 1; +pub const _ALLOCA_H: u32 = 1; +pub const SNDRV_PCM_INFO_MMAP: u32 = 1; +pub const SNDRV_PCM_INFO_MMAP_VALID: u32 = 2; +pub const SNDRV_PCM_INFO_DOUBLE: u32 = 4; +pub const SNDRV_PCM_INFO_BATCH: u32 = 16; +pub const SNDRV_PCM_INFO_SYNC_APPLPTR: u32 = 32; +pub const SNDRV_PCM_INFO_INTERLEAVED: u32 = 256; +pub const SNDRV_PCM_INFO_NONINTERLEAVED: u32 = 512; +pub const SNDRV_PCM_INFO_COMPLEX: u32 = 1024; +pub const SNDRV_PCM_INFO_BLOCK_TRANSFER: u32 = 65536; +pub const SNDRV_PCM_INFO_OVERRANGE: u32 = 131072; +pub const SNDRV_PCM_INFO_RESUME: u32 = 262144; +pub const SNDRV_PCM_INFO_PAUSE: u32 = 524288; +pub const SNDRV_PCM_INFO_HALF_DUPLEX: u32 = 1048576; +pub const SNDRV_PCM_INFO_JOINT_DUPLEX: u32 = 2097152; +pub const SNDRV_PCM_INFO_SYNC_START: u32 = 4194304; +pub const SNDRV_PCM_INFO_NO_PERIOD_WAKEUP: u32 = 8388608; +pub const SNDRV_PCM_INFO_HAS_WALL_CLOCK: u32 = 16777216; +pub const SNDRV_PCM_INFO_HAS_LINK_ATIME: u32 = 16777216; +pub const SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME: u32 = 33554432; +pub const SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME: u32 = 67108864; +pub const SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME: u32 = 134217728; +pub const SNDRV_PCM_INFO_DRAIN_TRIGGER: u32 = 1073741824; +pub const SNDRV_PCM_INFO_FIFO_IN_FRAMES: u32 = 2147483648; +pub const SNDRV_PCM_HW_PARAM_ACCESS: u32 = 0; +pub const SNDRV_PCM_HW_PARAM_FORMAT: u32 = 1; +pub const SNDRV_PCM_HW_PARAM_SUBFORMAT: u32 = 2; +pub const SNDRV_PCM_HW_PARAM_FIRST_MASK: u32 = 0; +pub const SNDRV_PCM_HW_PARAM_LAST_MASK: u32 = 2; +pub const SNDRV_PCM_HW_PARAM_SAMPLE_BITS: u32 = 8; +pub const SNDRV_PCM_HW_PARAM_FRAME_BITS: u32 = 9; +pub const SNDRV_PCM_HW_PARAM_CHANNELS: u32 = 10; +pub const SNDRV_PCM_HW_PARAM_RATE: u32 = 11; +pub const SNDRV_PCM_HW_PARAM_PERIOD_TIME: u32 = 12; +pub const SNDRV_PCM_HW_PARAM_PERIOD_SIZE: u32 = 13; +pub const SNDRV_PCM_HW_PARAM_PERIOD_BYTES: u32 = 14; +pub const SNDRV_PCM_HW_PARAM_PERIODS: u32 = 15; +pub const SNDRV_PCM_HW_PARAM_BUFFER_TIME: u32 = 16; +pub const SNDRV_PCM_HW_PARAM_BUFFER_SIZE: u32 = 17; +pub const SNDRV_PCM_HW_PARAM_BUFFER_BYTES: u32 = 18; +pub const SNDRV_PCM_HW_PARAM_TICK_TIME: u32 = 19; +pub const SNDRV_PCM_HW_PARAM_FIRST_INTERVAL: u32 = 8; +pub const SNDRV_PCM_HW_PARAM_LAST_INTERVAL: u32 = 19; +pub const SNDRV_PCM_HW_PARAMS_NORESAMPLE: u32 = 1; +pub const SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER: u32 = 2; +pub const SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP: u32 = 4; +pub const SNDRV_MASK_MAX: u32 = 256; +pub const SNDRV_PCM_SYNC_PTR_HWSYNC: u32 = 1; +pub const SNDRV_PCM_SYNC_PTR_APPL: u32 = 2; +pub const SNDRV_PCM_SYNC_PTR_AVAIL_MIN: u32 = 4; +pub const SNDRV_CHMAP_POSITION_MASK: u32 = 65535; +pub const SNDRV_CHMAP_PHASE_INVERSE: u32 = 65536; +pub const SNDRV_CHMAP_DRIVER_SPEC: u32 = 131072; +pub const SNDRV_RAWMIDI_INFO_OUTPUT: u32 = 1; +pub const SNDRV_RAWMIDI_INFO_INPUT: u32 = 2; +pub const SNDRV_RAWMIDI_INFO_DUPLEX: u32 = 4; +pub const SNDRV_TIMER_GLOBAL_SYSTEM: u32 = 0; +pub const SNDRV_TIMER_GLOBAL_RTC: u32 = 1; +pub const SNDRV_TIMER_GLOBAL_HPET: u32 = 2; +pub const SNDRV_TIMER_GLOBAL_HRTIMER: u32 = 3; +pub const SNDRV_TIMER_FLG_SLAVE: u32 = 1; +pub const SNDRV_TIMER_PSFLG_AUTO: u32 = 1; +pub const SNDRV_TIMER_PSFLG_EXCLUSIVE: u32 = 2; +pub const SNDRV_TIMER_PSFLG_EARLY_EVENT: u32 = 4; +pub const SNDRV_CTL_ELEM_ACCESS_READ: u32 = 1; +pub const SNDRV_CTL_ELEM_ACCESS_WRITE: u32 = 2; +pub const SNDRV_CTL_ELEM_ACCESS_READWRITE: u32 = 3; +pub const SNDRV_CTL_ELEM_ACCESS_VOLATILE: u32 = 4; +pub const SNDRV_CTL_ELEM_ACCESS_TIMESTAMP: u32 = 8; +pub const SNDRV_CTL_ELEM_ACCESS_TLV_READ: u32 = 16; +pub const SNDRV_CTL_ELEM_ACCESS_TLV_WRITE: u32 = 32; +pub const SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE: u32 = 48; +pub const SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND: u32 = 64; +pub const SNDRV_CTL_ELEM_ACCESS_INACTIVE: u32 = 256; +pub const SNDRV_CTL_ELEM_ACCESS_LOCK: u32 = 512; +pub const SNDRV_CTL_ELEM_ACCESS_OWNER: u32 = 1024; +pub const SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK: u32 = 268435456; +pub const SNDRV_CTL_ELEM_ACCESS_USER: u32 = 536870912; +pub const SNDRV_CTL_POWER_D0: u32 = 0; +pub const SNDRV_CTL_POWER_D1: u32 = 256; +pub const SNDRV_CTL_POWER_D2: u32 = 512; +pub const SNDRV_CTL_POWER_D3: u32 = 768; +pub const SNDRV_CTL_POWER_D3hot: u32 = 768; +pub const SNDRV_CTL_POWER_D3cold: u32 = 769; +pub const SNDRV_CTL_ELEM_ID_NAME_MAXLEN: u32 = 44; +pub const SNDRV_CTL_EVENT_MASK_VALUE: u32 = 1; +pub const SNDRV_CTL_EVENT_MASK_INFO: u32 = 2; +pub const SNDRV_CTL_EVENT_MASK_ADD: u32 = 4; +pub const SNDRV_CTL_EVENT_MASK_TLV: u32 = 8; +pub const SNDRV_CTL_EVENT_MASK_REMOVE: i32 = -1; +pub const SNDRV_CTL_NAME_NONE: &'static [u8; 1usize] = b"\0"; +pub const SNDRV_CTL_NAME_PLAYBACK: &'static [u8; 10usize] = b"Playback \0"; +pub const SNDRV_CTL_NAME_CAPTURE: &'static [u8; 9usize] = b"Capture \0"; +pub const SNDRV_CTL_NAME_IEC958_NONE: &'static [u8; 1usize] = b"\0"; +pub const SNDRV_CTL_NAME_IEC958_SWITCH: &'static [u8; 7usize] = b"Switch\0"; +pub const SNDRV_CTL_NAME_IEC958_VOLUME: &'static [u8; 7usize] = b"Volume\0"; +pub const SNDRV_CTL_NAME_IEC958_DEFAULT: &'static [u8; 8usize] = b"Default\0"; +pub const SNDRV_CTL_NAME_IEC958_MASK: &'static [u8; 5usize] = b"Mask\0"; +pub const SNDRV_CTL_NAME_IEC958_CON_MASK: &'static [u8; 9usize] = b"Con Mask\0"; +pub const SNDRV_CTL_NAME_IEC958_PRO_MASK: &'static [u8; 9usize] = b"Pro Mask\0"; +pub const SNDRV_CTL_NAME_IEC958_PCM_STREAM: &'static [u8; 11usize] = b"PCM Stream\0"; +pub type __s8 = ::std::os::raw::c_schar; +pub type __u8 = ::std::os::raw::c_uchar; +pub type __s16 = ::std::os::raw::c_short; +pub type __u16 = ::std::os::raw::c_ushort; +pub type __s32 = ::std::os::raw::c_int; +pub type __u32 = ::std::os::raw::c_uint; +pub type __s64 = ::std::os::raw::c_longlong; +pub type __u64 = ::std::os::raw::c_ulonglong; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_fd_set { + pub fds_bits: [::std::os::raw::c_ulong; 16usize], +} +#[test] +fn bindgen_test_layout___kernel_fd_set() { + assert_eq!( + ::std::mem::size_of::<__kernel_fd_set>(), + 128usize, + concat!("Size of: ", stringify!(__kernel_fd_set)) + ); + assert_eq!( + ::std::mem::align_of::<__kernel_fd_set>(), + 8usize, + concat!("Alignment of ", stringify!(__kernel_fd_set)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__kernel_fd_set>())).fds_bits as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__kernel_fd_set), + "::", + stringify!(fds_bits) + ) + ); +} +pub type __kernel_sighandler_t = + ::std::option::Option; +pub type __kernel_key_t = ::std::os::raw::c_int; +pub type __kernel_mqd_t = ::std::os::raw::c_int; +pub type __kernel_old_uid_t = ::std::os::raw::c_ushort; +pub type __kernel_old_gid_t = ::std::os::raw::c_ushort; +pub type __kernel_old_dev_t = ::std::os::raw::c_ulong; +pub type __kernel_long_t = ::std::os::raw::c_long; +pub type __kernel_ulong_t = ::std::os::raw::c_ulong; +pub type __kernel_ino_t = __kernel_ulong_t; +pub type __kernel_mode_t = ::std::os::raw::c_uint; +pub type __kernel_pid_t = ::std::os::raw::c_int; +pub type __kernel_ipc_pid_t = ::std::os::raw::c_int; +pub type __kernel_uid_t = ::std::os::raw::c_uint; +pub type __kernel_gid_t = ::std::os::raw::c_uint; +pub type __kernel_suseconds_t = __kernel_long_t; +pub type __kernel_daddr_t = ::std::os::raw::c_int; +pub type __kernel_uid32_t = ::std::os::raw::c_uint; +pub type __kernel_gid32_t = ::std::os::raw::c_uint; +pub type __kernel_size_t = __kernel_ulong_t; +pub type __kernel_ssize_t = __kernel_long_t; +pub type __kernel_ptrdiff_t = __kernel_long_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __kernel_fsid_t { + pub val: [::std::os::raw::c_int; 2usize], +} +#[test] +fn bindgen_test_layout___kernel_fsid_t() { + assert_eq!( + ::std::mem::size_of::<__kernel_fsid_t>(), + 8usize, + concat!("Size of: ", stringify!(__kernel_fsid_t)) + ); + assert_eq!( + ::std::mem::align_of::<__kernel_fsid_t>(), + 4usize, + concat!("Alignment of ", stringify!(__kernel_fsid_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__kernel_fsid_t>())).val as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__kernel_fsid_t), + "::", + stringify!(val) + ) + ); +} +pub type __kernel_off_t = __kernel_long_t; +pub type __kernel_loff_t = ::std::os::raw::c_longlong; +pub type __kernel_time_t = __kernel_long_t; +pub type __kernel_clock_t = __kernel_long_t; +pub type __kernel_timer_t = ::std::os::raw::c_int; +pub type __kernel_clockid_t = ::std::os::raw::c_int; +pub type __kernel_caddr_t = *mut ::std::os::raw::c_char; +pub type __kernel_uid16_t = ::std::os::raw::c_ushort; +pub type __kernel_gid16_t = ::std::os::raw::c_ushort; +pub type __le16 = __u16; +pub type __be16 = __u16; +pub type __le32 = __u32; +pub type __be32 = __u32; +pub type __le64 = __u64; +pub type __be64 = __u64; +pub type __sum16 = __u16; +pub type __wsum = __u32; +pub type wchar_t = ::std::os::raw::c_int; +pub const idtype_t_P_ALL: idtype_t = 0; +pub const idtype_t_P_PID: idtype_t = 1; +pub const idtype_t_P_PGID: idtype_t = 2; +pub type idtype_t = u32; +pub type __u_char = ::std::os::raw::c_uchar; +pub type __u_short = ::std::os::raw::c_ushort; +pub type __u_int = ::std::os::raw::c_uint; +pub type __u_long = ::std::os::raw::c_ulong; +pub type __int8_t = ::std::os::raw::c_schar; +pub type __uint8_t = ::std::os::raw::c_uchar; +pub type __int16_t = ::std::os::raw::c_short; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __int32_t = ::std::os::raw::c_int; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type __int64_t = ::std::os::raw::c_long; +pub type __uint64_t = ::std::os::raw::c_ulong; +pub type __quad_t = ::std::os::raw::c_long; +pub type __u_quad_t = ::std::os::raw::c_ulong; +pub type __dev_t = ::std::os::raw::c_ulong; +pub type __uid_t = ::std::os::raw::c_uint; +pub type __gid_t = ::std::os::raw::c_uint; +pub type __ino_t = ::std::os::raw::c_ulong; +pub type __ino64_t = ::std::os::raw::c_ulong; +pub type __mode_t = ::std::os::raw::c_uint; +pub type __nlink_t = ::std::os::raw::c_ulong; +pub type __off_t = ::std::os::raw::c_long; +pub type __off64_t = ::std::os::raw::c_long; +pub type __pid_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __fsid_t { + pub __val: [::std::os::raw::c_int; 2usize], +} +#[test] +fn bindgen_test_layout___fsid_t() { + assert_eq!( + ::std::mem::size_of::<__fsid_t>(), + 8usize, + concat!("Size of: ", stringify!(__fsid_t)) + ); + assert_eq!( + ::std::mem::align_of::<__fsid_t>(), + 4usize, + concat!("Alignment of ", stringify!(__fsid_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__fsid_t>())).__val as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__fsid_t), + "::", + stringify!(__val) + ) + ); +} +pub type __clock_t = ::std::os::raw::c_long; +pub type __rlim_t = ::std::os::raw::c_ulong; +pub type __rlim64_t = ::std::os::raw::c_ulong; +pub type __id_t = ::std::os::raw::c_uint; +pub type __time_t = ::std::os::raw::c_long; +pub type __useconds_t = ::std::os::raw::c_uint; +pub type __suseconds_t = ::std::os::raw::c_long; +pub type __daddr_t = ::std::os::raw::c_int; +pub type __key_t = ::std::os::raw::c_int; +pub type __clockid_t = ::std::os::raw::c_int; +pub type __timer_t = *mut ::std::os::raw::c_void; +pub type __blksize_t = ::std::os::raw::c_long; +pub type __blkcnt_t = ::std::os::raw::c_long; +pub type __blkcnt64_t = ::std::os::raw::c_long; +pub type __fsblkcnt_t = ::std::os::raw::c_ulong; +pub type __fsblkcnt64_t = ::std::os::raw::c_ulong; +pub type __fsfilcnt_t = ::std::os::raw::c_ulong; +pub type __fsfilcnt64_t = ::std::os::raw::c_ulong; +pub type __fsword_t = ::std::os::raw::c_long; +pub type __ssize_t = ::std::os::raw::c_long; +pub type __syscall_slong_t = ::std::os::raw::c_long; +pub type __syscall_ulong_t = ::std::os::raw::c_ulong; +pub type __loff_t = __off64_t; +pub type __qaddr_t = *mut __quad_t; +pub type __caddr_t = *mut ::std::os::raw::c_char; +pub type __intptr_t = ::std::os::raw::c_long; +pub type __socklen_t = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Copy, Clone)] +pub union wait { + pub w_status: ::std::os::raw::c_int, + pub __wait_terminated: wait__bindgen_ty_1, + pub __wait_stopped: wait__bindgen_ty_2, + _bindgen_union_align: u32, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wait__bindgen_ty_1 { + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize], u8>, + pub __bindgen_align: [u32; 0usize], +} +#[test] +fn bindgen_test_layout_wait__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(wait__bindgen_ty_1)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(wait__bindgen_ty_1)) + ); +} +impl wait__bindgen_ty_1 { + #[inline] + pub fn __w_termsig(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 7u8) as u32) } + } + #[inline] + pub fn set___w_termsig(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 7u8, val as u64) + } + } + #[inline] + pub fn __w_coredump(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u32) } + } + #[inline] + pub fn set___w_coredump(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn __w_retcode(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 8u8) as u32) } + } + #[inline] + pub fn set___w_retcode(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 8u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + __w_termsig: ::std::os::raw::c_uint, + __w_coredump: ::std::os::raw::c_uint, + __w_retcode: ::std::os::raw::c_uint, + ) -> __BindgenBitfieldUnit<[u8; 4usize], u8> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 4usize], u8> = + Default::default(); + __bindgen_bitfield_unit.set(0usize, 7u8, { + let __w_termsig: u32 = unsafe { ::std::mem::transmute(__w_termsig) }; + __w_termsig as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let __w_coredump: u32 = unsafe { ::std::mem::transmute(__w_coredump) }; + __w_coredump as u64 + }); + __bindgen_bitfield_unit.set(8usize, 8u8, { + let __w_retcode: u32 = unsafe { ::std::mem::transmute(__w_retcode) }; + __w_retcode as u64 + }); + __bindgen_bitfield_unit + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct wait__bindgen_ty_2 { + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize], u8>, + pub __bindgen_align: [u32; 0usize], +} +#[test] +fn bindgen_test_layout_wait__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(wait__bindgen_ty_2)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(wait__bindgen_ty_2)) + ); +} +impl wait__bindgen_ty_2 { + #[inline] + pub fn __w_stopval(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 8u8) as u32) } + } + #[inline] + pub fn set___w_stopval(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 8u8, val as u64) + } + } + #[inline] + pub fn __w_stopsig(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 8u8) as u32) } + } + #[inline] + pub fn set___w_stopsig(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 8u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + __w_stopval: ::std::os::raw::c_uint, + __w_stopsig: ::std::os::raw::c_uint, + ) -> __BindgenBitfieldUnit<[u8; 4usize], u8> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 4usize], u8> = + Default::default(); + __bindgen_bitfield_unit.set(0usize, 8u8, { + let __w_stopval: u32 = unsafe { ::std::mem::transmute(__w_stopval) }; + __w_stopval as u64 + }); + __bindgen_bitfield_unit.set(8usize, 8u8, { + let __w_stopsig: u32 = unsafe { ::std::mem::transmute(__w_stopsig) }; + __w_stopsig as u64 + }); + __bindgen_bitfield_unit + } +} +#[test] +fn bindgen_test_layout_wait() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(wait)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(wait)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).w_status as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(wait), + "::", + stringify!(w_status) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__wait_terminated as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(wait), + "::", + stringify!(__wait_terminated) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__wait_stopped as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(wait), + "::", + stringify!(__wait_stopped) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union __WAIT_STATUS { + pub __uptr: *mut wait, + pub __iptr: *mut ::std::os::raw::c_int, + _bindgen_union_align: u64, +} +#[test] +fn bindgen_test_layout___WAIT_STATUS() { + assert_eq!( + ::std::mem::size_of::<__WAIT_STATUS>(), + 8usize, + concat!("Size of: ", stringify!(__WAIT_STATUS)) + ); + assert_eq!( + ::std::mem::align_of::<__WAIT_STATUS>(), + 8usize, + concat!("Alignment of ", stringify!(__WAIT_STATUS)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__WAIT_STATUS>())).__uptr as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__WAIT_STATUS), + "::", + stringify!(__uptr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__WAIT_STATUS>())).__iptr as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__WAIT_STATUS), + "::", + stringify!(__iptr) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct div_t { + pub quot: ::std::os::raw::c_int, + pub rem: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_div_t() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(div_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(div_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).quot as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(div_t), + "::", + stringify!(quot) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rem as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(div_t), + "::", + stringify!(rem) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ldiv_t { + pub quot: ::std::os::raw::c_long, + pub rem: ::std::os::raw::c_long, +} +#[test] +fn bindgen_test_layout_ldiv_t() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(ldiv_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(ldiv_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).quot as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(ldiv_t), + "::", + stringify!(quot) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rem as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(ldiv_t), + "::", + stringify!(rem) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct lldiv_t { + pub quot: ::std::os::raw::c_longlong, + pub rem: ::std::os::raw::c_longlong, +} +#[test] +fn bindgen_test_layout_lldiv_t() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(lldiv_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(lldiv_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).quot as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(lldiv_t), + "::", + stringify!(quot) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rem as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(lldiv_t), + "::", + stringify!(rem) + ) + ); +} +extern "C" { + pub fn __ctype_get_mb_cur_max() -> usize; +} +extern "C" { + pub fn atof(__nptr: *const ::std::os::raw::c_char) -> f64; +} +extern "C" { + pub fn atoi(__nptr: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn atol(__nptr: *const ::std::os::raw::c_char) -> ::std::os::raw::c_long; +} +extern "C" { + pub fn atoll(__nptr: *const ::std::os::raw::c_char) -> ::std::os::raw::c_longlong; +} +extern "C" { + pub fn strtod( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + ) -> f64; +} +extern "C" { + pub fn strtof( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + ) -> f32; +} +extern "C" { + pub fn strtold( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + ) -> f64; +} +extern "C" { + pub fn strtol( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_long; +} +extern "C" { + pub fn strtoul( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + pub fn strtoq( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_longlong; +} +extern "C" { + pub fn strtouq( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_ulonglong; +} +extern "C" { + pub fn strtoll( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_longlong; +} +extern "C" { + pub fn strtoull( + __nptr: *const ::std::os::raw::c_char, + __endptr: *mut *mut ::std::os::raw::c_char, + __base: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_ulonglong; +} +extern "C" { + pub fn l64a(__n: ::std::os::raw::c_long) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn a64l(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_long; +} +pub type u_char = __u_char; +pub type u_short = __u_short; +pub type u_int = __u_int; +pub type u_long = __u_long; +pub type quad_t = __quad_t; +pub type u_quad_t = __u_quad_t; +pub type fsid_t = __fsid_t; +pub type loff_t = __loff_t; +pub type ino_t = __ino_t; +pub type dev_t = __dev_t; +pub type gid_t = __gid_t; +pub type mode_t = __mode_t; +pub type nlink_t = __nlink_t; +pub type uid_t = __uid_t; +pub type off_t = __off_t; +pub type pid_t = __pid_t; +pub type id_t = __id_t; +pub type daddr_t = __daddr_t; +pub type caddr_t = __caddr_t; +pub type key_t = __key_t; +pub type clock_t = __clock_t; +pub type time_t = __time_t; +pub type clockid_t = __clockid_t; +pub type timer_t = __timer_t; +pub type ulong = ::std::os::raw::c_ulong; +pub type ushort = ::std::os::raw::c_ushort; +pub type uint = ::std::os::raw::c_uint; +pub type u_int8_t = ::std::os::raw::c_uchar; +pub type u_int16_t = ::std::os::raw::c_ushort; +pub type u_int32_t = ::std::os::raw::c_uint; +pub type u_int64_t = ::std::os::raw::c_ulong; +pub type register_t = ::std::os::raw::c_long; +pub type __sig_atomic_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sigset_t { + pub __val: [::std::os::raw::c_ulong; 16usize], +} +#[test] +fn bindgen_test_layout___sigset_t() { + assert_eq!( + ::std::mem::size_of::<__sigset_t>(), + 128usize, + concat!("Size of: ", stringify!(__sigset_t)) + ); + assert_eq!( + ::std::mem::align_of::<__sigset_t>(), + 8usize, + concat!("Alignment of ", stringify!(__sigset_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__sigset_t>())).__val as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__sigset_t), + "::", + stringify!(__val) + ) + ); +} +pub type sigset_t = __sigset_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct timespec { + pub tv_sec: __time_t, + pub tv_nsec: __syscall_slong_t, +} +#[test] +fn bindgen_test_layout_timespec() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(timespec)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(timespec)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tv_sec as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(timespec), + "::", + stringify!(tv_sec) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tv_nsec as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(timespec), + "::", + stringify!(tv_nsec) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct timeval { + pub tv_sec: __time_t, + pub tv_usec: __suseconds_t, +} +#[test] +fn bindgen_test_layout_timeval() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(timeval)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(timeval)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tv_sec as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(timeval), + "::", + stringify!(tv_sec) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tv_usec as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(timeval), + "::", + stringify!(tv_usec) + ) + ); +} +pub type suseconds_t = __suseconds_t; +pub type __fd_mask = ::std::os::raw::c_long; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fd_set { + pub __fds_bits: [__fd_mask; 16usize], +} +#[test] +fn bindgen_test_layout_fd_set() { + assert_eq!( + ::std::mem::size_of::(), + 128usize, + concat!("Size of: ", stringify!(fd_set)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(fd_set)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__fds_bits as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(fd_set), + "::", + stringify!(__fds_bits) + ) + ); +} +pub type fd_mask = __fd_mask; +extern "C" { + pub fn select( + __nfds: ::std::os::raw::c_int, + __readfds: *mut fd_set, + __writefds: *mut fd_set, + __exceptfds: *mut fd_set, + __timeout: *mut timeval, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn pselect( + __nfds: ::std::os::raw::c_int, + __readfds: *mut fd_set, + __writefds: *mut fd_set, + __exceptfds: *mut fd_set, + __timeout: *const timespec, + __sigmask: *const __sigset_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gnu_dev_major(__dev: ::std::os::raw::c_ulonglong) -> ::std::os::raw::c_uint; +} +extern "C" { + pub fn gnu_dev_minor(__dev: ::std::os::raw::c_ulonglong) -> ::std::os::raw::c_uint; +} +extern "C" { + pub fn gnu_dev_makedev( + __major: ::std::os::raw::c_uint, + __minor: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_ulonglong; +} +pub type blksize_t = __blksize_t; +pub type blkcnt_t = __blkcnt_t; +pub type fsblkcnt_t = __fsblkcnt_t; +pub type fsfilcnt_t = __fsfilcnt_t; +pub type pthread_t = ::std::os::raw::c_ulong; +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_attr_t { + pub __size: [::std::os::raw::c_char; 56usize], + pub __align: ::std::os::raw::c_long, + _bindgen_union_align: [u64; 7usize], +} +#[test] +fn bindgen_test_layout_pthread_attr_t() { + assert_eq!( + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(pthread_attr_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_attr_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_attr_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_attr_t), + "::", + stringify!(__align) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __pthread_internal_list { + pub __prev: *mut __pthread_internal_list, + pub __next: *mut __pthread_internal_list, +} +#[test] +fn bindgen_test_layout___pthread_internal_list() { + assert_eq!( + ::std::mem::size_of::<__pthread_internal_list>(), + 16usize, + concat!("Size of: ", stringify!(__pthread_internal_list)) + ); + assert_eq!( + ::std::mem::align_of::<__pthread_internal_list>(), + 8usize, + concat!("Alignment of ", stringify!(__pthread_internal_list)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__pthread_internal_list>())).__prev as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__pthread_internal_list), + "::", + stringify!(__prev) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__pthread_internal_list>())).__next as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__pthread_internal_list), + "::", + stringify!(__next) + ) + ); +} +pub type __pthread_list_t = __pthread_internal_list; +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_mutex_t { + pub __data: pthread_mutex_t___pthread_mutex_s, + pub __size: [::std::os::raw::c_char; 40usize], + pub __align: ::std::os::raw::c_long, + _bindgen_union_align: [u64; 5usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_mutex_t___pthread_mutex_s { + pub __lock: ::std::os::raw::c_int, + pub __count: ::std::os::raw::c_uint, + pub __owner: ::std::os::raw::c_int, + pub __nusers: ::std::os::raw::c_uint, + pub __kind: ::std::os::raw::c_int, + pub __spins: ::std::os::raw::c_short, + pub __elision: ::std::os::raw::c_short, + pub __list: __pthread_list_t, +} +#[test] +fn bindgen_test_layout_pthread_mutex_t___pthread_mutex_s() { + assert_eq!( + ::std::mem::size_of::(), + 40usize, + concat!("Size of: ", stringify!(pthread_mutex_t___pthread_mutex_s)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(pthread_mutex_t___pthread_mutex_s) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__lock as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__lock) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__count as *const _ + as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__count) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__owner as *const _ + as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__owner) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__nusers as *const _ + as usize + }, + 12usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__nusers) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__kind as *const _ + as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__kind) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__spins as *const _ + as usize + }, + 20usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__spins) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__elision as *const _ + as usize + }, + 22usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__elision) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__list as *const _ + as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t___pthread_mutex_s), + "::", + stringify!(__list) + ) + ); +} +#[test] +fn bindgen_test_layout_pthread_mutex_t() { + assert_eq!( + ::std::mem::size_of::(), + 40usize, + concat!("Size of: ", stringify!(pthread_mutex_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_mutex_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__data as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t), + "::", + stringify!(__data) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutex_t), + "::", + stringify!(__align) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_mutexattr_t { + pub __size: [::std::os::raw::c_char; 4usize], + pub __align: ::std::os::raw::c_int, + _bindgen_union_align: u32, +} +#[test] +fn bindgen_test_layout_pthread_mutexattr_t() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(pthread_mutexattr_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(pthread_mutexattr_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutexattr_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_mutexattr_t), + "::", + stringify!(__align) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_cond_t { + pub __data: pthread_cond_t__bindgen_ty_1, + pub __size: [::std::os::raw::c_char; 48usize], + pub __align: ::std::os::raw::c_longlong, + _bindgen_union_align: [u64; 6usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_cond_t__bindgen_ty_1 { + pub __lock: ::std::os::raw::c_int, + pub __futex: ::std::os::raw::c_uint, + pub __total_seq: ::std::os::raw::c_ulonglong, + pub __wakeup_seq: ::std::os::raw::c_ulonglong, + pub __woken_seq: ::std::os::raw::c_ulonglong, + pub __mutex: *mut ::std::os::raw::c_void, + pub __nwaiters: ::std::os::raw::c_uint, + pub __broadcast_seq: ::std::os::raw::c_uint, +} +#[test] +fn bindgen_test_layout_pthread_cond_t__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 48usize, + concat!("Size of: ", stringify!(pthread_cond_t__bindgen_ty_1)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_cond_t__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__lock as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__lock) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__futex as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__futex) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__total_seq as *const _ + as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__total_seq) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__wakeup_seq as *const _ + as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__wakeup_seq) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__woken_seq as *const _ + as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__woken_seq) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__mutex as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__mutex) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__nwaiters as *const _ as usize + }, + 40usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__nwaiters) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__broadcast_seq as *const _ + as usize + }, + 44usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t__bindgen_ty_1), + "::", + stringify!(__broadcast_seq) + ) + ); +} +#[test] +fn bindgen_test_layout_pthread_cond_t() { + assert_eq!( + ::std::mem::size_of::(), + 48usize, + concat!("Size of: ", stringify!(pthread_cond_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_cond_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__data as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t), + "::", + stringify!(__data) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_cond_t), + "::", + stringify!(__align) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_condattr_t { + pub __size: [::std::os::raw::c_char; 4usize], + pub __align: ::std::os::raw::c_int, + _bindgen_union_align: u32, +} +#[test] +fn bindgen_test_layout_pthread_condattr_t() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(pthread_condattr_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(pthread_condattr_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_condattr_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_condattr_t), + "::", + stringify!(__align) + ) + ); +} +pub type pthread_key_t = ::std::os::raw::c_uint; +pub type pthread_once_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_rwlock_t { + pub __data: pthread_rwlock_t__bindgen_ty_1, + pub __size: [::std::os::raw::c_char; 56usize], + pub __align: ::std::os::raw::c_long, + _bindgen_union_align: [u64; 7usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct pthread_rwlock_t__bindgen_ty_1 { + pub __lock: ::std::os::raw::c_int, + pub __nr_readers: ::std::os::raw::c_uint, + pub __readers_wakeup: ::std::os::raw::c_uint, + pub __writer_wakeup: ::std::os::raw::c_uint, + pub __nr_readers_queued: ::std::os::raw::c_uint, + pub __nr_writers_queued: ::std::os::raw::c_uint, + pub __writer: ::std::os::raw::c_int, + pub __shared: ::std::os::raw::c_int, + pub __rwelision: ::std::os::raw::c_schar, + pub __pad1: [::std::os::raw::c_uchar; 7usize], + pub __pad2: ::std::os::raw::c_ulong, + pub __flags: ::std::os::raw::c_uint, +} +#[test] +fn bindgen_test_layout_pthread_rwlock_t__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(pthread_rwlock_t__bindgen_ty_1)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_rwlock_t__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__lock as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__lock) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__nr_readers as *const _ + as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__nr_readers) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__readers_wakeup as *const _ + as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__readers_wakeup) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__writer_wakeup as *const _ + as usize + }, + 12usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__writer_wakeup) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__nr_readers_queued + as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__nr_readers_queued) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__nr_writers_queued + as *const _ as usize + }, + 20usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__nr_writers_queued) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__writer as *const _ as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__writer) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__shared as *const _ as usize + }, + 28usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__shared) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__rwelision as *const _ + as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__rwelision) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__pad1 as *const _ as usize + }, + 33usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__pad1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__pad2 as *const _ as usize + }, + 40usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__pad2) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__flags as *const _ as usize + }, + 48usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t__bindgen_ty_1), + "::", + stringify!(__flags) + ) + ); +} +#[test] +fn bindgen_test_layout_pthread_rwlock_t() { + assert_eq!( + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(pthread_rwlock_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_rwlock_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__data as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t), + "::", + stringify!(__data) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlock_t), + "::", + stringify!(__align) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_rwlockattr_t { + pub __size: [::std::os::raw::c_char; 8usize], + pub __align: ::std::os::raw::c_long, + _bindgen_union_align: u64, +} +#[test] +fn bindgen_test_layout_pthread_rwlockattr_t() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(pthread_rwlockattr_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_rwlockattr_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlockattr_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_rwlockattr_t), + "::", + stringify!(__align) + ) + ); +} +pub type pthread_spinlock_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_barrier_t { + pub __size: [::std::os::raw::c_char; 32usize], + pub __align: ::std::os::raw::c_long, + _bindgen_union_align: [u64; 4usize], +} +#[test] +fn bindgen_test_layout_pthread_barrier_t() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(pthread_barrier_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(pthread_barrier_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_barrier_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_barrier_t), + "::", + stringify!(__align) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union pthread_barrierattr_t { + pub __size: [::std::os::raw::c_char; 4usize], + pub __align: ::std::os::raw::c_int, + _bindgen_union_align: u32, +} +#[test] +fn bindgen_test_layout_pthread_barrierattr_t() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(pthread_barrierattr_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(pthread_barrierattr_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__size as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_barrierattr_t), + "::", + stringify!(__size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__align as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(pthread_barrierattr_t), + "::", + stringify!(__align) + ) + ); +} +extern "C" { + pub fn random() -> ::std::os::raw::c_long; +} +extern "C" { + pub fn srandom(__seed: ::std::os::raw::c_uint); +} +extern "C" { + pub fn initstate( + __seed: ::std::os::raw::c_uint, + __statebuf: *mut ::std::os::raw::c_char, + __statelen: usize, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn setstate(__statebuf: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct random_data { + pub fptr: *mut i32, + pub rptr: *mut i32, + pub state: *mut i32, + pub rand_type: ::std::os::raw::c_int, + pub rand_deg: ::std::os::raw::c_int, + pub rand_sep: ::std::os::raw::c_int, + pub end_ptr: *mut i32, +} +#[test] +fn bindgen_test_layout_random_data() { + assert_eq!( + ::std::mem::size_of::(), + 48usize, + concat!("Size of: ", stringify!(random_data)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(random_data)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).fptr as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(random_data), + "::", + stringify!(fptr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rptr as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(random_data), + "::", + stringify!(rptr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).state as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(random_data), + "::", + stringify!(state) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rand_type as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(random_data), + "::", + stringify!(rand_type) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rand_deg as *const _ as usize }, + 28usize, + concat!( + "Offset of field: ", + stringify!(random_data), + "::", + stringify!(rand_deg) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rand_sep as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(random_data), + "::", + stringify!(rand_sep) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).end_ptr as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(random_data), + "::", + stringify!(end_ptr) + ) + ); +} +extern "C" { + pub fn random_r(__buf: *mut random_data, __result: *mut i32) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn srandom_r( + __seed: ::std::os::raw::c_uint, + __buf: *mut random_data, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn initstate_r( + __seed: ::std::os::raw::c_uint, + __statebuf: *mut ::std::os::raw::c_char, + __statelen: usize, + __buf: *mut random_data, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn setstate_r( + __statebuf: *mut ::std::os::raw::c_char, + __buf: *mut random_data, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn rand() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn srand(__seed: ::std::os::raw::c_uint); +} +extern "C" { + pub fn rand_r(__seed: *mut ::std::os::raw::c_uint) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn drand48() -> f64; +} +extern "C" { + pub fn erand48(__xsubi: *mut ::std::os::raw::c_ushort) -> f64; +} +extern "C" { + pub fn lrand48() -> ::std::os::raw::c_long; +} +extern "C" { + pub fn nrand48(__xsubi: *mut ::std::os::raw::c_ushort) -> ::std::os::raw::c_long; +} +extern "C" { + pub fn mrand48() -> ::std::os::raw::c_long; +} +extern "C" { + pub fn jrand48(__xsubi: *mut ::std::os::raw::c_ushort) -> ::std::os::raw::c_long; +} +extern "C" { + pub fn srand48(__seedval: ::std::os::raw::c_long); +} +extern "C" { + pub fn seed48(__seed16v: *mut ::std::os::raw::c_ushort) -> *mut ::std::os::raw::c_ushort; +} +extern "C" { + pub fn lcong48(__param: *mut ::std::os::raw::c_ushort); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct drand48_data { + pub __x: [::std::os::raw::c_ushort; 3usize], + pub __old_x: [::std::os::raw::c_ushort; 3usize], + pub __c: ::std::os::raw::c_ushort, + pub __init: ::std::os::raw::c_ushort, + pub __a: ::std::os::raw::c_ulonglong, +} +#[test] +fn bindgen_test_layout_drand48_data() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(drand48_data)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(drand48_data)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__x as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(drand48_data), + "::", + stringify!(__x) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__old_x as *const _ as usize }, + 6usize, + concat!( + "Offset of field: ", + stringify!(drand48_data), + "::", + stringify!(__old_x) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__c as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(drand48_data), + "::", + stringify!(__c) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__init as *const _ as usize }, + 14usize, + concat!( + "Offset of field: ", + stringify!(drand48_data), + "::", + stringify!(__init) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__a as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(drand48_data), + "::", + stringify!(__a) + ) + ); +} +extern "C" { + pub fn drand48_r(__buffer: *mut drand48_data, __result: *mut f64) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn erand48_r( + __xsubi: *mut ::std::os::raw::c_ushort, + __buffer: *mut drand48_data, + __result: *mut f64, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn lrand48_r( + __buffer: *mut drand48_data, + __result: *mut ::std::os::raw::c_long, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn nrand48_r( + __xsubi: *mut ::std::os::raw::c_ushort, + __buffer: *mut drand48_data, + __result: *mut ::std::os::raw::c_long, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn mrand48_r( + __buffer: *mut drand48_data, + __result: *mut ::std::os::raw::c_long, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn jrand48_r( + __xsubi: *mut ::std::os::raw::c_ushort, + __buffer: *mut drand48_data, + __result: *mut ::std::os::raw::c_long, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn srand48_r( + __seedval: ::std::os::raw::c_long, + __buffer: *mut drand48_data, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn seed48_r( + __seed16v: *mut ::std::os::raw::c_ushort, + __buffer: *mut drand48_data, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn lcong48_r( + __param: *mut ::std::os::raw::c_ushort, + __buffer: *mut drand48_data, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn malloc(__size: usize) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn calloc(__nmemb: usize, __size: usize) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn realloc( + __ptr: *mut ::std::os::raw::c_void, + __size: usize, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn free(__ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn cfree(__ptr: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn alloca(__size: usize) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn valloc(__size: usize) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn posix_memalign( + __memptr: *mut *mut ::std::os::raw::c_void, + __alignment: usize, + __size: usize, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn aligned_alloc(__alignment: usize, __size: usize) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn abort(); +} +extern "C" { + pub fn atexit(__func: ::std::option::Option) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn at_quick_exit( + __func: ::std::option::Option, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn on_exit( + __func: ::std::option::Option< + unsafe extern "C" fn( + __status: ::std::os::raw::c_int, + __arg: *mut ::std::os::raw::c_void, + ), + >, + __arg: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn exit(__status: ::std::os::raw::c_int); +} +extern "C" { + pub fn quick_exit(__status: ::std::os::raw::c_int); +} +extern "C" { + pub fn _Exit(__status: ::std::os::raw::c_int); +} +extern "C" { + pub fn getenv(__name: *const ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn putenv(__string: *mut ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn setenv( + __name: *const ::std::os::raw::c_char, + __value: *const ::std::os::raw::c_char, + __replace: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn unsetenv(__name: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn clearenv() -> ::std::os::raw::c_int; +} +extern "C" { + pub fn mktemp(__template: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn mkstemp(__template: *mut ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn mkstemps( + __template: *mut ::std::os::raw::c_char, + __suffixlen: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn mkdtemp(__template: *mut ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn system(__command: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn realpath( + __name: *const ::std::os::raw::c_char, + __resolved: *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +pub type __compar_fn_t = ::std::option::Option< + unsafe extern "C" fn(arg1: *const ::std::os::raw::c_void, arg2: *const ::std::os::raw::c_void) + -> ::std::os::raw::c_int, +>; +extern "C" { + pub fn bsearch( + __key: *const ::std::os::raw::c_void, + __base: *const ::std::os::raw::c_void, + __nmemb: usize, + __size: usize, + __compar: __compar_fn_t, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn qsort( + __base: *mut ::std::os::raw::c_void, + __nmemb: usize, + __size: usize, + __compar: __compar_fn_t, + ); +} +extern "C" { + pub fn abs(__x: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn labs(__x: ::std::os::raw::c_long) -> ::std::os::raw::c_long; +} +extern "C" { + pub fn llabs(__x: ::std::os::raw::c_longlong) -> ::std::os::raw::c_longlong; +} +extern "C" { + pub fn div(__numer: ::std::os::raw::c_int, __denom: ::std::os::raw::c_int) -> div_t; +} +extern "C" { + pub fn ldiv(__numer: ::std::os::raw::c_long, __denom: ::std::os::raw::c_long) -> ldiv_t; +} +extern "C" { + pub fn lldiv( + __numer: ::std::os::raw::c_longlong, + __denom: ::std::os::raw::c_longlong, + ) -> lldiv_t; +} +extern "C" { + pub fn ecvt( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn fcvt( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn gcvt( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn qecvt( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn qfcvt( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn qgcvt( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn ecvt_r( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + __len: usize, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn fcvt_r( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + __len: usize, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn qecvt_r( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + __len: usize, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn qfcvt_r( + __value: f64, + __ndigit: ::std::os::raw::c_int, + __decpt: *mut ::std::os::raw::c_int, + __sign: *mut ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + __len: usize, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn mblen(__s: *const ::std::os::raw::c_char, __n: usize) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn mbtowc( + __pwc: *mut wchar_t, + __s: *const ::std::os::raw::c_char, + __n: usize, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn wctomb(__s: *mut ::std::os::raw::c_char, __wchar: wchar_t) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn mbstowcs(__pwcs: *mut wchar_t, __s: *const ::std::os::raw::c_char, __n: usize) -> usize; +} +extern "C" { + pub fn wcstombs(__s: *mut ::std::os::raw::c_char, __pwcs: *const wchar_t, __n: usize) -> usize; +} +extern "C" { + pub fn rpmatch(__response: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn getsubopt( + __optionp: *mut *mut ::std::os::raw::c_char, + __tokens: *const *const ::std::os::raw::c_char, + __valuep: *mut *mut ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn getloadavg(__loadavg: *mut f64, __nelem: ::std::os::raw::c_int) + -> ::std::os::raw::c_int; +} +/// * +/// Digital audio interface * +/// * +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_aes_iec958 { + pub status: [::std::os::raw::c_uchar; 24usize], + pub subcode: [::std::os::raw::c_uchar; 147usize], + pub pad: ::std::os::raw::c_uchar, + pub dig_subframe: [::std::os::raw::c_uchar; 4usize], +} +#[test] +fn bindgen_test_layout_snd_aes_iec958() { + assert_eq!( + ::std::mem::size_of::(), + 176usize, + concat!("Size of: ", stringify!(snd_aes_iec958)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(snd_aes_iec958)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).status as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_aes_iec958), + "::", + stringify!(status) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subcode as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_aes_iec958), + "::", + stringify!(subcode) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pad as *const _ as usize }, + 171usize, + concat!( + "Offset of field: ", + stringify!(snd_aes_iec958), + "::", + stringify!(pad) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dig_subframe as *const _ as usize }, + 172usize, + concat!( + "Offset of field: ", + stringify!(snd_aes_iec958), + "::", + stringify!(dig_subframe) + ) + ); +} +/// * +/// CEA-861 Audio InfoFrame. Used in HDMI and DisplayPort * +/// * +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_cea_861_aud_if { + pub db1_ct_cc: ::std::os::raw::c_uchar, + pub db2_sf_ss: ::std::os::raw::c_uchar, + pub db3: ::std::os::raw::c_uchar, + pub db4_ca: ::std::os::raw::c_uchar, + pub db5_dminh_lsv: ::std::os::raw::c_uchar, +} +#[test] +fn bindgen_test_layout_snd_cea_861_aud_if() { + assert_eq!( + ::std::mem::size_of::(), + 5usize, + concat!("Size of: ", stringify!(snd_cea_861_aud_if)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(snd_cea_861_aud_if)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).db1_ct_cc as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_cea_861_aud_if), + "::", + stringify!(db1_ct_cc) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).db2_sf_ss as *const _ as usize }, + 1usize, + concat!( + "Offset of field: ", + stringify!(snd_cea_861_aud_if), + "::", + stringify!(db2_sf_ss) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).db3 as *const _ as usize }, + 2usize, + concat!( + "Offset of field: ", + stringify!(snd_cea_861_aud_if), + "::", + stringify!(db3) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).db4_ca as *const _ as usize }, + 3usize, + concat!( + "Offset of field: ", + stringify!(snd_cea_861_aud_if), + "::", + stringify!(db4_ca) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).db5_dminh_lsv as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_cea_861_aud_if), + "::", + stringify!(db5_dminh_lsv) + ) + ); +} +pub const SNDRV_HWDEP_IFACE_OPL2: _bindgen_ty_1 = 0; +pub const SNDRV_HWDEP_IFACE_OPL3: _bindgen_ty_1 = 1; +pub const SNDRV_HWDEP_IFACE_OPL4: _bindgen_ty_1 = 2; +pub const SNDRV_HWDEP_IFACE_SB16CSP: _bindgen_ty_1 = 3; +pub const SNDRV_HWDEP_IFACE_EMU10K1: _bindgen_ty_1 = 4; +pub const SNDRV_HWDEP_IFACE_YSS225: _bindgen_ty_1 = 5; +pub const SNDRV_HWDEP_IFACE_ICS2115: _bindgen_ty_1 = 6; +pub const SNDRV_HWDEP_IFACE_SSCAPE: _bindgen_ty_1 = 7; +pub const SNDRV_HWDEP_IFACE_VX: _bindgen_ty_1 = 8; +pub const SNDRV_HWDEP_IFACE_MIXART: _bindgen_ty_1 = 9; +pub const SNDRV_HWDEP_IFACE_USX2Y: _bindgen_ty_1 = 10; +pub const SNDRV_HWDEP_IFACE_EMUX_WAVETABLE: _bindgen_ty_1 = 11; +pub const SNDRV_HWDEP_IFACE_BLUETOOTH: _bindgen_ty_1 = 12; +pub const SNDRV_HWDEP_IFACE_USX2Y_PCM: _bindgen_ty_1 = 13; +pub const SNDRV_HWDEP_IFACE_PCXHR: _bindgen_ty_1 = 14; +pub const SNDRV_HWDEP_IFACE_SB_RC: _bindgen_ty_1 = 15; +pub const SNDRV_HWDEP_IFACE_HDA: _bindgen_ty_1 = 16; +pub const SNDRV_HWDEP_IFACE_USB_STREAM: _bindgen_ty_1 = 17; +pub const SNDRV_HWDEP_IFACE_FW_DICE: _bindgen_ty_1 = 18; +pub const SNDRV_HWDEP_IFACE_FW_FIREWORKS: _bindgen_ty_1 = 19; +pub const SNDRV_HWDEP_IFACE_FW_BEBOB: _bindgen_ty_1 = 20; +pub const SNDRV_HWDEP_IFACE_FW_OXFW: _bindgen_ty_1 = 21; +pub const SNDRV_HWDEP_IFACE_FW_DIGI00X: _bindgen_ty_1 = 22; +pub const SNDRV_HWDEP_IFACE_FW_TASCAM: _bindgen_ty_1 = 23; +pub const SNDRV_HWDEP_IFACE_LINE6: _bindgen_ty_1 = 24; +pub const SNDRV_HWDEP_IFACE_FW_MOTU: _bindgen_ty_1 = 25; +pub const SNDRV_HWDEP_IFACE_FW_FIREFACE: _bindgen_ty_1 = 26; +pub const SNDRV_HWDEP_IFACE_LAST: _bindgen_ty_1 = 26; +pub type _bindgen_ty_1 = u32; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_hwdep_info { + pub device: ::std::os::raw::c_uint, + pub card: ::std::os::raw::c_int, + pub id: [::std::os::raw::c_uchar; 64usize], + pub name: [::std::os::raw::c_uchar; 80usize], + pub iface: ::std::os::raw::c_int, + pub reserved: [::std::os::raw::c_uchar; 64usize], +} +#[test] +fn bindgen_test_layout_snd_hwdep_info() { + assert_eq!( + ::std::mem::size_of::(), + 220usize, + concat!("Size of: ", stringify!(snd_hwdep_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_hwdep_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).device as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_info), + "::", + stringify!(device) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).card as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_info), + "::", + stringify!(card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_info), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_info), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).iface as *const _ as usize }, + 152usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_info), + "::", + stringify!(iface) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 156usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_info), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_hwdep_dsp_status { + pub version: ::std::os::raw::c_uint, + pub id: [::std::os::raw::c_uchar; 32usize], + pub num_dsps: ::std::os::raw::c_uint, + pub dsp_loaded: ::std::os::raw::c_uint, + pub chip_ready: ::std::os::raw::c_uint, + pub reserved: [::std::os::raw::c_uchar; 16usize], +} +#[test] +fn bindgen_test_layout_snd_hwdep_dsp_status() { + assert_eq!( + ::std::mem::size_of::(), + 64usize, + concat!("Size of: ", stringify!(snd_hwdep_dsp_status)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_hwdep_dsp_status)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).version as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_status), + "::", + stringify!(version) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_status), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).num_dsps as *const _ as usize }, + 36usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_status), + "::", + stringify!(num_dsps) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dsp_loaded as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_status), + "::", + stringify!(dsp_loaded) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).chip_ready as *const _ as usize }, + 44usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_status), + "::", + stringify!(chip_ready) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_status), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_hwdep_dsp_image { + pub index: ::std::os::raw::c_uint, + pub name: [::std::os::raw::c_uchar; 64usize], + pub image: *mut ::std::os::raw::c_uchar, + pub length: usize, + pub driver_data: ::std::os::raw::c_ulong, +} +#[test] +fn bindgen_test_layout_snd_hwdep_dsp_image() { + assert_eq!( + ::std::mem::size_of::(), + 96usize, + concat!("Size of: ", stringify!(snd_hwdep_dsp_image)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_hwdep_dsp_image)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).index as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_image), + "::", + stringify!(index) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_image), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).image as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_image), + "::", + stringify!(image) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).length as *const _ as usize }, + 80usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_image), + "::", + stringify!(length) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).driver_data as *const _ as usize }, + 88usize, + concat!( + "Offset of field: ", + stringify!(snd_hwdep_dsp_image), + "::", + stringify!(driver_data) + ) + ); +} +pub type snd_pcm_uframes_t = ::std::os::raw::c_ulong; +pub type snd_pcm_sframes_t = ::std::os::raw::c_long; +pub const SNDRV_PCM_CLASS_GENERIC: _bindgen_ty_2 = 0; +pub const SNDRV_PCM_CLASS_MULTI: _bindgen_ty_2 = 1; +pub const SNDRV_PCM_CLASS_MODEM: _bindgen_ty_2 = 2; +pub const SNDRV_PCM_CLASS_DIGITIZER: _bindgen_ty_2 = 3; +pub const SNDRV_PCM_CLASS_LAST: _bindgen_ty_2 = 3; +pub type _bindgen_ty_2 = u32; +pub const SNDRV_PCM_SUBCLASS_GENERIC_MIX: _bindgen_ty_3 = 0; +pub const SNDRV_PCM_SUBCLASS_MULTI_MIX: _bindgen_ty_3 = 1; +pub const SNDRV_PCM_SUBCLASS_LAST: _bindgen_ty_3 = 1; +pub type _bindgen_ty_3 = u32; +pub const SNDRV_PCM_STREAM_PLAYBACK: _bindgen_ty_4 = 0; +pub const SNDRV_PCM_STREAM_CAPTURE: _bindgen_ty_4 = 1; +pub const SNDRV_PCM_STREAM_LAST: _bindgen_ty_4 = 1; +pub type _bindgen_ty_4 = u32; +pub type snd_pcm_access_t = ::std::os::raw::c_int; +pub type snd_pcm_format_t = ::std::os::raw::c_int; +pub type snd_pcm_subformat_t = ::std::os::raw::c_int; +pub type snd_pcm_state_t = ::std::os::raw::c_int; +pub const SNDRV_PCM_MMAP_OFFSET_DATA: _bindgen_ty_5 = 0; +pub const SNDRV_PCM_MMAP_OFFSET_STATUS: _bindgen_ty_5 = 2147483648; +pub const SNDRV_PCM_MMAP_OFFSET_CONTROL: _bindgen_ty_5 = 2164260864; +pub type _bindgen_ty_5 = u32; +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_pcm_sync_id { + pub id: [::std::os::raw::c_uchar; 16usize], + pub id16: [::std::os::raw::c_ushort; 8usize], + pub id32: [::std::os::raw::c_uint; 4usize], + _bindgen_union_align: [u32; 4usize], +} +#[test] +fn bindgen_test_layout_snd_pcm_sync_id() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(snd_pcm_sync_id)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_pcm_sync_id)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_id), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id16 as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_id), + "::", + stringify!(id16) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id32 as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_id), + "::", + stringify!(id32) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_pcm_info { + pub device: ::std::os::raw::c_uint, + pub subdevice: ::std::os::raw::c_uint, + pub stream: ::std::os::raw::c_int, + pub card: ::std::os::raw::c_int, + pub id: [::std::os::raw::c_uchar; 64usize], + pub name: [::std::os::raw::c_uchar; 80usize], + pub subname: [::std::os::raw::c_uchar; 32usize], + pub dev_class: ::std::os::raw::c_int, + pub dev_subclass: ::std::os::raw::c_int, + pub subdevices_count: ::std::os::raw::c_uint, + pub subdevices_avail: ::std::os::raw::c_uint, + pub sync: snd_pcm_sync_id, + pub reserved: [::std::os::raw::c_uchar; 64usize], +} +#[test] +fn bindgen_test_layout_snd_pcm_info() { + assert_eq!( + ::std::mem::size_of::(), + 288usize, + concat!("Size of: ", stringify!(snd_pcm_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_pcm_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).device as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(device) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subdevice as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(subdevice) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).stream as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(stream) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).card as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 80usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subname as *const _ as usize }, + 160usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(subname) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dev_class as *const _ as usize }, + 192usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(dev_class) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dev_subclass as *const _ as usize }, + 196usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(dev_subclass) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subdevices_count as *const _ as usize }, + 200usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(subdevices_count) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subdevices_avail as *const _ as usize }, + 204usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(subdevices_avail) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).sync as *const _ as usize }, + 208usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(sync) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 224usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_info), + "::", + stringify!(reserved) + ) + ); +} +pub type snd_pcm_hw_param_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_interval { + pub min: ::std::os::raw::c_uint, + pub max: ::std::os::raw::c_uint, + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize], u8>, + pub __bindgen_padding_0: [u8; 3usize], +} +#[test] +fn bindgen_test_layout_snd_interval() { + assert_eq!( + ::std::mem::size_of::(), + 12usize, + concat!("Size of: ", stringify!(snd_interval)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_interval)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).min as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_interval), + "::", + stringify!(min) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).max as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_interval), + "::", + stringify!(max) + ) + ); +} +impl snd_interval { + #[inline] + pub fn openmin(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u32) } + } + #[inline] + pub fn set_openmin(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn openmax(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u32) } + } + #[inline] + pub fn set_openmax(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn integer(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u32) } + } + #[inline] + pub fn set_integer(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn empty(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u32) } + } + #[inline] + pub fn set_empty(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + openmin: ::std::os::raw::c_uint, + openmax: ::std::os::raw::c_uint, + integer: ::std::os::raw::c_uint, + empty: ::std::os::raw::c_uint, + ) -> __BindgenBitfieldUnit<[u8; 1usize], u8> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize], u8> = + Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let openmin: u32 = unsafe { ::std::mem::transmute(openmin) }; + openmin as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let openmax: u32 = unsafe { ::std::mem::transmute(openmax) }; + openmax as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let integer: u32 = unsafe { ::std::mem::transmute(integer) }; + integer as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let empty: u32 = unsafe { ::std::mem::transmute(empty) }; + empty as u64 + }); + __bindgen_bitfield_unit + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_mask { + pub bits: [__u32; 8usize], +} +#[test] +fn bindgen_test_layout_snd_mask() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(snd_mask)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_mask)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).bits as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_mask), + "::", + stringify!(bits) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_pcm_hw_params { + pub flags: ::std::os::raw::c_uint, + pub masks: [snd_mask; 3usize], + pub mres: [snd_mask; 5usize], + pub intervals: [snd_interval; 12usize], + pub ires: [snd_interval; 9usize], + pub rmask: ::std::os::raw::c_uint, + pub cmask: ::std::os::raw::c_uint, + pub info: ::std::os::raw::c_uint, + pub msbits: ::std::os::raw::c_uint, + pub rate_num: ::std::os::raw::c_uint, + pub rate_den: ::std::os::raw::c_uint, + pub fifo_size: snd_pcm_uframes_t, + pub reserved: [::std::os::raw::c_uchar; 64usize], +} +#[test] +fn bindgen_test_layout_snd_pcm_hw_params() { + assert_eq!( + ::std::mem::size_of::(), + 608usize, + concat!("Size of: ", stringify!(snd_pcm_hw_params)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_hw_params)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).masks as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(masks) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).mres as *const _ as usize }, + 100usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(mres) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).intervals as *const _ as usize }, + 260usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(intervals) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ires as *const _ as usize }, + 404usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(ires) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rmask as *const _ as usize }, + 512usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(rmask) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cmask as *const _ as usize }, + 516usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(cmask) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).info as *const _ as usize }, + 520usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(info) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).msbits as *const _ as usize }, + 524usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(msbits) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rate_num as *const _ as usize }, + 528usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(rate_num) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rate_den as *const _ as usize }, + 532usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(rate_den) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).fifo_size as *const _ as usize }, + 536usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(fifo_size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 544usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_hw_params), + "::", + stringify!(reserved) + ) + ); +} +pub const SNDRV_PCM_TSTAMP_NONE: _bindgen_ty_6 = 0; +pub const SNDRV_PCM_TSTAMP_ENABLE: _bindgen_ty_6 = 1; +pub const SNDRV_PCM_TSTAMP_LAST: _bindgen_ty_6 = 1; +pub type _bindgen_ty_6 = u32; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_pcm_sw_params { + pub tstamp_mode: ::std::os::raw::c_int, + pub period_step: ::std::os::raw::c_uint, + pub sleep_min: ::std::os::raw::c_uint, + pub avail_min: snd_pcm_uframes_t, + pub xfer_align: snd_pcm_uframes_t, + pub start_threshold: snd_pcm_uframes_t, + pub stop_threshold: snd_pcm_uframes_t, + pub silence_threshold: snd_pcm_uframes_t, + pub silence_size: snd_pcm_uframes_t, + pub boundary: snd_pcm_uframes_t, + pub proto: ::std::os::raw::c_uint, + pub tstamp_type: ::std::os::raw::c_uint, + pub reserved: [::std::os::raw::c_uchar; 56usize], +} +#[test] +fn bindgen_test_layout_snd_pcm_sw_params() { + assert_eq!( + ::std::mem::size_of::(), + 136usize, + concat!("Size of: ", stringify!(snd_pcm_sw_params)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_sw_params)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp_mode as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(tstamp_mode) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).period_step as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(period_step) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).sleep_min as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(sleep_min) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).avail_min as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(avail_min) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xfer_align as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(xfer_align) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).start_threshold as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(start_threshold) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).stop_threshold as *const _ as usize + }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(stop_threshold) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).silence_threshold as *const _ as usize + }, + 48usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(silence_threshold) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).silence_size as *const _ as usize }, + 56usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(silence_size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).boundary as *const _ as usize }, + 64usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(boundary) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).proto as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(proto) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp_type as *const _ as usize }, + 76usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(tstamp_type) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 80usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sw_params), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_pcm_channel_info { + pub channel: ::std::os::raw::c_uint, + pub offset: __kernel_off_t, + pub first: ::std::os::raw::c_uint, + pub step: ::std::os::raw::c_uint, +} +#[test] +fn bindgen_test_layout_snd_pcm_channel_info() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(snd_pcm_channel_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_channel_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).channel as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_channel_info), + "::", + stringify!(channel) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).offset as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_channel_info), + "::", + stringify!(offset) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).first as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_channel_info), + "::", + stringify!(first) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).step as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_channel_info), + "::", + stringify!(step) + ) + ); +} +pub const SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT: _bindgen_ty_7 = 0; +pub const SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT: _bindgen_ty_7 = 1; +pub const SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK: _bindgen_ty_7 = 2; +pub const SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE: _bindgen_ty_7 = 3; +pub const SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED: _bindgen_ty_7 = 4; +pub const SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED: _bindgen_ty_7 = 5; +pub const SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST: _bindgen_ty_7 = 5; +pub type _bindgen_ty_7 = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_pcm_status { + pub state: snd_pcm_state_t, + pub trigger_tstamp: timespec, + pub tstamp: timespec, + pub appl_ptr: snd_pcm_uframes_t, + pub hw_ptr: snd_pcm_uframes_t, + pub delay: snd_pcm_sframes_t, + pub avail: snd_pcm_uframes_t, + pub avail_max: snd_pcm_uframes_t, + pub overrange: snd_pcm_uframes_t, + pub suspended_state: snd_pcm_state_t, + pub audio_tstamp_data: __u32, + pub audio_tstamp: timespec, + pub driver_tstamp: timespec, + pub audio_tstamp_accuracy: __u32, + pub reserved: [::std::os::raw::c_uchar; 20usize], +} +#[test] +fn bindgen_test_layout_snd_pcm_status() { + assert_eq!( + ::std::mem::size_of::(), + 152usize, + concat!("Size of: ", stringify!(snd_pcm_status)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_status)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).state as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(state) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).trigger_tstamp as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(trigger_tstamp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(tstamp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).appl_ptr as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(appl_ptr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).hw_ptr as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(hw_ptr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).delay as *const _ as usize }, + 56usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(delay) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).avail as *const _ as usize }, + 64usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(avail) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).avail_max as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(avail_max) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).overrange as *const _ as usize }, + 80usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(overrange) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).suspended_state as *const _ as usize }, + 88usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(suspended_state) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).audio_tstamp_data as *const _ as usize + }, + 92usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(audio_tstamp_data) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).audio_tstamp as *const _ as usize }, + 96usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(audio_tstamp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).driver_tstamp as *const _ as usize }, + 112usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(driver_tstamp) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).audio_tstamp_accuracy as *const _ as usize + }, + 128usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(audio_tstamp_accuracy) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 132usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_status), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_pcm_mmap_status { + pub state: snd_pcm_state_t, + pub pad1: ::std::os::raw::c_int, + pub hw_ptr: snd_pcm_uframes_t, + pub tstamp: timespec, + pub suspended_state: snd_pcm_state_t, + pub audio_tstamp: timespec, +} +#[test] +fn bindgen_test_layout_snd_pcm_mmap_status() { + assert_eq!( + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(snd_pcm_mmap_status)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_mmap_status)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).state as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_status), + "::", + stringify!(state) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pad1 as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_status), + "::", + stringify!(pad1) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).hw_ptr as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_status), + "::", + stringify!(hw_ptr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_status), + "::", + stringify!(tstamp) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).suspended_state as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_status), + "::", + stringify!(suspended_state) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).audio_tstamp as *const _ as usize + }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_status), + "::", + stringify!(audio_tstamp) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_pcm_mmap_control { + pub appl_ptr: snd_pcm_uframes_t, + pub avail_min: snd_pcm_uframes_t, +} +#[test] +fn bindgen_test_layout_snd_pcm_mmap_control() { + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(snd_pcm_mmap_control)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_mmap_control)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).appl_ptr as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_control), + "::", + stringify!(appl_ptr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).avail_min as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_mmap_control), + "::", + stringify!(avail_min) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_pcm_sync_ptr { + pub flags: ::std::os::raw::c_uint, + pub s: snd_pcm_sync_ptr__bindgen_ty_1, + pub c: snd_pcm_sync_ptr__bindgen_ty_2, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_pcm_sync_ptr__bindgen_ty_1 { + pub status: snd_pcm_mmap_status, + pub reserved: [::std::os::raw::c_uchar; 64usize], + _bindgen_union_align: [u64; 8usize], +} +#[test] +fn bindgen_test_layout_snd_pcm_sync_ptr__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 64usize, + concat!("Size of: ", stringify!(snd_pcm_sync_ptr__bindgen_ty_1)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_sync_ptr__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).status as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_ptr__bindgen_ty_1), + "::", + stringify!(status) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).reserved as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_ptr__bindgen_ty_1), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_pcm_sync_ptr__bindgen_ty_2 { + pub control: snd_pcm_mmap_control, + pub reserved: [::std::os::raw::c_uchar; 64usize], + _bindgen_union_align: [u64; 8usize], +} +#[test] +fn bindgen_test_layout_snd_pcm_sync_ptr__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::(), + 64usize, + concat!("Size of: ", stringify!(snd_pcm_sync_ptr__bindgen_ty_2)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_sync_ptr__bindgen_ty_2)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).control as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_ptr__bindgen_ty_2), + "::", + stringify!(control) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).reserved as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_ptr__bindgen_ty_2), + "::", + stringify!(reserved) + ) + ); +} +#[test] +fn bindgen_test_layout_snd_pcm_sync_ptr() { + assert_eq!( + ::std::mem::size_of::(), + 136usize, + concat!("Size of: ", stringify!(snd_pcm_sync_ptr)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_pcm_sync_ptr)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_ptr), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).s as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_ptr), + "::", + stringify!(s) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).c as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_pcm_sync_ptr), + "::", + stringify!(c) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_xferi { + pub result: snd_pcm_sframes_t, + pub buf: *mut ::std::os::raw::c_void, + pub frames: snd_pcm_uframes_t, +} +#[test] +fn bindgen_test_layout_snd_xferi() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(snd_xferi)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_xferi)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).result as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_xferi), + "::", + stringify!(result) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).buf as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_xferi), + "::", + stringify!(buf) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).frames as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_xferi), + "::", + stringify!(frames) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_xfern { + pub result: snd_pcm_sframes_t, + pub bufs: *mut *mut ::std::os::raw::c_void, + pub frames: snd_pcm_uframes_t, +} +#[test] +fn bindgen_test_layout_snd_xfern() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(snd_xfern)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_xfern)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).result as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_xfern), + "::", + stringify!(result) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).bufs as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_xfern), + "::", + stringify!(bufs) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).frames as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_xfern), + "::", + stringify!(frames) + ) + ); +} +pub const SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY: _bindgen_ty_8 = 0; +pub const SNDRV_PCM_TSTAMP_TYPE_MONOTONIC: _bindgen_ty_8 = 1; +pub const SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW: _bindgen_ty_8 = 2; +pub const SNDRV_PCM_TSTAMP_TYPE_LAST: _bindgen_ty_8 = 2; +pub type _bindgen_ty_8 = u32; +pub const SNDRV_CHMAP_UNKNOWN: _bindgen_ty_9 = 0; +pub const SNDRV_CHMAP_NA: _bindgen_ty_9 = 1; +pub const SNDRV_CHMAP_MONO: _bindgen_ty_9 = 2; +pub const SNDRV_CHMAP_FL: _bindgen_ty_9 = 3; +pub const SNDRV_CHMAP_FR: _bindgen_ty_9 = 4; +pub const SNDRV_CHMAP_RL: _bindgen_ty_9 = 5; +pub const SNDRV_CHMAP_RR: _bindgen_ty_9 = 6; +pub const SNDRV_CHMAP_FC: _bindgen_ty_9 = 7; +pub const SNDRV_CHMAP_LFE: _bindgen_ty_9 = 8; +pub const SNDRV_CHMAP_SL: _bindgen_ty_9 = 9; +pub const SNDRV_CHMAP_SR: _bindgen_ty_9 = 10; +pub const SNDRV_CHMAP_RC: _bindgen_ty_9 = 11; +pub const SNDRV_CHMAP_FLC: _bindgen_ty_9 = 12; +pub const SNDRV_CHMAP_FRC: _bindgen_ty_9 = 13; +pub const SNDRV_CHMAP_RLC: _bindgen_ty_9 = 14; +pub const SNDRV_CHMAP_RRC: _bindgen_ty_9 = 15; +pub const SNDRV_CHMAP_FLW: _bindgen_ty_9 = 16; +pub const SNDRV_CHMAP_FRW: _bindgen_ty_9 = 17; +pub const SNDRV_CHMAP_FLH: _bindgen_ty_9 = 18; +pub const SNDRV_CHMAP_FCH: _bindgen_ty_9 = 19; +pub const SNDRV_CHMAP_FRH: _bindgen_ty_9 = 20; +pub const SNDRV_CHMAP_TC: _bindgen_ty_9 = 21; +pub const SNDRV_CHMAP_TFL: _bindgen_ty_9 = 22; +pub const SNDRV_CHMAP_TFR: _bindgen_ty_9 = 23; +pub const SNDRV_CHMAP_TFC: _bindgen_ty_9 = 24; +pub const SNDRV_CHMAP_TRL: _bindgen_ty_9 = 25; +pub const SNDRV_CHMAP_TRR: _bindgen_ty_9 = 26; +pub const SNDRV_CHMAP_TRC: _bindgen_ty_9 = 27; +pub const SNDRV_CHMAP_TFLC: _bindgen_ty_9 = 28; +pub const SNDRV_CHMAP_TFRC: _bindgen_ty_9 = 29; +pub const SNDRV_CHMAP_TSL: _bindgen_ty_9 = 30; +pub const SNDRV_CHMAP_TSR: _bindgen_ty_9 = 31; +pub const SNDRV_CHMAP_LLFE: _bindgen_ty_9 = 32; +pub const SNDRV_CHMAP_RLFE: _bindgen_ty_9 = 33; +pub const SNDRV_CHMAP_BC: _bindgen_ty_9 = 34; +pub const SNDRV_CHMAP_BLC: _bindgen_ty_9 = 35; +pub const SNDRV_CHMAP_BRC: _bindgen_ty_9 = 36; +pub const SNDRV_CHMAP_LAST: _bindgen_ty_9 = 36; +pub type _bindgen_ty_9 = u32; +pub const SNDRV_RAWMIDI_STREAM_OUTPUT: _bindgen_ty_10 = 0; +pub const SNDRV_RAWMIDI_STREAM_INPUT: _bindgen_ty_10 = 1; +pub const SNDRV_RAWMIDI_STREAM_LAST: _bindgen_ty_10 = 1; +pub type _bindgen_ty_10 = u32; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_rawmidi_info { + pub device: ::std::os::raw::c_uint, + pub subdevice: ::std::os::raw::c_uint, + pub stream: ::std::os::raw::c_int, + pub card: ::std::os::raw::c_int, + pub flags: ::std::os::raw::c_uint, + pub id: [::std::os::raw::c_uchar; 64usize], + pub name: [::std::os::raw::c_uchar; 80usize], + pub subname: [::std::os::raw::c_uchar; 32usize], + pub subdevices_count: ::std::os::raw::c_uint, + pub subdevices_avail: ::std::os::raw::c_uint, + pub reserved: [::std::os::raw::c_uchar; 64usize], +} +#[test] +fn bindgen_test_layout_snd_rawmidi_info() { + assert_eq!( + ::std::mem::size_of::(), + 268usize, + concat!("Size of: ", stringify!(snd_rawmidi_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_rawmidi_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).device as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(device) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subdevice as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(subdevice) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).stream as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(stream) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).card as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 84usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subname as *const _ as usize }, + 164usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(subname) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).subdevices_count as *const _ as usize + }, + 196usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(subdevices_count) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).subdevices_avail as *const _ as usize + }, + 200usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(subdevices_avail) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 204usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_info), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_rawmidi_params { + pub stream: ::std::os::raw::c_int, + pub buffer_size: usize, + pub avail_min: usize, + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize], u8>, + pub reserved: [::std::os::raw::c_uchar; 16usize], +} +#[test] +fn bindgen_test_layout_snd_rawmidi_params() { + assert_eq!( + ::std::mem::size_of::(), + 48usize, + concat!("Size of: ", stringify!(snd_rawmidi_params)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_rawmidi_params)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).stream as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_params), + "::", + stringify!(stream) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).buffer_size as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_params), + "::", + stringify!(buffer_size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).avail_min as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_params), + "::", + stringify!(avail_min) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 25usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_params), + "::", + stringify!(reserved) + ) + ); +} +impl snd_rawmidi_params { + #[inline] + pub fn no_active_sensing(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u32) } + } + #[inline] + pub fn set_no_active_sensing(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + no_active_sensing: ::std::os::raw::c_uint, + ) -> __BindgenBitfieldUnit<[u8; 1usize], u8> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize], u8> = + Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let no_active_sensing: u32 = unsafe { ::std::mem::transmute(no_active_sensing) }; + no_active_sensing as u64 + }); + __bindgen_bitfield_unit + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_rawmidi_status { + pub stream: ::std::os::raw::c_int, + pub tstamp: timespec, + pub avail: usize, + pub xruns: usize, + pub reserved: [::std::os::raw::c_uchar; 16usize], +} +#[test] +fn bindgen_test_layout_snd_rawmidi_status() { + assert_eq!( + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(snd_rawmidi_status)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_rawmidi_status)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).stream as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_status), + "::", + stringify!(stream) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_status), + "::", + stringify!(tstamp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).avail as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_status), + "::", + stringify!(avail) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).xruns as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_status), + "::", + stringify!(xruns) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_rawmidi_status), + "::", + stringify!(reserved) + ) + ); +} +pub const SNDRV_TIMER_CLASS_NONE: _bindgen_ty_11 = -1; +pub const SNDRV_TIMER_CLASS_SLAVE: _bindgen_ty_11 = 0; +pub const SNDRV_TIMER_CLASS_GLOBAL: _bindgen_ty_11 = 1; +pub const SNDRV_TIMER_CLASS_CARD: _bindgen_ty_11 = 2; +pub const SNDRV_TIMER_CLASS_PCM: _bindgen_ty_11 = 3; +pub const SNDRV_TIMER_CLASS_LAST: _bindgen_ty_11 = 3; +pub type _bindgen_ty_11 = i32; +pub const SNDRV_TIMER_SCLASS_NONE: _bindgen_ty_12 = 0; +pub const SNDRV_TIMER_SCLASS_APPLICATION: _bindgen_ty_12 = 1; +pub const SNDRV_TIMER_SCLASS_SEQUENCER: _bindgen_ty_12 = 2; +pub const SNDRV_TIMER_SCLASS_OSS_SEQUENCER: _bindgen_ty_12 = 3; +pub const SNDRV_TIMER_SCLASS_LAST: _bindgen_ty_12 = 3; +pub type _bindgen_ty_12 = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_timer_id { + pub dev_class: ::std::os::raw::c_int, + pub dev_sclass: ::std::os::raw::c_int, + pub card: ::std::os::raw::c_int, + pub device: ::std::os::raw::c_int, + pub subdevice: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_snd_timer_id() { + assert_eq!( + ::std::mem::size_of::(), + 20usize, + concat!("Size of: ", stringify!(snd_timer_id)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_timer_id)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dev_class as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_id), + "::", + stringify!(dev_class) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dev_sclass as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_id), + "::", + stringify!(dev_sclass) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).card as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_id), + "::", + stringify!(card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).device as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_id), + "::", + stringify!(device) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subdevice as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_id), + "::", + stringify!(subdevice) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_timer_ginfo { + pub tid: snd_timer_id, + pub flags: ::std::os::raw::c_uint, + pub card: ::std::os::raw::c_int, + pub id: [::std::os::raw::c_uchar; 64usize], + pub name: [::std::os::raw::c_uchar; 80usize], + pub reserved0: ::std::os::raw::c_ulong, + pub resolution: ::std::os::raw::c_ulong, + pub resolution_min: ::std::os::raw::c_ulong, + pub resolution_max: ::std::os::raw::c_ulong, + pub clients: ::std::os::raw::c_uint, + pub reserved: [::std::os::raw::c_uchar; 32usize], +} +#[test] +fn bindgen_test_layout_snd_timer_ginfo() { + assert_eq!( + ::std::mem::size_of::(), + 248usize, + concat!("Size of: ", stringify!(snd_timer_ginfo)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_timer_ginfo)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tid as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(tid) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).card as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 28usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 92usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved0 as *const _ as usize }, + 176usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(reserved0) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).resolution as *const _ as usize }, + 184usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(resolution) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).resolution_min as *const _ as usize }, + 192usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(resolution_min) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).resolution_max as *const _ as usize }, + 200usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(resolution_max) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).clients as *const _ as usize }, + 208usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(clients) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 212usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_ginfo), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_timer_gparams { + pub tid: snd_timer_id, + pub period_num: ::std::os::raw::c_ulong, + pub period_den: ::std::os::raw::c_ulong, + pub reserved: [::std::os::raw::c_uchar; 32usize], +} +#[test] +fn bindgen_test_layout_snd_timer_gparams() { + assert_eq!( + ::std::mem::size_of::(), + 72usize, + concat!("Size of: ", stringify!(snd_timer_gparams)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_timer_gparams)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tid as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gparams), + "::", + stringify!(tid) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).period_num as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gparams), + "::", + stringify!(period_num) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).period_den as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gparams), + "::", + stringify!(period_den) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gparams), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_timer_gstatus { + pub tid: snd_timer_id, + pub resolution: ::std::os::raw::c_ulong, + pub resolution_num: ::std::os::raw::c_ulong, + pub resolution_den: ::std::os::raw::c_ulong, + pub reserved: [::std::os::raw::c_uchar; 32usize], +} +#[test] +fn bindgen_test_layout_snd_timer_gstatus() { + assert_eq!( + ::std::mem::size_of::(), + 80usize, + concat!("Size of: ", stringify!(snd_timer_gstatus)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_timer_gstatus)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tid as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gstatus), + "::", + stringify!(tid) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).resolution as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gstatus), + "::", + stringify!(resolution) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).resolution_num as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gstatus), + "::", + stringify!(resolution_num) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).resolution_den as *const _ as usize + }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gstatus), + "::", + stringify!(resolution_den) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_gstatus), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_timer_select { + pub id: snd_timer_id, + pub reserved: [::std::os::raw::c_uchar; 32usize], +} +#[test] +fn bindgen_test_layout_snd_timer_select() { + assert_eq!( + ::std::mem::size_of::(), + 52usize, + concat!("Size of: ", stringify!(snd_timer_select)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_timer_select)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_select), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_select), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_timer_info { + pub flags: ::std::os::raw::c_uint, + pub card: ::std::os::raw::c_int, + pub id: [::std::os::raw::c_uchar; 64usize], + pub name: [::std::os::raw::c_uchar; 80usize], + pub reserved0: ::std::os::raw::c_ulong, + pub resolution: ::std::os::raw::c_ulong, + pub reserved: [::std::os::raw::c_uchar; 64usize], +} +#[test] +fn bindgen_test_layout_snd_timer_info() { + assert_eq!( + ::std::mem::size_of::(), + 232usize, + concat!("Size of: ", stringify!(snd_timer_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_timer_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_info), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).card as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_info), + "::", + stringify!(card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_info), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_info), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved0 as *const _ as usize }, + 152usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_info), + "::", + stringify!(reserved0) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).resolution as *const _ as usize }, + 160usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_info), + "::", + stringify!(resolution) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 168usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_info), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_timer_params { + pub flags: ::std::os::raw::c_uint, + pub ticks: ::std::os::raw::c_uint, + pub queue_size: ::std::os::raw::c_uint, + pub reserved0: ::std::os::raw::c_uint, + pub filter: ::std::os::raw::c_uint, + pub reserved: [::std::os::raw::c_uchar; 60usize], +} +#[test] +fn bindgen_test_layout_snd_timer_params() { + assert_eq!( + ::std::mem::size_of::(), + 80usize, + concat!("Size of: ", stringify!(snd_timer_params)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_timer_params)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_params), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ticks as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_params), + "::", + stringify!(ticks) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).queue_size as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_params), + "::", + stringify!(queue_size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved0 as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_params), + "::", + stringify!(reserved0) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).filter as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_params), + "::", + stringify!(filter) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_params), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_timer_status { + pub tstamp: timespec, + pub resolution: ::std::os::raw::c_uint, + pub lost: ::std::os::raw::c_uint, + pub overrun: ::std::os::raw::c_uint, + pub queue: ::std::os::raw::c_uint, + pub reserved: [::std::os::raw::c_uchar; 64usize], +} +#[test] +fn bindgen_test_layout_snd_timer_status() { + assert_eq!( + ::std::mem::size_of::(), + 96usize, + concat!("Size of: ", stringify!(snd_timer_status)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_timer_status)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_status), + "::", + stringify!(tstamp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).resolution as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_status), + "::", + stringify!(resolution) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).lost as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_status), + "::", + stringify!(lost) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).overrun as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_status), + "::", + stringify!(overrun) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).queue as *const _ as usize }, + 28usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_status), + "::", + stringify!(queue) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_status), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_timer_read { + pub resolution: ::std::os::raw::c_uint, + pub ticks: ::std::os::raw::c_uint, +} +#[test] +fn bindgen_test_layout_snd_timer_read() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(snd_timer_read)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_timer_read)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).resolution as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_read), + "::", + stringify!(resolution) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ticks as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_read), + "::", + stringify!(ticks) + ) + ); +} +pub const SNDRV_TIMER_EVENT_RESOLUTION: _bindgen_ty_13 = 0; +pub const SNDRV_TIMER_EVENT_TICK: _bindgen_ty_13 = 1; +pub const SNDRV_TIMER_EVENT_START: _bindgen_ty_13 = 2; +pub const SNDRV_TIMER_EVENT_STOP: _bindgen_ty_13 = 3; +pub const SNDRV_TIMER_EVENT_CONTINUE: _bindgen_ty_13 = 4; +pub const SNDRV_TIMER_EVENT_PAUSE: _bindgen_ty_13 = 5; +pub const SNDRV_TIMER_EVENT_EARLY: _bindgen_ty_13 = 6; +pub const SNDRV_TIMER_EVENT_SUSPEND: _bindgen_ty_13 = 7; +pub const SNDRV_TIMER_EVENT_RESUME: _bindgen_ty_13 = 8; +pub const SNDRV_TIMER_EVENT_MSTART: _bindgen_ty_13 = 12; +pub const SNDRV_TIMER_EVENT_MSTOP: _bindgen_ty_13 = 13; +pub const SNDRV_TIMER_EVENT_MCONTINUE: _bindgen_ty_13 = 14; +pub const SNDRV_TIMER_EVENT_MPAUSE: _bindgen_ty_13 = 15; +pub const SNDRV_TIMER_EVENT_MSUSPEND: _bindgen_ty_13 = 17; +pub const SNDRV_TIMER_EVENT_MRESUME: _bindgen_ty_13 = 18; +pub type _bindgen_ty_13 = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_timer_tread { + pub event: ::std::os::raw::c_int, + pub tstamp: timespec, + pub val: ::std::os::raw::c_uint, +} +#[test] +fn bindgen_test_layout_snd_timer_tread() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(snd_timer_tread)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_timer_tread)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).event as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_tread), + "::", + stringify!(event) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_tread), + "::", + stringify!(tstamp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).val as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_timer_tread), + "::", + stringify!(val) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_card_info { + pub card: ::std::os::raw::c_int, + pub pad: ::std::os::raw::c_int, + pub id: [::std::os::raw::c_uchar; 16usize], + pub driver: [::std::os::raw::c_uchar; 16usize], + pub name: [::std::os::raw::c_uchar; 32usize], + pub longname: [::std::os::raw::c_uchar; 80usize], + pub reserved_: [::std::os::raw::c_uchar; 16usize], + pub mixername: [::std::os::raw::c_uchar; 80usize], + pub components: [::std::os::raw::c_uchar; 128usize], +} +#[test] +fn bindgen_test_layout_snd_ctl_card_info() { + assert_eq!( + ::std::mem::size_of::(), + 376usize, + concat!("Size of: ", stringify!(snd_ctl_card_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_ctl_card_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).card as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(card) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pad as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(pad) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).driver as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(driver) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).longname as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(longname) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved_ as *const _ as usize }, + 152usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(reserved_) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).mixername as *const _ as usize }, + 168usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(mixername) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).components as *const _ as usize }, + 248usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_card_info), + "::", + stringify!(components) + ) + ); +} +pub type snd_ctl_elem_type_t = ::std::os::raw::c_int; +pub type snd_ctl_elem_iface_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_elem_id { + pub numid: ::std::os::raw::c_uint, + pub iface: snd_ctl_elem_iface_t, + pub device: ::std::os::raw::c_uint, + pub subdevice: ::std::os::raw::c_uint, + pub name: [::std::os::raw::c_uchar; 44usize], + pub index: ::std::os::raw::c_uint, +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_id() { + assert_eq!( + ::std::mem::size_of::(), + 64usize, + concat!("Size of: ", stringify!(snd_ctl_elem_id)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_ctl_elem_id)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).numid as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_id), + "::", + stringify!(numid) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).iface as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_id), + "::", + stringify!(iface) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).device as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_id), + "::", + stringify!(device) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).subdevice as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_id), + "::", + stringify!(subdevice) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).name as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_id), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).index as *const _ as usize }, + 60usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_id), + "::", + stringify!(index) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_elem_list { + pub offset: ::std::os::raw::c_uint, + pub space: ::std::os::raw::c_uint, + pub used: ::std::os::raw::c_uint, + pub count: ::std::os::raw::c_uint, + pub pids: *mut snd_ctl_elem_id, + pub reserved: [::std::os::raw::c_uchar; 50usize], +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_list() { + assert_eq!( + ::std::mem::size_of::(), + 80usize, + concat!("Size of: ", stringify!(snd_ctl_elem_list)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_ctl_elem_list)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).offset as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_list), + "::", + stringify!(offset) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).space as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_list), + "::", + stringify!(space) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).used as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_list), + "::", + stringify!(used) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).count as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_list), + "::", + stringify!(count) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).pids as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_list), + "::", + stringify!(pids) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_list), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_elem_info { + pub id: snd_ctl_elem_id, + pub type_: snd_ctl_elem_type_t, + pub access: ::std::os::raw::c_uint, + pub count: ::std::os::raw::c_uint, + pub owner: __kernel_pid_t, + pub value: snd_ctl_elem_info__bindgen_ty_1, + pub dimen: snd_ctl_elem_info__bindgen_ty_2, + pub reserved: [::std::os::raw::c_uchar; 56usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_elem_info__bindgen_ty_1 { + pub integer: snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1, + pub integer64: snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2, + pub enumerated: snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3, + pub reserved: [::std::os::raw::c_uchar; 128usize], + _bindgen_union_align: [u64; 16usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1 { + pub min: ::std::os::raw::c_long, + pub max: ::std::os::raw::c_long, + pub step: ::std::os::raw::c_long, +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!( + "Size of: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).min + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(min) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).max + as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(max) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).step + as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(step) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2 { + pub min: ::std::os::raw::c_longlong, + pub max: ::std::os::raw::c_longlong, + pub step: ::std::os::raw::c_longlong, +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!( + "Size of: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).min + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(min) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).max + as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(max) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).step + as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(step) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3 { + pub items: ::std::os::raw::c_uint, + pub item: ::std::os::raw::c_uint, + pub name: [::std::os::raw::c_char; 64usize], + pub names_ptr: __u64, + pub names_length: ::std::os::raw::c_uint, +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3() { + assert_eq!( + ::std::mem::size_of::(), + 88usize, + concat!( + "Size of: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).items + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(items) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).item + as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(item) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).name + as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).names_ptr + as *const _ as usize + }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(names_ptr) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).names_length + as *const _ as usize + }, + 80usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(names_length) + ) + ); +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_info__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 128usize, + concat!("Size of: ", stringify!(snd_ctl_elem_info__bindgen_ty_1)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_ctl_elem_info__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).integer as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1), + "::", + stringify!(integer) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).integer64 as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1), + "::", + stringify!(integer64) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).enumerated as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1), + "::", + stringify!(enumerated) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).reserved as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_1), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_elem_info__bindgen_ty_2 { + pub d: [::std::os::raw::c_ushort; 4usize], + pub d_ptr: *mut ::std::os::raw::c_ushort, + _bindgen_union_align: u64, +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_info__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(snd_ctl_elem_info__bindgen_ty_2)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_ctl_elem_info__bindgen_ty_2)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).d as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_2), + "::", + stringify!(d) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).d_ptr as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info__bindgen_ty_2), + "::", + stringify!(d_ptr) + ) + ); +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_info() { + assert_eq!( + ::std::mem::size_of::(), + 272usize, + concat!("Size of: ", stringify!(snd_ctl_elem_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_ctl_elem_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).type_ as *const _ as usize }, + 64usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).access as *const _ as usize }, + 68usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(access) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).count as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(count) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).owner as *const _ as usize }, + 76usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(owner) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).value as *const _ as usize }, + 80usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(value) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).dimen as *const _ as usize }, + 208usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(dimen) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 216usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_info), + "::", + stringify!(reserved) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_elem_value { + pub id: snd_ctl_elem_id, + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize], u8>, + pub value: snd_ctl_elem_value__bindgen_ty_1, + pub tstamp: timespec, + pub reserved: [::std::os::raw::c_uchar; 112usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_elem_value__bindgen_ty_1 { + pub integer: snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_1, + pub integer64: snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_2, + pub enumerated: snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_3, + pub bytes: snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_4, + pub iec958: snd_aes_iec958, + _bindgen_union_align: [u64; 128usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_1 { + pub value: [::std::os::raw::c_long; 128usize], + pub value_ptr: *mut ::std::os::raw::c_long, + _bindgen_union_align: [u64; 128usize], +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 1024usize, + concat!( + "Size of: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).value + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(value) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).value_ptr + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(value_ptr) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_2 { + pub value: [::std::os::raw::c_longlong; 64usize], + pub value_ptr: *mut ::std::os::raw::c_longlong, + _bindgen_union_align: [u64; 64usize], +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::(), + 512usize, + concat!( + "Size of: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).value + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(value) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).value_ptr + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(value_ptr) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_3 { + pub item: [::std::os::raw::c_uint; 128usize], + pub item_ptr: *mut ::std::os::raw::c_uint, + _bindgen_union_align: [u64; 64usize], +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_3() { + assert_eq!( + ::std::mem::size_of::(), + 512usize, + concat!( + "Size of: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).item + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(item) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).item_ptr + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(item_ptr) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_4 { + pub data: [::std::os::raw::c_uchar; 512usize], + pub data_ptr: *mut ::std::os::raw::c_uchar, + _bindgen_union_align: [u64; 64usize], +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_4() { + assert_eq!( + ::std::mem::size_of::(), + 512usize, + concat!( + "Size of: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_4) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_4) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).data + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_4), + "::", + stringify!(data) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).data_ptr + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1__bindgen_ty_4), + "::", + stringify!(data_ptr) + ) + ); +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_value__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 1024usize, + concat!("Size of: ", stringify!(snd_ctl_elem_value__bindgen_ty_1)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_elem_value__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).integer as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1), + "::", + stringify!(integer) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).integer64 as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1), + "::", + stringify!(integer64) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).enumerated as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1), + "::", + stringify!(enumerated) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).bytes as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1), + "::", + stringify!(bytes) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).iec958 as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value__bindgen_ty_1), + "::", + stringify!(iec958) + ) + ); +} +#[test] +fn bindgen_test_layout_snd_ctl_elem_value() { + assert_eq!( + ::std::mem::size_of::(), + 1224usize, + concat!("Size of: ", stringify!(snd_ctl_elem_value)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(snd_ctl_elem_value)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).value as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value), + "::", + stringify!(value) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tstamp as *const _ as usize }, + 1096usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value), + "::", + stringify!(tstamp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 1112usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_elem_value), + "::", + stringify!(reserved) + ) + ); +} +impl snd_ctl_elem_value { + #[inline] + pub fn indirect(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u32) } + } + #[inline] + pub fn set_indirect(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + indirect: ::std::os::raw::c_uint, + ) -> __BindgenBitfieldUnit<[u8; 1usize], u8> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize], u8> = + Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let indirect: u32 = unsafe { ::std::mem::transmute(indirect) }; + indirect as u64 + }); + __bindgen_bitfield_unit + } +} +#[repr(C)] +#[derive(Debug)] +pub struct snd_ctl_tlv { + pub numid: ::std::os::raw::c_uint, + pub length: ::std::os::raw::c_uint, + pub tlv: __IncompleteArrayField<::std::os::raw::c_uint>, +} +#[test] +fn bindgen_test_layout_snd_ctl_tlv() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(snd_ctl_tlv)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_ctl_tlv)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).numid as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_tlv), + "::", + stringify!(numid) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).length as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_tlv), + "::", + stringify!(length) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tlv as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_tlv), + "::", + stringify!(tlv) + ) + ); +} +pub const sndrv_ctl_event_type_SNDRV_CTL_EVENT_ELEM: sndrv_ctl_event_type = 0; +pub const sndrv_ctl_event_type_SNDRV_CTL_EVENT_LAST: sndrv_ctl_event_type = 0; +pub type sndrv_ctl_event_type = u32; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_event { + pub type_: ::std::os::raw::c_int, + pub data: snd_ctl_event__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_ctl_event__bindgen_ty_1 { + pub elem: snd_ctl_event__bindgen_ty_1__bindgen_ty_1, + pub data8: [::std::os::raw::c_uchar; 60usize], + _bindgen_union_align: [u32; 17usize], +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_ctl_event__bindgen_ty_1__bindgen_ty_1 { + pub mask: ::std::os::raw::c_uint, + pub id: snd_ctl_elem_id, +} +#[test] +fn bindgen_test_layout_snd_ctl_event__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 68usize, + concat!( + "Size of: ", + stringify!(snd_ctl_event__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!( + "Alignment of ", + stringify!(snd_ctl_event__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).mask as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_event__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(mask) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).id as *const _ + as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_event__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(id) + ) + ); +} +#[test] +fn bindgen_test_layout_snd_ctl_event__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::(), + 68usize, + concat!("Size of: ", stringify!(snd_ctl_event__bindgen_ty_1)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_ctl_event__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).elem as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_event__bindgen_ty_1), + "::", + stringify!(elem) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).data8 as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_event__bindgen_ty_1), + "::", + stringify!(data8) + ) + ); +} +#[test] +fn bindgen_test_layout_snd_ctl_event() { + assert_eq!( + ::std::mem::size_of::(), + 72usize, + concat!("Size of: ", stringify!(snd_ctl_event)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(snd_ctl_event)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).type_ as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_event), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).data as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(snd_ctl_event), + "::", + stringify!(data) + ) + ); +} diff --git a/alsa/src/direct/ffi.rs b/alsa/src/direct/ffi.rs new file mode 100644 index 0000000..5e9738c --- /dev/null +++ b/alsa/src/direct/ffi.rs @@ -0,0 +1,79 @@ + +// Some definitions from the kernel headers + +// const SNDRV_PCM_MMAP_OFFSET_DATA: c_uint = 0x00000000; +pub const SNDRV_PCM_MMAP_OFFSET_STATUS: libc::c_uint = 0x80000000; +pub const SNDRV_PCM_MMAP_OFFSET_CONTROL: libc::c_uint = 0x81000000; + + +pub const SNDRV_PCM_SYNC_PTR_HWSYNC: libc::c_uint = 1; +pub const SNDRV_PCM_SYNC_PTR_APPL: libc::c_uint = 2; +pub const SNDRV_PCM_SYNC_PTR_AVAIL_MIN: libc::c_uint = 4; + +// #[repr(C)] +#[allow(non_camel_case_types)] +pub type snd_pcm_state_t = libc::c_int; + +// #[repr(C)] +#[allow(non_camel_case_types)] +pub type snd_pcm_uframes_t = libc::c_ulong; + +// I think?! Not sure how this will work with X32 ABI?! +#[allow(non_camel_case_types)] +pub type __kernel_off_t = libc::c_long; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_pcm_mmap_status { + pub state: snd_pcm_state_t, /* RO: state - SNDRV_PCM_STATE_XXXX */ + pub pad1: libc::c_int, /* Needed for 64 bit alignment */ + pub hw_ptr: snd_pcm_uframes_t, /* RO: hw ptr (0...boundary-1) */ + pub tstamp: libc::timespec, /* Timestamp */ + pub suspended_state: snd_pcm_state_t, /* RO: suspended stream state */ + pub audio_tstamp: libc::timespec, /* from sample counter or wall clock */ +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct snd_pcm_mmap_control { + pub appl_ptr: snd_pcm_uframes_t, /* RW: appl ptr (0...boundary-1) */ + pub avail_min: snd_pcm_uframes_t, /* RW: min available frames for wakeup */ +} + +#[repr(C)] +#[derive(Debug)] +pub struct snd_pcm_channel_info { + pub channel: libc::c_uint, + pub offset: __kernel_off_t, /* mmap offset */ + pub first: libc::c_uint, /* offset to first sample in bits */ + pub step: libc::c_uint, /* samples distance in bits */ +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_pcm_mmap_status_r { + pub status: snd_pcm_mmap_status, + pub reserved: [libc::c_uchar; 64], +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub union snd_pcm_mmap_control_r { + pub control: snd_pcm_mmap_control, + pub reserved: [libc::c_uchar; 64], +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct snd_pcm_sync_ptr { + pub flags: libc::c_uint, + pub s: snd_pcm_mmap_status_r, + pub c: snd_pcm_mmap_control_r, +} + +ioctl_read!(sndrv_pcm_ioctl_channel_info, b'A', 0x32, snd_pcm_channel_info); +ioctl_readwrite!(sndrv_pcm_ioctl_sync_ptr, b'A', 0x23, snd_pcm_sync_ptr); + +pub fn pagesize() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} diff --git a/alsa/src/direct/pcm.rs b/alsa/src/direct/pcm.rs new file mode 100644 index 0000000..ccea666 --- /dev/null +++ b/alsa/src/direct/pcm.rs @@ -0,0 +1,630 @@ +/*! +This module bypasses alsa-lib and directly read and write into memory mapped kernel memory. +In case of the sample memory, this is in many cases the DMA buffers that is transferred to the sound card. + +The reasons for doing this are: + + * Minimum overhead where it matters most: let alsa-lib do the code heavy setup - + then steal its file descriptor and deal with sample streaming from Rust. + * RT-safety to the maximum extent possible. Creating/dropping any of these structs causes syscalls, + but function calls on these are just read and write from memory. No syscalls, no memory allocations, + not even loops (with the exception of `MmapPlayback::write` that loops over samples to write). + * Possibility to allow Send + Sync for structs + * It's a fun experiment and an interesting deep dive into how alsa-lib does things. + +Note: Not all sound card drivers support this direct method of communication; although almost all +modern/common ones do. It only works with hardware devices though (such as "hw:xxx" device strings), +don't expect it to work with, e g, the PulseAudio plugin or so. + +For an example of how to use this mode, look in the "synth-example" directory. +*/ + +use libc; +use std::{mem, ptr, fmt, cmp}; +use crate::error::{Error, Result}; +use std::os::unix::io::RawFd; +use crate::{pcm, PollDescriptors, Direction}; +use crate::pcm::Frames; +use std::marker::PhantomData; + +use super::ffi::*; + +/// Read PCM status via a simple kernel syscall, bypassing alsa-lib. +/// +/// If Status is not available on your architecture, this is the second best option. +pub struct SyncPtrStatus(snd_pcm_mmap_status); + +impl SyncPtrStatus { + /// Executes sync_ptr syscall. + /// + /// Unsafe because + /// - setting appl_ptr and avail_min might make alsa-lib confused + /// - no check that the fd is really a PCM + pub unsafe fn sync_ptr(fd: RawFd, hwsync: bool, appl_ptr: Option, avail_min: Option) -> Result { + let mut data = snd_pcm_sync_ptr { + flags: (if hwsync { SNDRV_PCM_SYNC_PTR_HWSYNC } else { 0 }) + + (if appl_ptr.is_some() { SNDRV_PCM_SYNC_PTR_APPL } else { 0 }) + + (if avail_min.is_some() { SNDRV_PCM_SYNC_PTR_AVAIL_MIN } else { 0 }), + c: snd_pcm_mmap_control_r { + control: snd_pcm_mmap_control { + appl_ptr: appl_ptr.unwrap_or(0) as snd_pcm_uframes_t, + avail_min: avail_min.unwrap_or(0) as snd_pcm_uframes_t, + } + }, + s: mem::zeroed() + }; + + sndrv_pcm_ioctl_sync_ptr(fd, &mut data).map_err(|_| + Error::new("SNDRV_PCM_IOCTL_SYNC_PTR", nix::errno::Errno::last() as i32))?; + + let i = data.s.status.state; + if (i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)) { + Ok(SyncPtrStatus(data.s.status)) + } else { + Err(Error::unsupported("SNDRV_PCM_IOCTL_SYNC_PTR returned broken state")) + } + } + + pub fn hw_ptr(&self) -> pcm::Frames { self.0.hw_ptr as pcm::Frames } + pub fn state(&self) -> pcm::State { unsafe { mem::transmute(self.0.state as u8) } /* valid range checked in sync_ptr */ } + pub fn htstamp(&self) -> libc::timespec { self.0.tstamp } +} + + + +/// Read PCM status directly from memory, bypassing alsa-lib. +/// +/// This means that it's +/// 1) less overhead for reading status (no syscall, no allocations, no virtual dispatch, just a read from memory) +/// 2) Send + Sync, and +/// 3) will only work for "hw" / "plughw" devices (not e g PulseAudio plugins), and not +/// all of those are supported, although all common ones are (as of 2017, and a kernel from the same decade). +/// Kernel supported archs are: x86, PowerPC, Alpha. Use "SyncPtrStatus" for other archs. +/// +/// The values are updated every now and then by the kernel. Many functions will force an update to happen, +/// e g `PCM::avail()` and `PCM::delay()`. +/// +/// Note: Even if you close the original PCM device, ALSA will not actually close the device until all +/// Status structs are dropped too. +/// +#[derive(Debug)] +pub struct Status(DriverMemory); + +fn pcm_to_fd(p: &pcm::PCM) -> Result { + let mut fds: [libc::pollfd; 1] = unsafe { mem::zeroed() }; + let c = PollDescriptors::fill(p, &mut fds)?; + if c != 1 { + return Err(Error::unsupported("snd_pcm_poll_descriptors returned wrong number of fds")) + } + Ok(fds[0].fd) +} + +impl Status { + pub fn new(p: &pcm::PCM) -> Result { Status::from_fd(pcm_to_fd(p)?) } + + pub fn from_fd(fd: RawFd) -> Result { + DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_STATUS as libc::off_t, false).map(Status) + } + + /// Current PCM state. + pub fn state(&self) -> pcm::State { + unsafe { + let i = ptr::read_volatile(&(*self.0.ptr).state); + assert!((i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t))); + mem::transmute(i as u8) + } + } + + /// Number of frames hardware has read or written + /// + /// This number is updated every now and then by the kernel. + /// Calling most functions on the PCM will update it, so will usually a period interrupt. + /// No guarantees given. + /// + /// This value wraps at "boundary" (a large value you can read from SwParams). + pub fn hw_ptr(&self) -> pcm::Frames { + unsafe { + ptr::read_volatile(&(*self.0.ptr).hw_ptr) as pcm::Frames + } + } + + /// Timestamp - fast version of alsa-lib's Status::get_htstamp + /// + /// Note: This just reads the actual value in memory. + /// Unfortunately, the timespec is too big to be read atomically on most archs. + /// Therefore, this function can potentially give bogus result at times, at least in theory...? + pub fn htstamp(&self) -> libc::timespec { + unsafe { + ptr::read_volatile(&(*self.0.ptr).tstamp) + } + } + + /// Audio timestamp - fast version of alsa-lib's Status::get_audio_htstamp + /// + /// Note: This just reads the actual value in memory. + /// Unfortunately, the timespec is too big to be read atomically on most archs. + /// Therefore, this function can potentially give bogus result at times, at least in theory...? + pub fn audio_htstamp(&self) -> libc::timespec { + unsafe { + ptr::read_volatile(&(*self.0.ptr).audio_tstamp) + } + } +} + +/// Write PCM appl ptr directly, bypassing alsa-lib. +/// +/// Provides direct access to appl ptr and avail min, without the overhead of +/// alsa-lib or a syscall. Caveats that apply to Status applies to this struct too. +#[derive(Debug)] +pub struct Control(DriverMemory); + +impl Control { + pub fn new(p: &pcm::PCM) -> Result { Self::from_fd(pcm_to_fd(p)?) } + + pub fn from_fd(fd: RawFd) -> Result { + DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_CONTROL as libc::off_t, true).map(Control) + } + + /// Read number of frames application has read or written + /// + /// This value wraps at "boundary" (a large value you can read from SwParams). + pub fn appl_ptr(&self) -> pcm::Frames { + unsafe { + ptr::read_volatile(&(*self.0.ptr).appl_ptr) as pcm::Frames + } + } + + /// Set number of frames application has read or written + /// + /// When the kernel wakes up due to a period interrupt, this value will + /// be checked by the kernel. An XRUN will happen in case the application + /// has not read or written enough data. + pub fn set_appl_ptr(&self, value: pcm::Frames) { + unsafe { + ptr::write_volatile(&mut (*self.0.ptr).appl_ptr, value as snd_pcm_uframes_t) + } + } + + /// Read minimum number of frames in buffer in order to wakeup process + pub fn avail_min(&self) -> pcm::Frames { + unsafe { + ptr::read_volatile(&(*self.0.ptr).avail_min) as pcm::Frames + } + } + + /// Write minimum number of frames in buffer in order to wakeup process + pub fn set_avail_min(&self, value: pcm::Frames) { + unsafe { + ptr::write_volatile(&mut (*self.0.ptr).avail_min, value as snd_pcm_uframes_t) + } + } +} + +struct DriverMemory { + ptr: *mut S, + size: libc::size_t, +} + +impl fmt::Debug for DriverMemory { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DriverMemory({:?})", self.ptr) } +} + +impl DriverMemory { + fn new(fd: RawFd, count: usize, offs: libc::off_t, writable: bool) -> Result { + let mut total = count * mem::size_of::(); + let ps = pagesize(); + assert!(total > 0); + if total % ps != 0 { total += ps - total % ps }; + let flags = if writable { libc::PROT_WRITE | libc::PROT_READ } else { libc::PROT_READ }; + let p = unsafe { libc::mmap(ptr::null_mut(), total, flags, libc::MAP_FILE | libc::MAP_SHARED, fd, offs) }; + if p.is_null() || p == libc::MAP_FAILED { + Err(Error::new("mmap (of driver memory)", nix::errno::Errno::last() as i32)) + } else { + Ok(DriverMemory { ptr: p as *mut S, size: total }) + } + } +} + +unsafe impl Send for DriverMemory {} +unsafe impl Sync for DriverMemory {} + +impl Drop for DriverMemory { + fn drop(&mut self) { + unsafe {{ libc::munmap(self.ptr as *mut libc::c_void, self.size); } } + } +} + +#[derive(Debug)] +struct SampleData { + mem: DriverMemory, + frames: pcm::Frames, + channels: u32, +} + +impl SampleData { + pub fn new(p: &pcm::PCM) -> Result { + let params = p.hw_params_current()?; + let bufsize = params.get_buffer_size()?; + let channels = params.get_channels()?; + if params.get_access()? != pcm::Access::MMapInterleaved { + return Err(Error::unsupported("Not MMAP interleaved data")) + } + + let fd = pcm_to_fd(p)?; + let info = unsafe { + let mut info: snd_pcm_channel_info = mem::zeroed(); + sndrv_pcm_ioctl_channel_info(fd, &mut info).map_err(|_| + Error::new("SNDRV_PCM_IOCTL_CHANNEL_INFO", nix::errno::Errno::last() as i32))?; + info + }; + // println!("{:?}", info); + if (info.step != channels * mem::size_of::() as u32 * 8) || (info.first != 0) { + return Err(Error::unsupported("MMAP data size mismatch")) + } + Ok(SampleData { + mem: DriverMemory::new(fd, (bufsize as usize) * (channels as usize), info.offset as libc::off_t, true)?, + frames: bufsize, + channels, + }) + } +} + + +/// Dummy trait for better generics +pub trait MmapDir: fmt::Debug { + const DIR: Direction; + fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames; +} + +/// Dummy struct for better generics +#[derive(Copy, Clone, Debug)] +pub struct Playback; + +impl MmapDir for Playback { + const DIR: Direction = Direction::Playback; + #[inline] + fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames { + let r = hwptr.wrapping_add(buffersize).wrapping_sub(applptr); + let r = if r < 0 { r.wrapping_add(boundary) } else { r }; + if r as usize >= boundary as usize { r.wrapping_sub(boundary) } else { r } + } +} + +/// Dummy struct for better generics +#[derive(Copy, Clone, Debug)] +pub struct Capture; + +impl MmapDir for Capture { + const DIR: Direction = Direction::Capture; + #[inline] + fn avail(hwptr: Frames, applptr: Frames, _buffersize: Frames, boundary: Frames) -> Frames { + let r = hwptr.wrapping_sub(applptr); + if r < 0 { r.wrapping_add(boundary) } else { r } + } +} + +pub type MmapPlayback = MmapIO; + +pub type MmapCapture = MmapIO; + +#[derive(Debug)] +/// Struct containing direct I/O functions shared between playback and capture. +pub struct MmapIO { + data: SampleData, + c: Control, + ss: Status, + bound: Frames, + dir: PhantomData<*const D>, +} + +#[derive(Debug, Clone, Copy)] +/// A raw pointer to samples, and the amount of samples readable or writable. +pub struct RawSamples { + pub ptr: *mut S, + pub frames: Frames, + pub channels: u32, +} + +impl RawSamples { + #[inline] + /// Returns `frames` * `channels`, i e the amount of samples (of type `S`) that can be read/written. + pub fn samples(&self) -> isize { self.frames as isize * (self.channels as isize) } + + /// Writes samples from an iterator. + /// + /// Returns true if iterator was depleted, and the number of samples written. + /// This is just raw read/write of memory. + pub unsafe fn write_samples>(&self, i: &mut I) -> (bool, isize) { + let mut z = 0; + let max_samples = self.samples(); + while z < max_samples { + let b = if let Some(b) = i.next() { b } else { return (true, z) }; + ptr::write_volatile(self.ptr.offset(z), b); + z += 1; + }; + (false, z) + } + +} + +impl MmapIO { + fn new(p: &pcm::PCM) -> Result { + if p.info()?.get_stream() != D::DIR { + return Err(Error::unsupported("Wrong direction")); + } + let boundary = p.sw_params_current()?.get_boundary()?; + Ok(MmapIO { + data: SampleData::new(p)?, + c: Control::new(p)?, + ss: Status::new(p)?, + bound: boundary, + dir: PhantomData, + }) + } +} + +pub (crate) fn new_mmap(p: &pcm::PCM) -> Result> { MmapIO::new(p) } + +impl MmapIO { + /// Read current status + pub fn status(&self) -> &Status { &self.ss } + + /// Read current number of frames committed by application + /// + /// This number wraps at 'boundary'. + #[inline] + pub fn appl_ptr(&self) -> Frames { self.c.appl_ptr() } + + /// Read current number of frames read / written by hardware + /// + /// This number wraps at 'boundary'. + #[inline] + pub fn hw_ptr(&self) -> Frames { self.ss.hw_ptr() } + + /// The number at which hw_ptr and appl_ptr wraps. + #[inline] + pub fn boundary(&self) -> Frames { self.bound } + + /// Total number of frames in hardware buffer + #[inline] + pub fn buffer_size(&self) -> Frames { self.data.frames } + + /// Number of channels in stream + #[inline] + pub fn channels(&self) -> u32 { self.data.channels } + + /// Notifies the kernel that frames have now been read / written by the application + /// + /// This will allow the kernel to write new data into this part of the buffer. + pub fn commit(&self, v: Frames) { + let mut z = self.appl_ptr() + v; + if z + v >= self.boundary() { z -= self.boundary() }; + self.c.set_appl_ptr(z) + } + + /// Number of frames available to read / write. + /// + /// In case of an underrun, this value might be bigger than the buffer size. + pub fn avail(&self) -> Frames { D::avail(self.hw_ptr(), self.appl_ptr(), self.buffer_size(), self.boundary()) } + + /// Returns raw pointers to data to read / write. + /// + /// Use this if you want to read/write data yourself (instead of using iterators). If you do, + /// using `write_volatile` or `read_volatile` is recommended, since it's DMA memory and can + /// change at any time. + /// + /// Since this is a ring buffer, there might be more data to read/write in the beginning + /// of the buffer as well. If so this is returned as the second return value. + pub fn data_ptr(&self) -> (RawSamples, Option>) { + let (hwptr, applptr) = (self.hw_ptr(), self.appl_ptr()); + let c = self.channels(); + let bufsize = self.buffer_size(); + + // These formulas mostly mimic the behaviour of + // snd_pcm_mmap_begin (in alsa-lib/src/pcm/pcm.c). + let offs = applptr % bufsize; + let mut a = D::avail(hwptr, applptr, bufsize, self.boundary()); + a = cmp::min(a, bufsize); + let b = bufsize - offs; + let more_data = if b < a { + let z = a - b; + a = b; + Some( RawSamples { ptr: self.data.mem.ptr, frames: z, channels: c }) + } else { None }; + + let p = unsafe { self.data.mem.ptr.offset(offs as isize * self.data.channels as isize) }; + (RawSamples { ptr: p, frames: a, channels: c }, more_data) + } +} + +impl MmapPlayback { + /// Write samples to the kernel ringbuffer. + pub fn write>(&mut self, i: &mut I) -> Frames { + let (data, more_data) = self.data_ptr(); + let (iter_end, samples) = unsafe { data.write_samples(i) }; + let mut z = samples / data.channels as isize; + if !iter_end { + if let Some(data2) = more_data { + let (_, samples2) = unsafe { data2.write_samples(i) }; + z += samples2 / data2.channels as isize; + } + } + let z = z as Frames; + self.commit(z); + z + } +} + +impl MmapCapture { + /// Read samples from the kernel ringbuffer. + /// + /// When the iterator is dropped or depleted, the read samples will be committed, i e, + /// the kernel can then write data to the location again. So do this ASAP. + pub fn iter(&mut self) -> CaptureIter { + let (data, more_data) = self.data_ptr(); + CaptureIter { + m: self, + samples: data, + p_offs: 0, + read_samples: 0, + next_p: more_data, + } + } +} + +/// Iterator over captured samples +pub struct CaptureIter<'a, S: 'static> { + m: &'a MmapCapture, + samples: RawSamples, + p_offs: isize, + read_samples: isize, + next_p: Option>, +} + +impl<'a, S: 'static + Copy> CaptureIter<'a, S> { + fn handle_max(&mut self) { + self.p_offs = 0; + if let Some(p2) = self.next_p.take() { + self.samples = p2; + } else { + self.m.commit((self.read_samples / self.samples.channels as isize) as Frames); + self.read_samples = 0; + self.samples.frames = 0; // Shortcut to "None" in case anyone calls us again + } + } +} + +impl<'a, S: 'static + Copy> Iterator for CaptureIter<'a, S> { + type Item = S; + + #[inline] + fn next(&mut self) -> Option { + if self.p_offs >= self.samples.samples() { + self.handle_max(); + if self.samples.frames <= 0 { return None; } + } + let s = unsafe { ptr::read_volatile(self.samples.ptr.offset(self.p_offs)) }; + self.p_offs += 1; + self.read_samples += 1; + Some(s) + } +} + +impl<'a, S: 'static> Drop for CaptureIter<'a, S> { + fn drop(&mut self) { + self.m.commit((self.read_samples / self.m.data.channels as isize) as Frames); + } +} + + +#[test] +#[ignore] // Not everyone has a recording device on plughw:1. So let's ignore this test by default. +fn record_from_plughw_rw() { + use crate::pcm::*; + use crate::{ValueOr, Direction}; + use std::ffi::CString; + let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap(); + let ss = self::Status::new(&pcm).unwrap(); + let c = self::Control::new(&pcm).unwrap(); + let hwp = HwParams::any(&pcm).unwrap(); + hwp.set_channels(2).unwrap(); + hwp.set_rate(44100, ValueOr::Nearest).unwrap(); + hwp.set_format(Format::s16()).unwrap(); + hwp.set_access(Access::RWInterleaved).unwrap(); + pcm.hw_params(&hwp).unwrap(); + + { + let swp = pcm.sw_params_current().unwrap(); + swp.set_tstamp_mode(true).unwrap(); + pcm.sw_params(&swp).unwrap(); + } + assert_eq!(ss.state(), State::Prepared); + pcm.start().unwrap(); + assert_eq!(c.appl_ptr(), 0); + println!("{:?}, {:?}", ss, c); + let mut buf = [0i16; 512*2]; + assert_eq!(pcm.io_i16().unwrap().readi(&mut buf).unwrap(), 512); + assert_eq!(c.appl_ptr(), 512); + + assert_eq!(ss.state(), State::Running); + assert!(ss.hw_ptr() >= 512); + let t2 = ss.htstamp(); + assert!(t2.tv_sec > 0 || t2.tv_nsec > 0); +} + + +#[test] +#[ignore] // Not everyone has a record device on plughw:1. So let's ignore this test by default. +fn record_from_plughw_mmap() { + use crate::pcm::*; + use crate::{ValueOr, Direction}; + use std::ffi::CString; + use std::{thread, time}; + + let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap(); + let hwp = HwParams::any(&pcm).unwrap(); + hwp.set_channels(2).unwrap(); + hwp.set_rate(44100, ValueOr::Nearest).unwrap(); + hwp.set_format(Format::s16()).unwrap(); + hwp.set_access(Access::MMapInterleaved).unwrap(); + pcm.hw_params(&hwp).unwrap(); + + let ss = unsafe { SyncPtrStatus::sync_ptr(pcm_to_fd(&pcm).unwrap(), false, None, None).unwrap() }; + assert_eq!(ss.state(), State::Prepared); + + let mut m = pcm.direct_mmap_capture::().unwrap(); + + assert_eq!(m.status().state(), State::Prepared); + assert_eq!(m.appl_ptr(), 0); + assert_eq!(m.hw_ptr(), 0); + + + println!("{:?}", m); + + let now = time::Instant::now(); + pcm.start().unwrap(); + while m.avail() < 256 { thread::sleep(time::Duration::from_millis(1)) }; + assert!(now.elapsed() >= time::Duration::from_millis(256 * 1000 / 44100)); + let (ptr1, md) = m.data_ptr(); + assert_eq!(ptr1.channels, 2); + assert!(ptr1.frames >= 256); + assert!(md.is_none()); + println!("Has {:?} frames at {:?} in {:?}", m.avail(), ptr1.ptr, now.elapsed()); + let samples: Vec = m.iter().collect(); + assert!(samples.len() >= ptr1.frames as usize * 2); + println!("Collected {} samples", samples.len()); + let (ptr2, _md) = m.data_ptr(); + assert!(unsafe { ptr1.ptr.offset(256 * 2) } <= ptr2.ptr); +} + +#[test] +#[ignore] +fn playback_to_plughw_mmap() { + use crate::pcm::*; + use crate::{ValueOr, Direction}; + use std::ffi::CString; + + let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Playback, false).unwrap(); + let hwp = HwParams::any(&pcm).unwrap(); + hwp.set_channels(2).unwrap(); + hwp.set_rate(44100, ValueOr::Nearest).unwrap(); + hwp.set_format(Format::s16()).unwrap(); + hwp.set_access(Access::MMapInterleaved).unwrap(); + pcm.hw_params(&hwp).unwrap(); + let mut m = pcm.direct_mmap_playback::().unwrap(); + + assert_eq!(m.status().state(), State::Prepared); + assert_eq!(m.appl_ptr(), 0); + assert_eq!(m.hw_ptr(), 0); + + println!("{:?}", m); + let mut i = (0..(m.buffer_size() * 2)).map(|i| + (((i / 2) as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16); + m.write(&mut i); + assert_eq!(m.appl_ptr(), m.buffer_size()); + + pcm.start().unwrap(); + pcm.drain().unwrap(); + assert_eq!(m.appl_ptr(), m.buffer_size()); + assert!(m.hw_ptr() >= m.buffer_size()); +} diff --git a/alsa/src/error.rs b/alsa/src/error.rs new file mode 100644 index 0000000..02debb8 --- /dev/null +++ b/alsa/src/error.rs @@ -0,0 +1,100 @@ +#![macro_use] + +use libc::{c_void, c_int, c_char, free}; +use std::{fmt, str}; +use std::ffi::CStr; +use std::error::Error as StdError; + +/// ALSA error +/// +/// Most ALSA functions can return a negative error code. +/// If so, then that error code is wrapped into this `Error` struct. +/// An Error is also returned in case ALSA returns a string that +/// cannot be translated into Rust's UTF-8 strings. +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct Error(&'static str, nix::Error); + +pub type Result = ::std::result::Result; + +macro_rules! acheck { + ($f: ident ( $($x: expr),* ) ) => {{ + let r = unsafe { alsa::$f( $($x),* ) }; + if r < 0 { Err(Error::new(stringify!($f), -r as ::libc::c_int)) } + else { Ok(r) } + }} +} + +pub fn from_const<'a>(func: &'static str, s: *const c_char) -> Result<&'a str> { + if s.is_null() { return Err(invalid_str(func)) }; + let cc = unsafe { CStr::from_ptr(s) }; + str::from_utf8(cc.to_bytes()).map_err(|_| invalid_str(func)) +} + +pub fn from_alloc(func: &'static str, s: *mut c_char) -> Result { + if s.is_null() { return Err(invalid_str(func)) }; + let c = unsafe { CStr::from_ptr(s) }; + let ss = str::from_utf8(c.to_bytes()).map_err(|_| { + unsafe { free(s as *mut c_void); } + invalid_str(func) + })?.to_string(); + unsafe { free(s as *mut c_void); } + Ok(ss) +} + +pub fn from_code(func: &'static str, r: c_int) -> Result { + if r < 0 { Err(Error::new(func, r)) } + else { Ok(r) } +} + +impl Error { + pub fn new(func: &'static str, res: c_int) -> Error { + let errno = nix::errno::Errno::from_i32(res as i32); + Error(func, errno) + } + + pub fn unsupported(func: &'static str) -> Error { + Error(func, nix::Error::ENOTSUP) + } + + /// The function which failed. + pub fn func(&self) -> &'static str { self.0 } + + + /// Underlying error + /// + /// Match this against the re-export of `nix::Error` in this crate, not against a specific version + /// of the nix crate. The nix crate version might be updated with minor updates of this library. + pub fn errno(&self) -> nix::Error { self.1 } + + /// Underlying error + /// + /// Match this against the re-export of `nix::Error` in this crate, not against a specific version + /// of the nix crate. The nix crate version might be updated with minor updates of this library. + pub fn nix_error(&self) -> nix::Error { self.1 } +} + +pub fn invalid_str(func: &'static str) -> Error { Error(func, nix::Error::EILSEQ) } + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.1) } + fn description(&self) -> &str { "ALSA error" } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ALSA function '{}' failed with error '{}'", self.0, self.1) + } +} + +impl From for fmt::Error { + fn from(_: Error) -> fmt::Error { fmt::Error } +} + + +#[test] +fn broken_pcm_name() { + use std::ffi::CString; + let e = crate::PCM::open(&*CString::new("this_PCM_does_not_exist").unwrap(), crate::Direction::Playback, false).err().unwrap(); + assert_eq!(e.func(), "snd_pcm_open"); + assert_eq!(e.errno(), nix::errno::Errno::ENOENT); +} diff --git a/alsa/src/hctl.rs b/alsa/src/hctl.rs new file mode 100644 index 0000000..b6f5aad --- /dev/null +++ b/alsa/src/hctl.rs @@ -0,0 +1,162 @@ +//! HCtl API - for mixer control and jack detection +//! +//! # Example +//! Print all jacks and their status +//! +//! ``` +//! for a in ::alsa::card::Iter::new().map(|x| x.unwrap()) { +//! use std::ffi::CString; +//! use alsa::hctl::HCtl; +//! let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap(); +//! h.load().unwrap(); +//! for b in h.elem_iter() { +//! use alsa::ctl::ElemIface; +//! let id = b.get_id().unwrap(); +//! if id.get_interface() != ElemIface::Card { continue; } +//! let name = id.get_name().unwrap(); +//! if !name.ends_with(" Jack") { continue; } +//! if name.ends_with(" Phantom Jack") { +//! println!("{} is always present", &name[..name.len()-13]) +//! } +//! else { println!("{} is {}", &name[..name.len()-5], +//! if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" }) +//! } +//! } +//! } +//! ``` + +use crate::alsa; +use std::ffi::{CStr, CString}; +use super::error::*; +use std::ptr; +use super::{ctl_int, poll}; +use libc::{c_short, c_uint, c_int, pollfd}; + + +/// [snd_hctl_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper +pub struct HCtl(*mut alsa::snd_hctl_t); + +unsafe impl Send for HCtl {} + +impl Drop for HCtl { + fn drop(&mut self) { unsafe { alsa::snd_hctl_close(self.0) }; } +} + +impl HCtl { + /// Wrapper around open that takes a &str instead of a &CStr + pub fn new(c: &str, nonblock: bool) -> Result { + Self::open(&CString::new(c).unwrap(), nonblock) + } + + /// Open does not support async mode (it's not very Rustic anyway) + /// Note: You probably want to call `load` afterwards. + pub fn open(c: &CStr, nonblock: bool) -> Result { + let mut r = ptr::null_mut(); + let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys + acheck!(snd_hctl_open(&mut r, c.as_ptr(), flags)) + .map(|_| HCtl(r)) + } + + pub fn load(&self) -> Result<()> { acheck!(snd_hctl_load(self.0)).map(|_| ()) } + + pub fn elem_iter(&self) -> ElemIter { ElemIter(self, ptr::null_mut()) } + + pub fn find_elem(&self, id: &ctl_int::ElemId) -> Option { + let p = unsafe { alsa::snd_hctl_find_elem(self.0, ctl_int::elem_id_ptr(id)) }; + if p.is_null() { None } else { Some(Elem(self, p)) } + } + + pub fn handle_events(&self) -> Result { + acheck!(snd_hctl_handle_events(self.0)).map(|x| x as u32) + } + + pub fn wait(&self, timeout_ms: Option) -> Result { + acheck!(snd_hctl_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) } +} + +impl poll::Descriptors for HCtl { + fn count(&self) -> usize { + unsafe { alsa::snd_hctl_poll_descriptors_count(self.0) as usize } + } + fn fill(&self, p: &mut [pollfd]) -> Result { + let z = unsafe { alsa::snd_hctl_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) }; + from_code("snd_hctl_poll_descriptors", z).map(|_| z as usize) + } + fn revents(&self, p: &[pollfd]) -> Result { + let mut r = 0; + let z = unsafe { alsa::snd_hctl_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) }; + from_code("snd_hctl_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short)) + } +} + +/// Iterates over elements for a `HCtl` +pub struct ElemIter<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t); + +impl<'a> Iterator for ElemIter<'a> { + type Item = Elem<'a>; + fn next(&mut self) -> Option> { + self.1 = if self.1.is_null() { unsafe { alsa::snd_hctl_first_elem((self.0).0) }} + else { unsafe { alsa::snd_hctl_elem_next(self.1) }}; + if self.1.is_null() { None } + else { Some(Elem(self.0, self.1)) } + } +} + + +/// [snd_hctl_elem_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper +pub struct Elem<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t); + +impl<'a> Elem<'a> { + pub fn get_id(&self) -> Result { + let v = ctl_int::elem_id_new()?; + unsafe { alsa::snd_hctl_elem_get_id(self.1, ctl_int::elem_id_ptr(&v)) }; + Ok(v) + } + pub fn info(&self) -> Result { + let v = ctl_int::elem_info_new()?; + acheck!(snd_hctl_elem_info(self.1, ctl_int::elem_info_ptr(&v))).map(|_| v) + } + pub fn read(&self) -> Result { + let i = self.info()?; + let v = ctl_int::elem_value_new(i.get_type(), i.get_count())?; + acheck!(snd_hctl_elem_read(self.1, ctl_int::elem_value_ptr(&v))).map(|_| v) + } + + pub fn write(&self, v: &ctl_int::ElemValue) -> Result { + acheck!(snd_hctl_elem_write(self.1, ctl_int::elem_value_ptr(v))).map(|e| e > 0) + } +} + +#[test] +fn print_hctls() { + for a in super::card::Iter::new().map(|x| x.unwrap()) { + use std::ffi::CString; + let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap(); + h.load().unwrap(); + println!("Card {}:", a.get_name().unwrap()); + for b in h.elem_iter() { + println!(" {:?} - {:?}", b.get_id().unwrap(), b.read().unwrap()); + } + } +} + +#[test] +fn print_jacks() { + for a in super::card::Iter::new().map(|x| x.unwrap()) { + use std::ffi::CString; + let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap(); + h.load().unwrap(); + for b in h.elem_iter() { + let id = b.get_id().unwrap(); + if id.get_interface() != super::ctl_int::ElemIface::Card { continue; } + let name = id.get_name().unwrap(); + if !name.ends_with(" Jack") { continue; } + if name.ends_with(" Phantom Jack") { + println!("{} is always present", &name[..name.len()-13]) + } + else { println!("{} is {}", &name[..name.len()-5], + if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" }) + } + } + } +} diff --git a/alsa/src/io.rs b/alsa/src/io.rs new file mode 100644 index 0000000..f6c3739 --- /dev/null +++ b/alsa/src/io.rs @@ -0,0 +1,49 @@ +use crate::alsa; +use super::error::*; +use std::{slice, ptr, fmt}; + +/// [snd_output_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___output.html) wrapper +pub struct Output(*mut alsa::snd_output_t); + +unsafe impl Send for Output {} + +impl Drop for Output { + fn drop(&mut self) { unsafe { alsa::snd_output_close(self.0) }; } +} + +impl Output { + + pub fn buffer_open() -> Result { + let mut q = ptr::null_mut(); + acheck!(snd_output_buffer_open(&mut q)).map(|_| Output(q)) + } + + pub fn buffer_string T>(&self, f: F) -> T { + let b = unsafe { + let mut q = ptr::null_mut(); + let s = alsa::snd_output_buffer_string(self.0, &mut q); + slice::from_raw_parts(q as *const u8, s as usize) + }; + f(b) + } +} + +impl fmt::Debug for Output { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Output(")?; + fmt::Display::fmt(self, f)?; + write!(f, ")") + /* self.buffer_string(|b| f.write_str(try!(str::from_utf8(b).map_err(|_| fmt::Error)))) */ + } +} + +impl fmt::Display for Output { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.buffer_string(|b| { + let s = String::from_utf8_lossy(b); + f.write_str(&*s) + }) + } +} + +pub fn output_handle(o: &Output) -> *mut alsa::snd_output_t { o.0 } diff --git a/alsa/src/lib.rs b/alsa/src/lib.rs new file mode 100644 index 0000000..ce83ec4 --- /dev/null +++ b/alsa/src/lib.rs @@ -0,0 +1,140 @@ +//! Thin but safe wrappers for [ALSA](https://alsa-project.org). +//! +//! [GitHub repo](https://github.com/diwic/alsa-rs) +//! +//! [Crates.io](https://crates.io/crates/alsa) +//! +//! This ALSA API wrapper/binding is WIP - the ALSA API is huge, and new +//! functions and structs might be added as requested. +//! +//! Most functions map 1-to-1 to alsa-lib functions, e g, `ctl::CardInfo::get_id()` is a wrapper around +//! `snd_ctl_card_info_get_id` and the [alsa-lib documentation](https://www.alsa-project.org/alsa-doc/alsa-lib/) +//! can be consulted for additional information. +//! +//! Enjoy! + +#![allow(clippy::all)] +#![warn(clippy::correctness, clippy::suspicious, clippy::perf)] + +extern crate alsa_sys as alsa; +extern crate libc; +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate nix as nix_the_crate; + +macro_rules! alsa_enum { + ($(#[$attr:meta])+ $name:ident, $static_name:ident [$count:expr], $( $a:ident = $b:ident),* ,) => +{ +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +$(#[$attr])* +pub enum $name { +$( + $a = alsa::$b as isize, +)* +} + +static $static_name: [$name; $count] = + [ $( $name::$a, )* ]; + +impl $name { + /// Returns a slice of all possible values; useful for iteration + pub fn all() -> &'static [$name] { &$static_name[..] } + + #[allow(dead_code)] + fn from_c_int(c: ::libc::c_int, s: &'static str) -> Result<$name> { + Self::all().iter().find(|&&x| c == x as ::libc::c_int).map(|&x| x) + .ok_or_else(|| Error::unsupported(s)) + } +} + +} +} + +/// Replaces constants ending with PLAYBACK/CAPTURE as well as +/// INPUT/OUTPUT +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Direction { + Playback, + Capture +} +impl Direction { + #[inline] + pub fn input() -> Direction { Direction::Capture } + #[inline] + pub fn output() -> Direction { Direction::Playback } +} + +/// Used to restrict hw parameters. In case the submitted +/// value is unavailable, in which direction should one search +/// for available values? +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum ValueOr { + /// The value set is the submitted value, or less + Less = -1, + /// The value set is the submitted value, or the nearest + Nearest = 0, + /// The value set is the submitted value, or greater + Greater = 1, +} + +/// Rounding mode (used in some mixer related calls) +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Round { + /// Round down (towards negative infinity) + Floor = 0, + /// Round up (towards positive infinity) + Ceil = 1, +} + +mod error; +pub use crate::error::{Error, Result}; + +pub mod card; +pub use crate::card::Card as Card; + +mod ctl_int; +pub mod ctl { + //! Control device API + pub use super::ctl_int::{Ctl, CardInfo, ElemIface, ElemId, ElemType, ElemValue, ElemInfo}; +} + +pub use crate::ctl::Ctl as Ctl; + +pub mod hctl; +pub use crate::hctl::HCtl as HCtl; + +pub mod pcm; +pub use crate::pcm::PCM as PCM; + +pub mod rawmidi; +pub use crate::rawmidi::Rawmidi as Rawmidi; + +pub mod device_name; + +pub mod poll; +pub use crate::poll::Descriptors as PollDescriptors; + +pub mod mixer; +pub use crate::mixer::Mixer as Mixer; + +pub mod seq; +pub use crate::seq::Seq as Seq; + +mod io; +pub use crate::io::Output; + +// Reexported inside PCM module +mod chmap; + +pub mod direct; + +/// Re-exports from the nix crate. +/// +/// Use these re-exports instead of also depending on the nix crate. There +/// is no guarantee that these will match a specific nix version, it may +/// change between minor updates of the library. +pub mod nix { + pub use nix_the_crate::Error; + pub use nix_the_crate::errno; +} diff --git a/alsa/src/mixer.rs b/alsa/src/mixer.rs new file mode 100644 index 0000000..d925f09 --- /dev/null +++ b/alsa/src/mixer.rs @@ -0,0 +1,639 @@ +//! Mixer API - Simple Mixer API for mixer control +//! +use std::ffi::{CStr, CString}; +use std::{ptr, mem, fmt, ops}; +use libc::{c_long, c_int, c_uint, c_short, pollfd}; +use crate::poll; + +use crate::alsa; +use super::Round; +use super::error::*; + +const SELEM_ID_SIZE: usize = 64; + +/// wraps [snd_mixer_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___mixer.html) +#[derive(Debug)] +pub struct Mixer(*mut alsa::snd_mixer_t); + +unsafe impl Send for Mixer {} + +impl Mixer { + /// Opens a mixer and attaches it to a card identified by its name (like hw:0) and loads the + /// mixer after registering a Selem. + pub fn new(name: &str, nonblock: bool) -> Result { + let mut mixer = Mixer::open(nonblock)?; + mixer.attach(&CString::new(name).unwrap())?; + Selem::register(&mut mixer)?; + mixer.load()?; + Ok(mixer) + } + + /// Creates a Selem by looking for a specific selem by name given a mixer (of a card) + pub fn find_selem(&self, id: &SelemId) -> Option { + let selem = unsafe { alsa::snd_mixer_find_selem(self.0, id.as_ptr()) }; + + if selem.is_null() { None } + else { Some(Selem(Elem {handle: selem, _mixer: self})) } + } + + pub fn open(nonblock: bool) -> Result { + let mut r = ptr::null_mut(); + let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys + acheck!(snd_mixer_open(&mut r, flags)).map(|_| Mixer(r)) + } + + pub fn attach(&mut self, name: &CStr) -> Result<()> { + acheck!(snd_mixer_attach(self.0, name.as_ptr())).map(|_| ()) + } + + pub fn load(&mut self) -> Result<()> { + acheck!(snd_mixer_load(self.0)).map(|_| ()) + } + + pub fn iter(&self) -> Iter { + Iter { + last_handle: ptr::null_mut(), + mixer: self + } + } + + pub fn handle_events(&self) -> Result { + acheck!(snd_mixer_handle_events(self.0)).map(|x| x as u32) + } + + pub fn wait(&self, timeout_ms: Option) -> Result { + acheck!(snd_mixer_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) } +} + +/// Closes mixer and frees used resources +impl Drop for Mixer { + fn drop(&mut self) { + unsafe { alsa::snd_mixer_close(self.0) }; + } +} + + +impl poll::Descriptors for Mixer { + fn count(&self) -> usize { + unsafe { alsa::snd_mixer_poll_descriptors_count(self.0) as usize } + } + fn fill(&self, p: &mut [pollfd]) -> Result { + let z = unsafe { alsa::snd_mixer_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) }; + from_code("snd_mixer_poll_descriptors", z).map(|_| z as usize) + } + fn revents(&self, p: &[pollfd]) -> Result { + let mut r = 0; + let z = unsafe { alsa::snd_mixer_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) }; + from_code("snd_mixer_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short)) + } +} + + +/// Wrapper for a mB (millibel) value. +/// +/// Despite some ALSA functions named "dB", they actually take mB values instead. +/// This is a wrapper type to help with those calculations. Its interior is the +/// actual mB value. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MilliBel(pub i64); + +impl MilliBel { + pub fn to_db(self) -> f32 { (self.0 as f32) / 100.0 } + pub fn from_db(db: f32) -> Self { MilliBel((db * 100.0) as i64) } +} + +impl ops::Deref for MilliBel { + type Target = i64; + fn deref(&self) -> &i64 { &self.0 } +} + +impl ops::Add for MilliBel { + type Output = MilliBel; + fn add(self, rhs: Self) -> Self { MilliBel(self.0 + rhs.0) } +} + +impl ops::AddAssign for MilliBel { + fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 } +} + +impl ops::Sub for MilliBel { + type Output = MilliBel; + fn sub(self, rhs: Self) -> Self { MilliBel(self.0 - rhs.0) } +} + +impl ops::SubAssign for MilliBel { + fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 } +} + +/// Wraps [snd_mixer_elem_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___mixer.html) +#[derive(Copy, Clone, Debug)] +pub struct Elem<'a>{ + handle: *mut alsa::snd_mixer_elem_t, + _mixer: &'a Mixer +} + +/// Iterator for all elements of mixer +#[derive(Copy, Clone)] +pub struct Iter<'a>{ + last_handle: *mut alsa::snd_mixer_elem_t, + mixer: &'a Mixer +} + +impl<'a> Iterator for Iter<'a> { + type Item = Elem<'a>; + + fn next(&mut self) -> Option> { + let elem = if self.last_handle.is_null() { + unsafe { alsa::snd_mixer_first_elem(self.mixer.0) } + } else { + unsafe { alsa::snd_mixer_elem_next(self.last_handle) } + }; + + if elem.is_null() { + None + } else { + self.last_handle = elem; + Some(Elem { handle: elem, _mixer: self.mixer}) + } + } + +} + +/// Wrapper for [snd_mixer_selem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html) +/// No allocation (uses fixed array) +// #[derive(Copy, Clone, Debug)] +pub struct SelemId([u8; SELEM_ID_SIZE]); + +impl SelemId { + + pub fn new(name: &str, index: u32) -> SelemId { + let mut s = SelemId::empty(); + s.set_name(&CString::new(name).unwrap()); + s.set_index(index); + s + } + + /// Returns an empty (zeroed) SelemId. This id is not a usable id and need to be initialized + /// like `SelemId::new()` does + pub fn empty() -> SelemId { + assert!(unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize <= SELEM_ID_SIZE); + // Create empty selem_id and fill from mixer + SelemId(unsafe { mem::zeroed() }) + } + + /// Convert SelemId into ``*mut snd_mixer_selem_id_t` that the alsa call needs. + /// See [snd_mixer_selem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html) + #[inline] + fn as_ptr(&self) -> *mut alsa::snd_mixer_selem_id_t { + self.0.as_ptr() as *const _ as *mut alsa::snd_mixer_selem_id_t + } + + pub fn get_name(&self) -> Result<&str> { + let c = unsafe { alsa::snd_mixer_selem_id_get_name(self.as_ptr()) }; + from_const("snd_mixer_selem_id_get_name", c) + } + + pub fn get_index(&self) -> u32 { + unsafe { alsa::snd_mixer_selem_id_get_index(self.as_ptr()) } + } + + pub fn set_name(&mut self, name: &CStr) { + unsafe { alsa::snd_mixer_selem_id_set_name(self.as_ptr(), name.as_ptr()) }; + } + + pub fn set_index(&mut self, index: u32) { + unsafe { alsa::snd_mixer_selem_id_set_index(self.as_ptr(), index) }; + } + +} + +/// Wraps an Elem as a Selem +// #[derive(Copy, Clone)] +pub struct Selem<'a>(Elem<'a>); + +impl<'a> Selem<'a> { + /// Creates a Selem by wrapping `elem`. + pub fn new(elem: Elem<'a>) -> Option> { + if unsafe { alsa::snd_mixer_elem_get_type(elem.handle) } == alsa::SND_MIXER_ELEM_SIMPLE + { Some(Selem(elem)) } else { None } + } + + /// TODO: This function might change to support regopt and to return the mixer class + pub fn register(mixer: &mut Mixer) -> Result<()> { + acheck!(snd_mixer_selem_register(mixer.0, ptr::null_mut(), ptr::null_mut())).map(|_| ()) + } + + pub fn get_id(&self) -> SelemId { + let id = SelemId::empty(); + unsafe { alsa::snd_mixer_selem_get_id(self.handle, id.as_ptr()) }; + id + } + + pub fn has_capture_volume(&self) -> bool { + unsafe { alsa::snd_mixer_selem_has_capture_volume(self.handle) > 0 } + } + + pub fn has_capture_switch(&self) -> bool { + unsafe { alsa::snd_mixer_selem_has_capture_switch(self.handle) > 0 } + } + + pub fn has_playback_volume(&self) -> bool { + unsafe { alsa::snd_mixer_selem_has_playback_volume(self.handle) > 0 } + } + + pub fn has_playback_switch(&self) -> bool { + unsafe { alsa::snd_mixer_selem_has_playback_switch(self.handle) > 0 } + } + + pub fn can_capture(&self) -> bool { + self.has_capture_volume() || self.has_capture_switch() + } + + pub fn can_playback(&self) -> bool { + self.has_playback_volume() || self.has_playback_switch() + } + + pub fn has_volume(&self) -> bool { + self.has_capture_volume() || self.has_playback_volume() + } + + /// returns range for capture volume as (min, max) values + pub fn get_capture_volume_range(&self) -> (i64, i64) { + let mut min: c_long = 0; + let mut max: c_long = 0; + unsafe { alsa::snd_mixer_selem_get_capture_volume_range(self.handle, &mut min, &mut max) }; + (min as i64, max as i64) + } + + /// returns (min, max) values. + pub fn get_capture_db_range(&self) -> (MilliBel, MilliBel) { + let mut min: c_long = 0; + let mut max: c_long = 0; + unsafe { alsa::snd_mixer_selem_get_capture_dB_range(self.handle, &mut min, &mut max) }; + (MilliBel(min as i64), MilliBel(max as i64)) + } + + /// returns (min, max) values. + pub fn get_playback_volume_range(&self) -> (i64, i64) { + let mut min: c_long = 0; + let mut max: c_long = 0; + unsafe { alsa::snd_mixer_selem_get_playback_volume_range(self.handle, &mut min, &mut max) }; + (min as i64, max as i64) + } + + /// returns (min, max) values. + pub fn get_playback_db_range(&self) -> (MilliBel, MilliBel) { + let mut min: c_long = 0; + let mut max: c_long = 0; + unsafe { alsa::snd_mixer_selem_get_playback_dB_range(self.handle, &mut min, &mut max) }; + (MilliBel(min as i64), MilliBel(max as i64)) + } + + pub fn is_playback_mono(&self) -> bool { + unsafe { alsa::snd_mixer_selem_is_playback_mono(self.handle) == 1 } + } + + pub fn has_capture_channel(&self, channel: SelemChannelId) -> bool { + unsafe { alsa::snd_mixer_selem_has_capture_channel(self.handle, channel as i32) > 0 } + } + + pub fn has_playback_channel(&self, channel: SelemChannelId) -> bool { + unsafe { alsa::snd_mixer_selem_has_playback_channel(self.handle, channel as i32) > 0 } + } + + /// Gets name from snd_mixer_selem_channel_name + pub fn channel_name(channel: SelemChannelId) -> Result<&'static str> { + let c = unsafe { alsa::snd_mixer_selem_channel_name(channel as i32) }; + from_const("snd_mixer_selem_channel_name", c) + } + + pub fn get_playback_volume(&self, channel: SelemChannelId) -> Result { + let mut value: c_long = 0; + acheck!(snd_mixer_selem_get_playback_volume(self.handle, channel as i32, &mut value)).and_then(|_| Ok(value as i64)) + } + + /// returns volume in millibels. + pub fn get_playback_vol_db(&self, channel: SelemChannelId) -> Result { + self.get_playback_volume(channel) + .and_then(|volume| self.ask_playback_vol_db(volume)) + } + + /// Asks alsa to convert playback volume to millibels. + pub fn ask_playback_vol_db(&self, volume: i64) -> Result { + let mut decibel_value: c_long = 0; + acheck!(snd_mixer_selem_ask_playback_vol_dB(self.handle, volume as c_long, &mut decibel_value)) + .map(|_| MilliBel(decibel_value as i64)) + } + + pub fn get_capture_volume(&self, channel: SelemChannelId) -> Result { + let mut value: c_long = 0; + acheck!(snd_mixer_selem_get_capture_volume(self.handle, channel as i32, &mut value)).map(|_| value as i64) + } + + /// returns volume in millibels. + pub fn get_capture_vol_db(&self, channel: SelemChannelId) -> Result { + self.get_capture_volume(channel) + .and_then(|volume| self.ask_capture_vol_db(volume)) + } + + /// Asks alsa to convert capture volume to millibels + pub fn ask_capture_vol_db(&self, volume: i64) -> Result { + let mut decibel_value: c_long = 0; + acheck!(snd_mixer_selem_ask_capture_vol_dB (self.handle, volume as c_long, &mut decibel_value)) + .map(|_| MilliBel(decibel_value as i64)) + } + + pub fn set_playback_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> { + acheck!(snd_mixer_selem_set_playback_volume(self.handle, channel as i32, value as c_long)).map(|_| ()) + } + + pub fn set_playback_volume_range(&self, min: i64, max: i64) -> Result<()> { + acheck!(snd_mixer_selem_set_playback_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ()) + } + + pub fn set_playback_volume_all(&self, value: i64) -> Result<()> { + acheck!(snd_mixer_selem_set_playback_volume_all(self.handle, value as c_long)).map(|_| ()) + } + + pub fn set_playback_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> { + acheck!(snd_mixer_selem_set_playback_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ()) + } + + pub fn set_capture_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> { + acheck!(snd_mixer_selem_set_capture_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ()) + } + + pub fn set_playback_db_all(&self, value: MilliBel, dir: Round) -> Result<()> { + acheck!(snd_mixer_selem_set_playback_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ()) + } + + pub fn set_capture_db_all(&self, value: MilliBel, dir: Round) -> Result<()> { + acheck!(snd_mixer_selem_set_capture_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ()) + } + + pub fn set_capture_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> { + acheck!(snd_mixer_selem_set_capture_volume(self.handle, channel as i32, value as c_long)).map(|_| ()) + } + + pub fn set_capture_volume_range(&self, min: i64, max: i64) -> Result<()> { + acheck!(snd_mixer_selem_set_capture_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ()) + } + + pub fn set_playback_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> { + acheck!(snd_mixer_selem_set_playback_switch(self.handle, channel as i32, value)).map(|_| ()) + } + + pub fn set_playback_switch_all(&self, value: i32) -> Result<()> { + acheck!(snd_mixer_selem_set_playback_switch_all(self.handle, value)).map(|_| ()) + } + + pub fn set_capture_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> { + acheck!(snd_mixer_selem_set_capture_switch(self.handle, channel as i32, value)).map(|_| ()) + } + + pub fn set_capture_switch_all(&self, value: i32) -> Result<()> { + acheck!(snd_mixer_selem_set_capture_switch_all(self.handle, value)).map(|_| ()) + } + + pub fn get_playback_switch(&self, channel: SelemChannelId) -> Result { + let mut value: i32 = 0; + acheck!(snd_mixer_selem_get_playback_switch(self.handle, channel as i32, &mut value)).map(|_| value) + } + + pub fn get_capture_switch(&self, channel: SelemChannelId) -> Result { + let mut value: i32 = 0; + acheck!(snd_mixer_selem_get_capture_switch(self.handle, channel as i32, &mut value)).map(|_| value) + } + + pub fn is_enumerated(&self) -> bool { + unsafe { alsa::snd_mixer_selem_is_enumerated(self.handle) == 1 } + } + + pub fn is_enum_playback(&self) -> bool { + unsafe { alsa::snd_mixer_selem_is_enum_playback(self.handle) == 1 } + } + + pub fn is_enum_capture(&self) -> bool { + unsafe { alsa::snd_mixer_selem_is_enum_capture(self.handle) == 1 } + } + + pub fn get_enum_items(&self) -> Result { + acheck!(snd_mixer_selem_get_enum_items(self.handle)).map(|v| v as u32) + } + + pub fn get_enum_item_name(&self, idx: u32) -> Result { + let mut temp = [0 as ::libc::c_char; 128]; + acheck!(snd_mixer_selem_get_enum_item_name(self.handle, idx, temp.len()-1, temp.as_mut_ptr())) + .and_then(|_| from_const("snd_mixer_selem_get_enum_item_name", temp.as_ptr())) + .map(|v| v.into()) + } + + /// Enumerates over valid Enum values + pub fn iter_enum(&self) -> Result { + Ok(IterEnum(self, 0, self.get_enum_items()?)) + } + + pub fn get_enum_item(&self, channel: SelemChannelId) -> Result { + let mut temp = 0; + acheck!(snd_mixer_selem_get_enum_item(self.handle, channel as i32, &mut temp)) + .map(|_| temp) + } + + pub fn set_enum_item(&self, channel: SelemChannelId, idx: u32) -> Result<()> { + acheck!(snd_mixer_selem_set_enum_item(self.handle, channel as i32, idx)) + .map(|_| ()) + } +} + +impl<'a> ops::Deref for Selem<'a> { + type Target = Elem<'a>; + + /// returns the elem of this selem + fn deref(&self) -> &Elem<'a> { + &self.0 + } +} + +pub struct IterEnum<'a>(&'a Selem<'a>, u32, u32); + +impl<'a> Iterator for IterEnum<'a> { + type Item = Result; + fn next(&mut self) -> Option { + if self.1 >= self.2 { None } + else { self.1 += 1; Some(self.0.get_enum_item_name(self.1-1)) } + } +} + +alsa_enum!( + /// Wrapper for [SND_MIXER_SCHN_*](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html) constants + SelemChannelId, ALL_SELEM_CHANNEL_ID[11], + + Unknown = SND_MIXER_SCHN_UNKNOWN, + FrontLeft = SND_MIXER_SCHN_FRONT_LEFT, + FrontRight = SND_MIXER_SCHN_FRONT_RIGHT, + RearLeft = SND_MIXER_SCHN_REAR_LEFT, + RearRight = SND_MIXER_SCHN_REAR_RIGHT, + FrontCenter = SND_MIXER_SCHN_FRONT_CENTER, + Woofer = SND_MIXER_SCHN_WOOFER, + SideLeft = SND_MIXER_SCHN_SIDE_LEFT, + SideRight = SND_MIXER_SCHN_SIDE_RIGHT, + RearCenter = SND_MIXER_SCHN_REAR_CENTER, + Last = SND_MIXER_SCHN_LAST, +); + +impl SelemChannelId { + pub fn mono() -> SelemChannelId { SelemChannelId::FrontLeft } +} + +impl fmt::Display for SelemChannelId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", Selem::channel_name(*self).unwrap()) + } +} + +#[test] +fn print_mixer_of_cards() { + use super::card; + + for card in card::Iter::new().map(|c| c.unwrap()) { + println!("Card #{}: {} ({})", card.get_index(), card.get_name().unwrap(), card.get_longname().unwrap()); + + let mixer = Mixer::new(&format!("hw:{}", card.get_index()), false).unwrap(); + for selem in mixer.iter().filter_map(|e| Selem::new(e)) { + + let sid = selem.get_id(); + println!("\tMixer element {},{}:", sid.get_name().unwrap(), sid.get_index()); + + if selem.has_volume() { + print!("\t Volume limits: "); + if selem.has_capture_volume() { + let (vmin, vmax) = selem.get_capture_volume_range(); + let (mbmin, mbmax) = selem.get_capture_db_range(); + print!("Capture = {} - {}", vmin, vmax); + print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db()); + } + if selem.has_playback_volume() { + let (vmin, vmax) = selem.get_playback_volume_range(); + let (mbmin, mbmax) = selem.get_playback_db_range(); + print!("Playback = {} - {}", vmin, vmax); + print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db()); + } + println!(); + } + + if selem.is_enumerated() { + print!("\t Valid values: "); + for v in selem.iter_enum().unwrap() { print!("{}, ", v.unwrap()) }; + print!("\n\t Current values: "); + for v in SelemChannelId::all().iter().filter_map(|&v| selem.get_enum_item(v).ok()) { + print!("{}, ", selem.get_enum_item_name(v).unwrap()); + } + println!(); + } + + if selem.can_capture() { + print!("\t Capture channels: "); + for channel in SelemChannelId::all() { + if selem.has_capture_channel(*channel) { print!("{}, ", channel) }; + } + println!(); + print!("\t Capture volumes: "); + for channel in SelemChannelId::all() { + if selem.has_capture_channel(*channel) { print!("{}: {} ({} dB), ", channel, + match selem.get_capture_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()}, + match selem.get_capture_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()} + );} + } + println!(); + } + + if selem.can_playback() { + print!("\t Playback channels: "); + if selem.is_playback_mono() { + print!("Mono"); + } else { + for channel in SelemChannelId::all() { + if selem.has_playback_channel(*channel) { print!("{}, ", channel) }; + } + } + println!(); + if selem.has_playback_volume() { + print!("\t Playback volumes: "); + for channel in SelemChannelId::all() { + if selem.has_playback_channel(*channel) { print!("{}: {} / {}dB, ", + channel, + match selem.get_playback_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()}, + match selem.get_playback_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()} + );} + } + println!(); + } + } + } + } +} + +#[test] +#[ignore] +fn get_and_set_playback_volume() { + let mixer = Mixer::new("hw:1", false).unwrap(); + let selem = mixer.find_selem(&SelemId::new("Master", 0)).unwrap(); + + let (rmin, rmax) = selem.get_playback_volume_range(); + let mut channel = SelemChannelId::mono(); + for c in SelemChannelId::all().iter() { + if selem.has_playback_channel(*c) { channel = *c; break } + } + println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel); + + let old: i64 = selem.get_playback_volume(channel).unwrap(); + let new: i64 = rmax / 2; + assert_ne!(new, old); + + println!("Changing volume of {} from {} to {}", channel, old, new); + selem.set_playback_volume(channel, new).unwrap(); + let mut result: i64 = selem.get_playback_volume(channel).unwrap(); + assert_eq!(new, result); + + // return volume to old value + selem.set_playback_volume(channel, old).unwrap(); + result = selem.get_playback_volume(channel).unwrap(); + assert_eq!(old, result); +} + +#[test] +#[ignore] +fn get_and_set_capture_volume() { + let mixer = Mixer::new("hw:1", false).unwrap(); + let selem = mixer.find_selem(&SelemId::new("Capture", 0)).unwrap(); + + let (rmin, rmax) = selem.get_capture_volume_range(); + let mut channel = SelemChannelId::mono(); + for c in SelemChannelId::all().iter() { + if selem.has_playback_channel(*c) { channel = *c; break } + } + println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel); + + let old: i64 = selem.get_capture_volume(channel).unwrap(); + let new: i64 = rmax / 2; + assert_ne!(new, old); + + println!("Changing volume of {} from {} to {}", channel, old, new); + selem.set_capture_volume(channel, new).unwrap(); + let mut result: i64 = selem.get_capture_volume(channel).unwrap(); + assert_eq!(new, result); + + // return volume to old value + selem.set_capture_volume(channel, old).unwrap(); + result = selem.get_capture_volume(channel).unwrap(); + assert_eq!(old, result); +} + + +#[test] +fn print_sizeof() { + let selemid = unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize; + + assert!(selemid <= SELEM_ID_SIZE); + println!("Selem id: {}", selemid); +} diff --git a/alsa/src/pcm.rs b/alsa/src/pcm.rs new file mode 100644 index 0000000..b25a1d9 --- /dev/null +++ b/alsa/src/pcm.rs @@ -0,0 +1,1151 @@ +//! Audio playback and capture +//! +//! # Example +//! Playback a sine wave through the "default" device. +//! +//! ``` +//! use alsa::{Direction, ValueOr}; +//! use alsa::pcm::{PCM, HwParams, Format, Access, State}; +//! +//! // Open default playback device +//! let pcm = PCM::new("default", Direction::Playback, false).unwrap(); +//! +//! // Set hardware parameters: 44100 Hz / Mono / 16 bit +//! let hwp = HwParams::any(&pcm).unwrap(); +//! hwp.set_channels(1).unwrap(); +//! hwp.set_rate(44100, ValueOr::Nearest).unwrap(); +//! hwp.set_format(Format::s16()).unwrap(); +//! hwp.set_access(Access::RWInterleaved).unwrap(); +//! pcm.hw_params(&hwp).unwrap(); +//! let io = pcm.io_i16().unwrap(); +//! +//! // Make sure we don't start the stream too early +//! let hwp = pcm.hw_params_current().unwrap(); +//! let swp = pcm.sw_params_current().unwrap(); +//! swp.set_start_threshold(hwp.get_buffer_size().unwrap()).unwrap(); +//! pcm.sw_params(&swp).unwrap(); +//! +//! // Make a sine wave +//! let mut buf = [0i16; 1024]; +//! for (i, a) in buf.iter_mut().enumerate() { +//! *a = ((i as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16 +//! } +//! +//! // Play it back for 2 seconds. +//! for _ in 0..2*44100/1024 { +//! assert_eq!(io.writei(&buf[..]).unwrap(), 1024); +//! } +//! +//! // In case the buffer was larger than 2 seconds, start the stream manually. +//! if pcm.state() != State::Running { pcm.start().unwrap() }; +//! // Wait for the stream to finish playback. +//! pcm.drain().unwrap(); +//! ``` + + +use libc::{c_int, c_uint, c_void, ssize_t, c_short, timespec, pollfd}; +use crate::alsa; +use std::marker::PhantomData; +use std::mem::size_of; +use std::ffi::{CStr, CString}; +use std::{io, fmt, ptr, cell}; +use super::error::*; +use super::{Direction, Output, poll, ValueOr, chmap}; + +pub use super::chmap::{Chmap, ChmapPosition, ChmapType, ChmapsQuery}; + +/// [snd_pcm_sframes_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) +pub type Frames = alsa::snd_pcm_sframes_t; + +pub struct Info(*mut alsa::snd_pcm_info_t); + +impl Info { + pub fn new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_pcm_info_malloc(&mut p)).map(|_| Info(p)) + } + + pub fn get_card(&self) -> i32 { + unsafe { alsa::snd_pcm_info_get_card(self.0) } + } + + pub fn get_device(&self) -> u32 { + unsafe { alsa::snd_pcm_info_get_device(self.0) } + } + + pub fn get_subdevice(&self) -> u32 { + unsafe { alsa::snd_pcm_info_get_subdevice(self.0) } + } + + pub fn get_id(&self) -> Result<&str> { + let c = unsafe { alsa::snd_pcm_info_get_id(self.0) }; + from_const("snd_pcm_info_get_id", c) + } + + pub fn get_name(&self) -> Result<&str> { + let c = unsafe { alsa::snd_pcm_info_get_name(self.0) }; + from_const("snd_pcm_info_get_name", c) + } + + pub fn get_subdevice_name(&self) -> Result<&str> { + let c = unsafe { alsa::snd_pcm_info_get_subdevice_name(self.0) }; + from_const("snd_pcm_info_get_subdevice_name", c) + } + + pub fn get_stream(&self) -> Direction { + match unsafe { alsa::snd_pcm_info_get_stream(self.0) } { + alsa::SND_PCM_STREAM_CAPTURE => Direction::Capture, + alsa::SND_PCM_STREAM_PLAYBACK => Direction::Playback, + n @ _ => panic!("snd_pcm_info_get_stream invalid direction '{}'", n), + } + } +} + +impl Drop for Info { + fn drop(&mut self) { unsafe { alsa::snd_pcm_info_free(self.0) }; } +} + +/// [snd_pcm_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) wrapper - start here for audio playback and recording +pub struct PCM(*mut alsa::snd_pcm_t, cell::Cell); + +unsafe impl Send for PCM {} + +impl PCM { + fn check_has_io(&self) { + if self.1.get() { panic!("No hw_params call or additional IO objects allowed") } + } + + /// Wrapper around open that takes a &str instead of a &CStr + pub fn new(name: &str, dir: Direction, nonblock: bool) -> Result { + Self::open(&CString::new(name).unwrap(), dir, nonblock) + } + + // Does not offer async mode (it's not very Rustic anyway) + pub fn open(name: &CStr, dir: Direction, nonblock: bool) -> Result { + let mut r = ptr::null_mut(); + let stream = match dir { + Direction::Capture => alsa::SND_PCM_STREAM_CAPTURE, + Direction::Playback => alsa::SND_PCM_STREAM_PLAYBACK + }; + let flags = if nonblock { alsa::SND_PCM_NONBLOCK } else { 0 }; + acheck!(snd_pcm_open(&mut r, name.as_ptr(), stream, flags)).map(|_| PCM(r, cell::Cell::new(false))) + } + + pub fn start(&self) -> Result<()> { acheck!(snd_pcm_start(self.0)).map(|_| ()) } + pub fn drop(&self) -> Result<()> { acheck!(snd_pcm_drop(self.0)).map(|_| ()) } + pub fn pause(&self, pause: bool) -> Result<()> { + acheck!(snd_pcm_pause(self.0, if pause { 1 } else { 0 })).map(|_| ()) } + pub fn resume(&self) -> Result<()> { acheck!(snd_pcm_resume(self.0)).map(|_| ()) } + pub fn drain(&self) -> Result<()> { acheck!(snd_pcm_drain(self.0)).map(|_| ()) } + pub fn prepare(&self) -> Result<()> { acheck!(snd_pcm_prepare(self.0)).map(|_| ()) } + pub fn reset(&self) -> Result<()> { acheck!(snd_pcm_reset(self.0)).map(|_| ()) } + pub fn recover(&self, err: c_int, silent: bool) -> Result<()> { + acheck!(snd_pcm_recover(self.0, err, if silent { 1 } else { 0 })).map(|_| ()) } + + /// Wrapper around snd_pcm_recover. + /// + /// Returns Ok if the error was successfully recovered from, or the original + /// error if the error was unhandled. + pub fn try_recover(&self, err: Error, silent: bool) -> Result<()> { + self.recover(err.errno() as c_int, silent) + } + + pub fn wait(&self, timeout_ms: Option) -> Result { + acheck!(snd_pcm_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) } + + pub fn state(&self) -> State { + let rawstate = self.state_raw(); + if let Ok(state) = State::from_c_int(rawstate, "snd_pcm_state") { + state + } + else { + panic!("snd_pcm_state returned an invalid value of {}", rawstate); + } + } + + /// Only used internally, and for debugging the alsa library. Please use the "state" function instead. + pub fn state_raw(&self) -> c_int { unsafe { alsa::snd_pcm_state(self.0) as c_int } } + + pub fn bytes_to_frames(&self, i: isize) -> Frames { unsafe { alsa::snd_pcm_bytes_to_frames(self.0, i as ssize_t) }} + pub fn frames_to_bytes(&self, i: Frames) -> isize { unsafe { alsa::snd_pcm_frames_to_bytes(self.0, i) as isize }} + + pub fn avail_update(&self) -> Result { acheck!(snd_pcm_avail_update(self.0)) } + pub fn avail(&self) -> Result { acheck!(snd_pcm_avail(self.0)) } + + pub fn avail_delay(&self) -> Result<(Frames, Frames)> { + let (mut a, mut d) = (0, 0); + acheck!(snd_pcm_avail_delay(self.0, &mut a, &mut d)).map(|_| (a, d)) + } + pub fn delay(&self) -> Result { + let mut d = 0; + acheck!(snd_pcm_delay(self.0, &mut d)).map(|_| d) + } + + pub fn status(&self) -> Result { + StatusBuilder::new().build(self) + } + + fn verify_format(&self, f: Format) -> Result<()> { + let ff = self.hw_params_current().and_then(|h| h.get_format())?; + if ff == f { Ok(()) } + else { + // let s = format!("Invalid sample format ({:?}, expected {:?})", ff, f); + Err(Error::unsupported("io_xx")) + } + } + + pub fn io_i8(&self) -> Result> { self.io_checked() } + pub fn io_u8(&self) -> Result> { self.io_checked() } + pub fn io_i16(&self) -> Result> { self.io_checked() } + pub fn io_u16(&self) -> Result> { self.io_checked() } + pub fn io_i32(&self) -> Result> { self.io_checked() } + pub fn io_u32(&self) -> Result> { self.io_checked() } + pub fn io_f32(&self) -> Result> { self.io_checked() } + pub fn io_f64(&self) -> Result> { self.io_checked() } + + pub fn io_checked(&self) -> Result> { + self.verify_format(S::FORMAT).map(|_| IO::new(self)) + } + + #[deprecated(note = "renamed to io_bytes")] + pub fn io(&self) -> IO { IO::new(self) } + pub fn io_bytes(&self) -> IO { IO::new(self) } + + /// Read buffers by talking to the kernel directly, bypassing alsa-lib. + pub fn direct_mmap_capture(&self) -> Result> { + self.check_has_io(); + crate::direct::pcm::new_mmap(self) + } + + /// Write buffers by talking to the kernel directly, bypassing alsa-lib. + pub fn direct_mmap_playback(&self) -> Result> { + self.check_has_io(); + crate::direct::pcm::new_mmap(self) + } + + /// Sets hw parameters. Note: No IO object can exist for this PCM + /// when hw parameters are set. + pub fn hw_params(&self, h: &HwParams) -> Result<()> { + self.check_has_io(); + acheck!(snd_pcm_hw_params(self.0, h.0)).map(|_| ()) + } + + /// Retreive current PCM hardware configuration. + pub fn hw_params_current(&self) -> Result { + HwParams::new(self).and_then(|h| + acheck!(snd_pcm_hw_params_current(self.0, h.0)).map(|_| h)) + } + + pub fn sw_params(&self, h: &SwParams) -> Result<()> { + acheck!(snd_pcm_sw_params(self.0, h.0)).map(|_| ()) + } + + pub fn sw_params_current(&self) -> Result { + SwParams::new(self).and_then(|h| + acheck!(snd_pcm_sw_params_current(self.0, h.0)).map(|_| h)) + } + + /// Wraps `snd_pcm_get_params`, returns `(buffer_size, period_size)`. + pub fn get_params(&self) -> Result<(u64, u64)> { + let mut buffer_size = 0; + let mut period_size = 0; + acheck!(snd_pcm_get_params(self.0, &mut buffer_size, &mut period_size)) + .map(|_| (buffer_size as u64, period_size as u64)) + + } + + pub fn info(&self) -> Result { + Info::new().and_then(|info| + acheck!(snd_pcm_info(self.0, info.0)).map(|_| info )) + } + + pub fn dump(&self, o: &mut Output) -> Result<()> { + acheck!(snd_pcm_dump(self.0, super::io::output_handle(o))).map(|_| ()) + } + + pub fn dump_hw_setup(&self, o: &mut Output) -> Result<()> { + acheck!(snd_pcm_dump_hw_setup(self.0, super::io::output_handle(o))).map(|_| ()) + } + + pub fn dump_sw_setup(&self, o: &mut Output) -> Result<()> { + acheck!(snd_pcm_dump_sw_setup(self.0, super::io::output_handle(o))).map(|_| ()) + } + + pub fn query_chmaps(&self) -> ChmapsQuery { + chmap::chmaps_query_new(unsafe { alsa::snd_pcm_query_chmaps(self.0) }) + } + + pub fn set_chmap(&self, c: &Chmap) -> Result<()> { + acheck!(snd_pcm_set_chmap(self.0, chmap::chmap_handle(c))).map(|_| ()) + } + + pub fn get_chmap(&self) -> Result { + let p = unsafe { alsa::snd_pcm_get_chmap(self.0) }; + if p.is_null() { Err(Error::unsupported("snd_pcm_get_chmap")) } + else { Ok(chmap::chmap_new(p)) } + } + + pub fn link(&self, other: &PCM) -> Result<()> { + acheck!(snd_pcm_link(self.0, other.0)).map(|_| ()) + } + + pub fn unlink(&self) -> Result<()> { + acheck!(snd_pcm_unlink(self.0)).map(|_| ()) + } +} + +impl Drop for PCM { + fn drop(&mut self) { unsafe { alsa::snd_pcm_close(self.0) }; } +} + + +impl poll::Descriptors for PCM { + fn count(&self) -> usize { + unsafe { alsa::snd_pcm_poll_descriptors_count(self.0) as usize } + } + fn fill(&self, p: &mut [pollfd]) -> Result { + let z = unsafe { alsa::snd_pcm_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) }; + from_code("snd_pcm_poll_descriptors", z).map(|_| z as usize) + } + fn revents(&self, p: &[pollfd]) -> Result { + let mut r = 0; + let z = unsafe { alsa::snd_pcm_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) }; + from_code("snd_pcm_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short)) + } +} + +/// Sample format dependent struct for reading from and writing data to a `PCM`. +/// Also implements `std::io::Read` and `std::io::Write`. +/// +/// Note: Only one IO object is allowed in scope at a time (for mmap safety). +pub struct IO<'a, S: Copy>(&'a PCM, PhantomData); + +impl<'a, S: Copy> Drop for IO<'a, S> { + fn drop(&mut self) { (self.0).1.set(false) } +} + +impl<'a, S: Copy> IO<'a, S> { + + fn new(a: &'a PCM) -> IO<'a, S> { + a.check_has_io(); + a.1.set(true); + IO(a, PhantomData) + } + + fn to_frames(&self, b: usize) -> alsa::snd_pcm_uframes_t { + // TODO: Do we need to check for overflow here? + self.0.bytes_to_frames((b * size_of::()) as isize) as alsa::snd_pcm_uframes_t + } + + fn from_frames(&self, b: alsa::snd_pcm_uframes_t) -> usize { + // TODO: Do we need to check for overflow here? + (self.0.frames_to_bytes(b as Frames) as usize) / size_of::() + } + + /// On success, returns number of *frames* written. + /// (Multiply with number of channels to get number of items in buf successfully written.) + pub fn writei(&self, buf: &[S]) -> Result { + acheck!(snd_pcm_writei((self.0).0, buf.as_ptr() as *const c_void, self.to_frames(buf.len()))).map(|r| r as usize) + } + + /// On success, returns number of *frames* read. + /// (Multiply with number of channels to get number of items in buf successfully read.) + pub fn readi(&self, buf: &mut [S]) -> Result { + acheck!(snd_pcm_readi((self.0).0, buf.as_mut_ptr() as *mut c_void, self.to_frames(buf.len()))).map(|r| r as usize) + } + + /// Wrapper around snd_pcm_mmap_begin and snd_pcm_mmap_commit. + /// + /// You can read/write into the sound card's buffer during the call to the closure. + /// According to alsa-lib docs, you should call avail_update before calling this function. + /// + /// All calculations are in *frames*, i e, the closure should return number of frames processed. + /// Also, there might not be as many frames to read/write as requested, and there can even be + /// an empty buffer supplied to the closure. + /// + /// Note: This function works only with interleaved access mode. + pub fn mmap usize>(&self, frames: usize, func: F) -> Result { + let mut f = frames as alsa::snd_pcm_uframes_t; + let mut offs: alsa::snd_pcm_uframes_t = 0; + let mut areas = ptr::null(); + acheck!(snd_pcm_mmap_begin((self.0).0, &mut areas, &mut offs, &mut f))?; + + let (first, step) = unsafe { ((*areas).first, (*areas).step) }; + if first != 0 || step as isize != self.0.frames_to_bytes(1) * 8 { + unsafe { alsa::snd_pcm_mmap_commit((self.0).0, offs, 0) }; + // let s = format!("Can only mmap a single interleaved buffer (first = {:?}, step = {:?})", first, step); + return Err(Error::unsupported("snd_pcm_mmap_begin")); + } + + let buf = unsafe { + let p = ((*areas).addr as *mut S).add(self.from_frames(offs)); + ::std::slice::from_raw_parts_mut(p, self.from_frames(f)) + }; + let fres = func(buf); + debug_assert!(fres <= f as usize); + acheck!(snd_pcm_mmap_commit((self.0).0, offs, fres as alsa::snd_pcm_uframes_t)).map(|r| r as usize) + } +} + +impl<'a, S: Copy> io::Read for IO<'a, S> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let size = self.0.bytes_to_frames(buf.len() as isize) as alsa::snd_pcm_uframes_t; // TODO: Do we need to check for overflow here? + let r = unsafe { alsa::snd_pcm_readi((self.0).0, buf.as_mut_ptr() as *mut c_void, size) }; + if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) } + else { Ok(self.0.frames_to_bytes(r) as usize) } + } +} + +impl<'a, S: Copy> io::Write for IO<'a, S> { + fn write(&mut self, buf: &[u8]) -> io::Result { + let size = self.0.bytes_to_frames(buf.len() as isize) as alsa::snd_pcm_uframes_t; // TODO: Do we need to check for overflow here? + let r = unsafe { alsa::snd_pcm_writei((self.0).0, buf.as_ptr() as *const c_void, size) }; + if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) } + else { Ok(self.0.frames_to_bytes(r) as usize) } + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} + + +alsa_enum!( + /// [SND_PCM_STATE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants + State, ALL_STATES[9], + + Open = SND_PCM_STATE_OPEN, + Setup = SND_PCM_STATE_SETUP, + Prepared = SND_PCM_STATE_PREPARED, + Running = SND_PCM_STATE_RUNNING, + XRun = SND_PCM_STATE_XRUN, + Draining = SND_PCM_STATE_DRAINING, + Paused = SND_PCM_STATE_PAUSED, + Suspended = SND_PCM_STATE_SUSPENDED, + Disconnected = SND_PCM_STATE_DISCONNECTED, +); + +alsa_enum!( + #[non_exhaustive] + /// [SND_PCM_FORMAT_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants + Format, ALL_FORMATS[48], + + Unknown = SND_PCM_FORMAT_UNKNOWN, + S8 = SND_PCM_FORMAT_S8, + U8 = SND_PCM_FORMAT_U8, + S16LE = SND_PCM_FORMAT_S16_LE, + S16BE = SND_PCM_FORMAT_S16_BE, + U16LE = SND_PCM_FORMAT_U16_LE, + U16BE = SND_PCM_FORMAT_U16_BE, + S24LE = SND_PCM_FORMAT_S24_LE, + S24BE = SND_PCM_FORMAT_S24_BE, + U24LE = SND_PCM_FORMAT_U24_LE, + U24BE = SND_PCM_FORMAT_U24_BE, + S32LE = SND_PCM_FORMAT_S32_LE, + S32BE = SND_PCM_FORMAT_S32_BE, + U32LE = SND_PCM_FORMAT_U32_LE, + U32BE = SND_PCM_FORMAT_U32_BE, + FloatLE = SND_PCM_FORMAT_FLOAT_LE, + FloatBE = SND_PCM_FORMAT_FLOAT_BE, + Float64LE = SND_PCM_FORMAT_FLOAT64_LE, + Float64BE = SND_PCM_FORMAT_FLOAT64_BE, + IEC958SubframeLE = SND_PCM_FORMAT_IEC958_SUBFRAME_LE, + IEC958SubframeBE = SND_PCM_FORMAT_IEC958_SUBFRAME_BE, + MuLaw = SND_PCM_FORMAT_MU_LAW, + ALaw = SND_PCM_FORMAT_A_LAW, + ImaAdPCM = SND_PCM_FORMAT_IMA_ADPCM, + MPEG = SND_PCM_FORMAT_MPEG, + GSM = SND_PCM_FORMAT_GSM, + Special = SND_PCM_FORMAT_SPECIAL, + S243LE = SND_PCM_FORMAT_S24_3LE, + S243BE = SND_PCM_FORMAT_S24_3BE, + U243LE = SND_PCM_FORMAT_U24_3LE, + U243BE = SND_PCM_FORMAT_U24_3BE, + S203LE = SND_PCM_FORMAT_S20_3LE, + S203BE = SND_PCM_FORMAT_S20_3BE, + U203LE = SND_PCM_FORMAT_U20_3LE, + U203BE = SND_PCM_FORMAT_U20_3BE, + S183LE = SND_PCM_FORMAT_S18_3LE, + S183BE = SND_PCM_FORMAT_S18_3BE, + U183LE = SND_PCM_FORMAT_U18_3LE, + U183BE = SND_PCM_FORMAT_U18_3BE, + G72324 = SND_PCM_FORMAT_G723_24, + G723241B = SND_PCM_FORMAT_G723_24_1B, + G72340 = SND_PCM_FORMAT_G723_40, + G723401B = SND_PCM_FORMAT_G723_40_1B, + DSDU8 = SND_PCM_FORMAT_DSD_U8, + DSDU16LE = SND_PCM_FORMAT_DSD_U16_LE, + DSDU32LE = SND_PCM_FORMAT_DSD_U32_LE, + DSDU16BE = SND_PCM_FORMAT_DSD_U16_BE, + DSDU32BE = SND_PCM_FORMAT_DSD_U32_BE, +); + +impl fmt::Display for Format { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use Format::*; + match *self { + S8 => write!(f, "S8"), + U8 => write!(f, "U8"), + S16LE => write!(f, "S16_LE"), + S16BE => write!(f, "S16_BE"), + U16LE => write!(f, "U16_LE"), + U16BE => write!(f, "U16_BE"), + S24LE => write!(f, "S24_LE"), + S24BE => write!(f, "S24_BE"), + U24LE => write!(f, "U24_LE"), + U24BE => write!(f, "U24_BE"), + S32LE => write!(f, "S32_LE"), + S32BE => write!(f, "S32_BE"), + U32LE => write!(f, "U32_LE"), + U32BE => write!(f, "U32_BE"), + FloatLE => write!(f, "FLOAT_LE"), + FloatBE => write!(f, "FLOAT_BE"), + Float64LE => write!(f, "FLOAT64_LE"), + Float64BE => write!(f, "FLOAT64_BE"), + IEC958SubframeLE => write!(f, "IEC958_SUBFRAME_LE"), + IEC958SubframeBE => write!(f, "IEC958_SUBFRAME_BE"), + MuLaw => write!(f, "MU_LAW"), + ALaw => write!(f, "A_LAW"), + ImaAdPCM => write!(f, "IMA_ADPCM"), + MPEG => write!(f, "MPEG"), + GSM => write!(f, "GSM"), + Special => write!(f, "SPECIAL"), + S243LE => write!(f, "S24_3LE"), + S243BE => write!(f, "S24_3BE"), + U243LE => write!(f, "U24_3LE"), + U243BE => write!(f, "U24_3BE"), + S203LE => write!(f, "S20_3LE"), + S203BE => write!(f, "S20_3BE"), + U203LE => write!(f, "U20_3LE"), + U203BE => write!(f, "U20_3BE"), + S183LE => write!(f, "S18_3LE"), + S183BE => write!(f, "S18_3BE"), + U183LE => write!(f, "U18_3LE"), + U183BE => write!(f, "U18_3BE"), + G72324 => write!(f, "G723_24"), + G723241B => write!(f, "G723_24_1B"), + G72340 => write!(f, "G723_40"), + G723401B => write!(f, "G723_40_1B"), + DSDU8 => write!(f, "DSD_U8"), + DSDU16LE => write!(f, "DSD_U16_LE"), + DSDU32LE => write!(f, "DSD_U32_LE"), + DSDU16BE => write!(f, "DSD_U16_BE"), + DSDU32BE => write!(f, "DSD_U32_BE"), + _ => write!(f, "UNKNOWN"), + } + } +} + +impl Format { + pub const fn s16() -> Format { ::FORMAT } + pub const fn u16() -> Format { ::FORMAT } + pub const fn s32() -> Format { ::FORMAT } + pub const fn u32() -> Format { ::FORMAT } + pub const fn float() -> Format { ::FORMAT } + pub const fn float64() -> Format { ::FORMAT } + + #[cfg(target_endian = "little")] pub const fn s24() -> Format { Format::S24LE } + #[cfg(target_endian = "big")] pub const fn s24() -> Format { Format::S24BE } + + #[cfg(target_endian = "little")] pub const fn s24_3() -> Format { Format::S243LE } + #[cfg(target_endian = "big")] pub const fn s24_3() -> Format { Format::S243BE } + + #[cfg(target_endian = "little")] pub const fn u24() -> Format { Format::U24LE } + #[cfg(target_endian = "big")] pub const fn u24() -> Format { Format::U24BE } + + #[cfg(target_endian = "little")] pub const fn u24_3() -> Format { Format::U243LE } + #[cfg(target_endian = "big")] pub const fn u24_3() -> Format { Format::U243BE } + + #[cfg(target_endian = "little")] pub const fn s20_3() -> Format { Format::S203LE } + #[cfg(target_endian = "big")] pub const fn s20_3() -> Format { Format::S203BE } + + #[cfg(target_endian = "little")] pub const fn u20_3() -> Format { Format::U203LE } + #[cfg(target_endian = "big")] pub const fn u20_3() -> Format { Format::U203BE } + + #[cfg(target_endian = "little")] pub const fn s18_3() -> Format { Format::S183LE } + #[cfg(target_endian = "big")] pub const fn s18_3() -> Format { Format::S183BE } + + #[cfg(target_endian = "little")] pub const fn u18_3() -> Format { Format::U183LE } + #[cfg(target_endian = "big")] pub const fn u18_3() -> Format { Format::U183BE } + + #[cfg(target_endian = "little")] pub const fn dsd_u16() -> Format { Format::DSDU16LE } + #[cfg(target_endian = "big")] pub const fn dsd_u16() -> Format { Format::DSDU16BE } + + #[cfg(target_endian = "little")] pub const fn dsd_u32() -> Format { Format::DSDU32LE } + #[cfg(target_endian = "big")] pub const fn dsd_u32() -> Format { Format::DSDU32BE } + + #[cfg(target_endian = "little")] pub const fn iec958_subframe() -> Format { Format::IEC958SubframeLE } + #[cfg(target_endian = "big")] pub const fn iec958_subframe() -> Format { Format::IEC958SubframeBE } +} + + +pub trait IoFormat: Copy { + const FORMAT: Format; +} + +impl IoFormat for i8 { const FORMAT: Format = Format::S8; } +impl IoFormat for u8 { const FORMAT: Format = Format::U8; } + +impl IoFormat for i16 { + #[cfg(target_endian = "little")] + const FORMAT: Format = Format::S16LE; + #[cfg(target_endian = "big")] + const FORMAT: Format = Format::S16BE; +} +impl IoFormat for u16 { + #[cfg(target_endian = "little")] + const FORMAT: Format = Format::U16LE; + #[cfg(target_endian = "big")] + const FORMAT: Format = Format::U16BE; +} +impl IoFormat for i32 { + #[cfg(target_endian = "little")] + const FORMAT: Format = Format::S32LE; + #[cfg(target_endian = "big")] + const FORMAT: Format = Format::S32BE; +} +impl IoFormat for u32 { + #[cfg(target_endian = "little")] + const FORMAT: Format = Format::U32LE; + #[cfg(target_endian = "big")] + const FORMAT: Format = Format::U32BE; +} +impl IoFormat for f32 { + #[cfg(target_endian = "little")] + const FORMAT: Format = Format::FloatLE; + #[cfg(target_endian = "big")] + const FORMAT: Format = Format::FloatBE; +} +impl IoFormat for f64 { + #[cfg(target_endian = "little")] + const FORMAT: Format = Format::Float64LE; + #[cfg(target_endian = "big")] + const FORMAT: Format = Format::Float64BE; +} + + +alsa_enum!( + /// [SND_PCM_ACCESS_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants + Access, ALL_ACCESSES[5], + + MMapInterleaved = SND_PCM_ACCESS_MMAP_INTERLEAVED, + MMapNonInterleaved = SND_PCM_ACCESS_MMAP_NONINTERLEAVED, + MMapComplex = SND_PCM_ACCESS_MMAP_COMPLEX, + RWInterleaved = SND_PCM_ACCESS_RW_INTERLEAVED, + RWNonInterleaved = SND_PCM_ACCESS_RW_NONINTERLEAVED, +); + +alsa_enum!( + /// [SND_PCM_TSTAMP_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants + TstampType, ALL_TSTAMP_TYPES[3], + + Gettimeofday = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY, + Monotonic = SND_PCM_TSTAMP_TYPE_MONOTONIC, + MonotonicRaw = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW, +); + +/// [snd_pcm_hw_params_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html) wrapper +pub struct HwParams<'a>(*mut alsa::snd_pcm_hw_params_t, &'a PCM); + +impl<'a> Drop for HwParams<'a> { + fn drop(&mut self) { unsafe { alsa::snd_pcm_hw_params_free(self.0) }; } +} + +impl<'a> HwParams<'a> { + fn new(a: &'a PCM) -> Result> { + let mut p = ptr::null_mut(); + acheck!(snd_pcm_hw_params_malloc(&mut p)).map(|_| HwParams(p, a)) + } + + pub fn any(a: &'a PCM) -> Result> { HwParams::new(a).and_then(|p| + acheck!(snd_pcm_hw_params_any(a.0, p.0)).map(|_| p) + )} + + pub fn get_rate_resample(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_rate_resample((self.1).0, self.0, &mut v)).map(|_| v != 0) + } + + pub fn set_rate_resample(&self, resample: bool) -> Result<()> { + acheck!(snd_pcm_hw_params_set_rate_resample((self.1).0, self.0, if resample {1} else {0})).map(|_| ()) + } + + pub fn set_channels_near(&self, v: u32) -> Result { + let mut r = v as c_uint; + acheck!(snd_pcm_hw_params_set_channels_near((self.1).0, self.0, &mut r)).map(|_| r) + } + + pub fn set_channels(&self, v: u32) -> Result<()> { + acheck!(snd_pcm_hw_params_set_channels((self.1).0, self.0, v as c_uint)).map(|_| ()) + } + + pub fn get_channels(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_channels(self.0, &mut v)).map(|_| v as u32) + } + + pub fn get_channels_max(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_channels_max(self.0, &mut v)).map(|_| v as u32) + } + + pub fn get_channels_min(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_channels_min(self.0, &mut v)).map(|_| v as u32) + } + + pub fn test_channels(&self, v: u32) -> Result<()> { + acheck!(snd_pcm_hw_params_test_channels((self.1).0, self.0, v as c_uint)).map(|_| ()) + } + + pub fn set_rate_near(&self, v: u32, dir: ValueOr) -> Result { + let mut d = dir as c_int; + let mut r = v as c_uint; + acheck!(snd_pcm_hw_params_set_rate_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r) + } + + pub fn set_rate(&self, v: u32, dir: ValueOr) -> Result<()> { + acheck!(snd_pcm_hw_params_set_rate((self.1).0, self.0, v as c_uint, dir as c_int)).map(|_| ()) + } + + pub fn get_rate(&self) -> Result { + let (mut v, mut d) = (0,0); + acheck!(snd_pcm_hw_params_get_rate(self.0, &mut v, &mut d)).map(|_| v as u32) + } + + pub fn get_rate_max(&self) -> Result { + let mut v = 0; + // Note on the null ptr: if this ptr is not null, then the value behind it is replaced with + // -1 if the suprenum is not in the set (i.e. it's an open range), 0 otherwise. This could + // be returned along with the value, but it's safe to pass a null ptr in, in which case the + // pointer is not dereferenced. + acheck!(snd_pcm_hw_params_get_rate_max(self.0, &mut v, ptr::null_mut())).map(|_| v as u32) + } + + pub fn get_rate_min(&self) -> Result { + let mut v = 0; + // Note on the null ptr: see get_rate_max but read +1 and infinum instead of -1 and + // suprenum. + acheck!(snd_pcm_hw_params_get_rate_min(self.0, &mut v, ptr::null_mut())).map(|_| v as u32) + } + + pub fn test_rate(&self, rate: u32) -> Result<()> { + acheck!(snd_pcm_hw_params_test_rate((self.1).0, self.0, rate as c_uint, 0)).map(|_| ()) + } + + pub fn set_format(&self, v: Format) -> Result<()> { + acheck!(snd_pcm_hw_params_set_format((self.1).0, self.0, v as c_int)).map(|_| ()) + } + + pub fn get_format(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_format(self.0, &mut v)) + .and_then(|_| Format::from_c_int(v, "snd_pcm_hw_params_get_format")) + } + + pub fn test_format(&self, v: Format) -> Result<()> { + acheck!(snd_pcm_hw_params_test_format((self.1).0, self.0, v as c_int)).map(|_| ()) + } + + pub fn set_access(&self, v: Access) -> Result<()> { + acheck!(snd_pcm_hw_params_set_access((self.1).0, self.0, v as c_uint)).map(|_| ()) + } + + pub fn get_access(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_access(self.0, &mut v)) + .and_then(|_| Access::from_c_int(v as c_int, "snd_pcm_hw_params_get_access")) + } + + pub fn set_period_size_near(&self, v: Frames, dir: ValueOr) -> Result { + let mut d = dir as c_int; + let mut r = v as alsa::snd_pcm_uframes_t; + acheck!(snd_pcm_hw_params_set_period_size_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as Frames) + } + + pub fn set_period_size(&self, v: Frames, dir: ValueOr) -> Result<()> { + acheck!(snd_pcm_hw_params_set_period_size((self.1).0, self.0, v as alsa::snd_pcm_uframes_t, dir as c_int)).map(|_| ()) + } + + pub fn set_period_time_near(&self, v: u32, dir: ValueOr) -> Result { + let mut d = dir as c_int; + let mut r = v as c_uint; + acheck!(snd_pcm_hw_params_set_period_time_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as u32) + } + + pub fn get_period_size(&self) -> Result { + let (mut v, mut d) = (0,0); + acheck!(snd_pcm_hw_params_get_period_size(self.0, &mut v, &mut d)).map(|_| v as Frames) + } + + pub fn get_period_size_min(&self) -> Result { + let (mut v, mut d) = (0,0); + acheck!(snd_pcm_hw_params_get_period_size_min(self.0, &mut v, &mut d)).map(|_| v as Frames) + } + + pub fn get_period_size_max(&self) -> Result { + let (mut v, mut d) = (0,0); + acheck!(snd_pcm_hw_params_get_period_size_max(self.0, &mut v, &mut d)).map(|_| v as Frames) + } + + pub fn set_periods(&self, v: u32, dir: ValueOr) -> Result<()> { + acheck!(snd_pcm_hw_params_set_periods((self.1).0, self.0, v as c_uint, dir as c_int)).map(|_| ()) + } + + pub fn get_periods(&self) -> Result { + let (mut v, mut d) = (0,0); + acheck!(snd_pcm_hw_params_get_periods(self.0, &mut v, &mut d)).map(|_| v as u32) + } + + pub fn set_buffer_size_near(&self, v: Frames) -> Result { + let mut r = v as alsa::snd_pcm_uframes_t; + acheck!(snd_pcm_hw_params_set_buffer_size_near((self.1).0, self.0, &mut r)).map(|_| r as Frames) + } + + pub fn set_buffer_size_max(&self, v: Frames) -> Result { + let mut r = v as alsa::snd_pcm_uframes_t; + acheck!(snd_pcm_hw_params_set_buffer_size_max((self.1).0, self.0, &mut r)).map(|_| r as Frames) + } + + pub fn set_buffer_size_min(&self, v: Frames) -> Result { + let mut r = v as alsa::snd_pcm_uframes_t; + acheck!(snd_pcm_hw_params_set_buffer_size_min((self.1).0, self.0, &mut r)).map(|_| r as Frames) + } + + pub fn set_buffer_size(&self, v: Frames) -> Result<()> { + acheck!(snd_pcm_hw_params_set_buffer_size((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) + } + + pub fn set_buffer_time_near(&self, v: u32, dir: ValueOr) -> Result { + let mut d = dir as c_int; + let mut r = v as c_uint; + acheck!(snd_pcm_hw_params_set_buffer_time_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as u32) + } + + pub fn get_buffer_size(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_buffer_size(self.0, &mut v)).map(|_| v as Frames) + } + + pub fn get_buffer_size_min(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_buffer_size_min(self.0, &mut v)).map(|_| v as Frames) + } + + pub fn get_buffer_size_max(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_hw_params_get_buffer_size_max(self.0, &mut v)).map(|_| v as Frames) + } + + pub fn get_buffer_time_min(&self) -> Result { + let (mut v, mut d) = (0,0); + acheck!(snd_pcm_hw_params_get_buffer_time_min(self.0, &mut v, &mut d)).map(|_| v as u32) + } + + pub fn get_buffer_time_max(&self) -> Result { + let (mut v, mut d) = (0,0); + acheck!(snd_pcm_hw_params_get_buffer_time_max(self.0, &mut v, &mut d)).map(|_| v as u32) + } + + /// Returns true if the alsa stream can be paused, false if not. + /// + /// This function should only be called when the configuration space contains a single + /// configuration. Call `PCM::hw_params` to choose a single configuration from the + /// configuration space. + pub fn can_pause(&self) -> bool { + unsafe { alsa::snd_pcm_hw_params_can_pause(self.0) != 0 } + } + + /// Returns true if the alsa stream can be resumed, false if not. + /// + /// This function should only be called when the configuration space contains a single + /// configuration. Call `PCM::hw_params` to choose a single configuration from the + /// configuration space. + pub fn can_resume(&self) -> bool { + unsafe { alsa::snd_pcm_hw_params_can_resume(self.0) != 0 } + } + + /// Returns true if the alsa stream supports the provided `AudioTstampType`, false if not. + /// + /// This function should only be called when the configuration space contains a single + /// configuration. Call `PCM::hw_params` to choose a single configuration from the + /// configuration space. + pub fn supports_audio_ts_type(&self, type_: AudioTstampType) -> bool { + unsafe { alsa::snd_pcm_hw_params_supports_audio_ts_type(self.0, type_ as libc::c_int) != 0 } + } + + pub fn dump(&self, o: &mut Output) -> Result<()> { + acheck!(snd_pcm_hw_params_dump(self.0, super::io::output_handle(o))).map(|_| ()) + } + + pub fn copy_from(&mut self, other: &HwParams<'a>) { + self.1 = other.1; + unsafe { alsa::snd_pcm_hw_params_copy(self.0, other.0) }; + } +} + +impl<'a> Clone for HwParams<'a> { + fn clone(&self) -> HwParams<'a> { + let mut r = HwParams::new(self.1).unwrap(); + r.copy_from(self); + r + } +} + +impl<'a> fmt::Debug for HwParams<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("HwParams") + .field("channels", &self.get_channels()) + .field("rate", &format!("{:?} Hz", self.get_rate())) + .field("format", &self.get_format()) + .field("access", &self.get_access()) + .field("period_size", &format!("{:?} frames", self.get_period_size())) + .field("buffer_size", &format!("{:?} frames", self.get_buffer_size())) + .finish() + } +} + +/// [snd_pcm_sw_params_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html) wrapper +pub struct SwParams<'a>(*mut alsa::snd_pcm_sw_params_t, &'a PCM); + +impl<'a> Drop for SwParams<'a> { + fn drop(&mut self) { unsafe { alsa::snd_pcm_sw_params_free(self.0) }; } +} + +impl<'a> SwParams<'a> { + + fn new(a: &'a PCM) -> Result> { + let mut p = ptr::null_mut(); + acheck!(snd_pcm_sw_params_malloc(&mut p)).map(|_| SwParams(p, a)) + } + + pub fn set_avail_min(&self, v: Frames) -> Result<()> { + acheck!(snd_pcm_sw_params_set_avail_min((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) + } + + pub fn get_avail_min(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_sw_params_get_avail_min(self.0, &mut v)).map(|_| v as Frames) + } + + pub fn get_boundary(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_sw_params_get_boundary(self.0, &mut v)).map(|_| v as Frames) + } + + pub fn set_start_threshold(&self, v: Frames) -> Result<()> { + acheck!(snd_pcm_sw_params_set_start_threshold((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) + } + + pub fn get_start_threshold(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_sw_params_get_start_threshold(self.0, &mut v)).map(|_| v as Frames) + } + + pub fn set_stop_threshold(&self, v: Frames) -> Result<()> { + acheck!(snd_pcm_sw_params_set_stop_threshold((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ()) + } + + pub fn get_stop_threshold(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_sw_params_get_stop_threshold(self.0, &mut v)).map(|_| v as Frames) + } + + pub fn set_tstamp_mode(&self, v: bool) -> Result<()> { + let z = if v { alsa::SND_PCM_TSTAMP_ENABLE } else { alsa::SND_PCM_TSTAMP_NONE }; + acheck!(snd_pcm_sw_params_set_tstamp_mode((self.1).0, self.0, z)).map(|_| ()) + } + + pub fn get_tstamp_mode(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_sw_params_get_tstamp_mode(self.0, &mut v)).map(|_| v != 0) + } + + pub fn set_tstamp_type(&self, v: TstampType) -> Result<()> { + acheck!(snd_pcm_sw_params_set_tstamp_type((self.1).0, self.0, v as u32)).map(|_| ()) + } + + pub fn get_tstamp_type(&self) -> Result { + let mut v = 0; + acheck!(snd_pcm_sw_params_get_tstamp_type(self.0, &mut v))?; + TstampType::from_c_int(v as c_int, "snd_pcm_sw_params_get_tstamp_type") + } + + pub fn dump(&self, o: &mut Output) -> Result<()> { + acheck!(snd_pcm_sw_params_dump(self.0, super::io::output_handle(o))).map(|_| ()) + } +} + +impl<'a> fmt::Debug for SwParams<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, + "SwParams(avail_min: {:?} frames, start_threshold: {:?} frames, stop_threshold: {:?} frames)", + self.get_avail_min(), self.get_start_threshold(), self.get_stop_threshold()) + } +} + +const STATUS_SIZE: usize = 152; + +/// [snd_pcm_status_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___status.html) wrapper +pub struct Status([u8; STATUS_SIZE]); + +impl Status { + fn new() -> Status { + assert!(unsafe { alsa::snd_pcm_status_sizeof() } as usize <= STATUS_SIZE); + Status([0; STATUS_SIZE]) + } + + fn ptr(&self) -> *mut alsa::snd_pcm_status_t { self.0.as_ptr() as *const _ as *mut alsa::snd_pcm_status_t } + + pub fn get_htstamp(&self) -> timespec { + let mut h = timespec {tv_sec: 0, tv_nsec: 0}; + unsafe { alsa::snd_pcm_status_get_htstamp(self.ptr(), &mut h) }; + h + } + + pub fn get_trigger_htstamp(&self) -> timespec { + let mut h = timespec {tv_sec: 0, tv_nsec: 0}; + unsafe { alsa::snd_pcm_status_get_trigger_htstamp(self.ptr(), &mut h) }; + h + } + + pub fn get_audio_htstamp(&self) -> timespec { + let mut h = timespec {tv_sec: 0, tv_nsec: 0}; + unsafe { alsa::snd_pcm_status_get_audio_htstamp(self.ptr(), &mut h) }; + h + } + + pub fn get_state(&self) -> State { State::from_c_int( + unsafe { alsa::snd_pcm_status_get_state(self.ptr()) } as c_int, "snd_pcm_status_get_state").unwrap() } + + pub fn get_avail(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_avail(self.ptr()) as Frames }} + pub fn get_delay(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_delay(self.ptr()) }} + pub fn get_avail_max(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_avail_max(self.ptr()) as Frames }} + pub fn get_overrange(&self) -> Frames { unsafe { alsa::snd_pcm_status_get_overrange(self.ptr()) as Frames }} + + pub fn dump(&self, o: &mut Output) -> Result<()> { + acheck!(snd_pcm_status_dump(self.ptr(), super::io::output_handle(o))).map(|_| ()) + } +} + +/// Builder for [`Status`]. +/// +/// Allows setting the audio timestamp configuration before retrieving the +/// status from the stream. +pub struct StatusBuilder(Status); + +impl StatusBuilder { + pub fn new() -> Self { + StatusBuilder(Status::new()) + } + + pub fn audio_htstamp_config( + self, + type_requested: AudioTstampType, + report_delay: bool, + ) -> Self { + let mut cfg: alsa::snd_pcm_audio_tstamp_config_t = unsafe { std::mem::zeroed() }; + cfg.set_type_requested(type_requested as _); + cfg.set_report_delay(report_delay as _); + unsafe { alsa::snd_pcm_status_set_audio_htstamp_config(self.0.ptr(), &mut cfg) }; + self + } + + pub fn build(self, pcm: &PCM) -> Result { + acheck!(snd_pcm_status(pcm.0, self.0.ptr())).map(|_| self.0) + } +} + +alsa_enum!( + #[non_exhaustive] + /// [SND_PCM_AUDIO_TSTAMP_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants + AudioTstampType, ALL_AUDIO_TSTAMP_TYPES[6], + + Compat = SND_PCM_AUDIO_TSTAMP_TYPE_COMPAT, + Default = SND_PCM_AUDIO_TSTAMP_TYPE_DEFAULT, + Link = SND_PCM_AUDIO_TSTAMP_TYPE_LINK, + LinkAbsolute = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE, + LinkEstimated = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED, + LinkSynchronized = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED, +); + +#[test] +fn info_from_default() { + use std::ffi::CString; + let pcm = PCM::open(&*CString::new("default").unwrap(), Direction::Capture, false).unwrap(); + let info = pcm.info().unwrap(); + println!("PCM Info:"); + println!("\tCard: {}", info.get_card()); + println!("\tDevice: {}", info.get_device()); + println!("\tSubdevice: {}", info.get_subdevice()); + println!("\tId: {}", info.get_id().unwrap()); + println!("\tName: {}", info.get_name().unwrap()); + println!("\tSubdevice Name: {}", info.get_subdevice_name().unwrap()); +} + +#[test] +fn drop() { + use std::ffi::CString; + let pcm = PCM::open(&*CString::new("default").unwrap(), Direction::Capture, false).unwrap(); + // Verify that this does not cause a naming conflict (issue #14) + let _ = pcm.drop(); +} + +#[test] +fn record_from_default() { + use std::ffi::CString; + let pcm = PCM::open(&*CString::new("default").unwrap(), Direction::Capture, false).unwrap(); + let hwp = HwParams::any(&pcm).unwrap(); + hwp.set_channels(2).unwrap(); + hwp.set_rate(44100, ValueOr::Nearest).unwrap(); + hwp.set_format(Format::s16()).unwrap(); + hwp.set_access(Access::RWInterleaved).unwrap(); + pcm.hw_params(&hwp).unwrap(); + pcm.start().unwrap(); + let mut buf = [0i16; 1024]; + assert_eq!(pcm.io_i16().unwrap().readi(&mut buf).unwrap(), 1024/2); +} + +#[test] +fn playback_to_default() { + use std::ffi::CString; + let pcm = PCM::open(&*CString::new("default").unwrap(), Direction::Playback, false).unwrap(); + let hwp = HwParams::any(&pcm).unwrap(); + hwp.set_channels(1).unwrap(); + hwp.set_rate(44100, ValueOr::Nearest).unwrap(); + hwp.set_format(Format::s16()).unwrap(); + hwp.set_access(Access::RWInterleaved).unwrap(); + pcm.hw_params(&hwp).unwrap(); + + let hwp = pcm.hw_params_current().unwrap(); + let swp = pcm.sw_params_current().unwrap(); + swp.set_start_threshold(hwp.get_buffer_size().unwrap()).unwrap(); + pcm.sw_params(&swp).unwrap(); + + println!("PCM status: {:?}, {:?}", pcm.state(), pcm.hw_params_current().unwrap()); + let mut outp = Output::buffer_open().unwrap(); + pcm.dump(&mut outp).unwrap(); + println!("== PCM dump ==\n{}", outp); + + let mut buf = [0i16; 1024]; + for (i, a) in buf.iter_mut().enumerate() { + *a = ((i as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16 + } + let io = pcm.io_i16().unwrap(); + for _ in 0..2*44100/1024 { // 2 seconds of playback + println!("PCM state: {:?}", pcm.state()); + assert_eq!(io.writei(&buf[..]).unwrap(), 1024); + } + if pcm.state() != State::Running { pcm.start().unwrap() }; + + let mut outp2 = Output::buffer_open().unwrap(); + pcm.status().unwrap().dump(&mut outp2).unwrap(); + println!("== PCM status dump ==\n{}", outp2); + + pcm.drain().unwrap(); +} + +#[test] +fn print_sizeof() { + let s = unsafe { alsa::snd_pcm_status_sizeof() } as usize; + println!("Status size: {}", s); + + assert!(s <= STATUS_SIZE); +} diff --git a/alsa/src/poll.rs b/alsa/src/poll.rs new file mode 100644 index 0000000..3e91911 --- /dev/null +++ b/alsa/src/poll.rs @@ -0,0 +1,68 @@ +//! Tiny poll ffi +//! +//! A tiny wrapper around libc's poll system call. + +use libc; +use super::error::*; +use std::io; +pub use libc::pollfd; + + +bitflags! { + pub struct Flags: ::libc::c_short { + const IN = ::libc::POLLIN; + const PRI = ::libc::POLLPRI; + const OUT = ::libc::POLLOUT; + const ERR = ::libc::POLLERR; + const HUP = ::libc::POLLHUP; + const NVAL = ::libc::POLLNVAL; + } +} + +pub trait Descriptors { + fn count(&self) -> usize; + fn fill(&self, _: &mut [pollfd]) -> Result; + fn revents(&self, _: &[pollfd]) -> Result; + + /// Wrapper around count and fill - returns an array of pollfds + fn get(&self) -> Result> { + let mut v = vec![pollfd { fd: 0, events: 0, revents: 0 }; self.count()]; + if self.fill(&mut v)? != v.len() { Err(Error::unsupported("did not fill the poll descriptors array")) } + else { Ok(v) } + } +} + +impl Descriptors for pollfd { + fn count(&self) -> usize { 1 } + fn fill(&self, a: &mut [pollfd]) -> Result { a[0] = *self; Ok(1) } + fn revents(&self, a: &[pollfd]) -> Result { Ok(Flags::from_bits_truncate(a[0].revents)) } +} + +/// Wrapper around the libc poll call. +pub fn poll(fds: &mut[pollfd], timeout: i32) -> Result { + let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::nfds_t, timeout as libc::c_int) }; + if r >= 0 { Ok(r as usize) } else { + from_code("poll", -io::Error::last_os_error().raw_os_error().unwrap()).map(|_| unreachable!()) + } +} + +/// Builds a pollfd array, polls it, and returns the poll descriptors which have non-zero revents. +pub fn poll_all<'a>(desc: &[&'a dyn Descriptors], timeout: i32) -> Result> { + + let mut pollfds: Vec = vec!(); + let mut indices = vec!(); + for v2 in desc.iter().map(|q| q.get()) { + let v = v2?; + indices.push(pollfds.len() .. pollfds.len()+v.len()); + pollfds.extend(v); + }; + + poll(&mut pollfds, timeout)?; + + let mut res = vec!(); + for (i, r) in indices.into_iter().enumerate() { + let z = desc[i].revents(&pollfds[r])?; + if !z.is_empty() { res.push((desc[i], z)); } + } + Ok(res) +} diff --git a/alsa/src/rawmidi.rs b/alsa/src/rawmidi.rs new file mode 100644 index 0000000..50ef9cc --- /dev/null +++ b/alsa/src/rawmidi.rs @@ -0,0 +1,211 @@ +//! MIDI devices I/O and enumeration + +use libc::{c_int, c_uint, c_void, size_t, c_short, pollfd}; +use super::ctl_int::{ctl_ptr, Ctl}; +use super::{Direction, poll}; +use super::error::*; +use crate::alsa; +use std::{ptr, io}; +use std::ffi::{CStr, CString}; + +/// Iterator over [Rawmidi](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) devices and subdevices +pub struct Iter<'a> { + ctl: &'a Ctl, + device: c_int, + in_count: i32, + out_count: i32, + current: i32, +} + +/// [snd_rawmidi_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper +pub struct Info(*mut alsa::snd_rawmidi_info_t); + +impl Drop for Info { + fn drop(&mut self) { unsafe { alsa::snd_rawmidi_info_free(self.0) }; } +} + +impl Info { + fn new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_rawmidi_info_malloc(&mut p)).map(|_| Info(p)) + } + + fn from_iter(c: &Ctl, device: i32, sub: i32, dir: Direction) -> Result { + let r = Info::new()?; + unsafe { alsa::snd_rawmidi_info_set_device(r.0, device as c_uint) }; + let d = match dir { + Direction::Playback => alsa::SND_RAWMIDI_STREAM_OUTPUT, + Direction::Capture => alsa::SND_RAWMIDI_STREAM_INPUT, + }; + unsafe { alsa::snd_rawmidi_info_set_stream(r.0, d) }; + unsafe { alsa::snd_rawmidi_info_set_subdevice(r.0, sub as c_uint) }; + acheck!(snd_ctl_rawmidi_info(ctl_ptr(c), r.0)).map(|_| r) + } + + fn subdev_count(c: &Ctl, device: c_int) -> Result<(i32, i32)> { + let i = Info::from_iter(c, device, 0, Direction::Capture)?; + let o = Info::from_iter(c, device, 0, Direction::Playback)?; + Ok((unsafe { alsa::snd_rawmidi_info_get_subdevices_count(o.0) as i32 }, + unsafe { alsa::snd_rawmidi_info_get_subdevices_count(i.0) as i32 })) + } + + pub fn get_device(&self) -> i32 { unsafe { alsa::snd_rawmidi_info_get_device(self.0) as i32 }} + pub fn get_subdevice(&self) -> i32 { unsafe { alsa::snd_rawmidi_info_get_subdevice(self.0) as i32 }} + pub fn get_stream(&self) -> super::Direction { + if unsafe { alsa::snd_rawmidi_info_get_stream(self.0) } == alsa::SND_RAWMIDI_STREAM_OUTPUT { super::Direction::Playback } + else { super::Direction::Capture } + } + + pub fn get_subdevice_name(&self) -> Result { + let c = unsafe { alsa::snd_rawmidi_info_get_subdevice_name(self.0) }; + from_const("snd_rawmidi_info_get_subdevice_name", c).map(|s| s.to_string()) + } + pub fn get_id(&self) -> Result { + let c = unsafe { alsa::snd_rawmidi_info_get_id(self.0) }; + from_const("snd_rawmidi_info_get_id", c).map(|s| s.to_string()) + } +} + +/// [snd_rawmidi_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper +pub struct Status(*mut alsa::snd_rawmidi_status_t); + +impl Status { + fn new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_rawmidi_status_malloc(&mut p)).map(|_| Status(p)) + } +} + +impl Status { + pub fn get_avail(&self) -> usize { unsafe { alsa::snd_rawmidi_status_get_avail(self.0 as *const _) } } + pub fn get_xruns(&self) -> usize { unsafe { alsa::snd_rawmidi_status_get_xruns(self.0 as *const _) } } +} + +impl Drop for Status { + fn drop(&mut self) { unsafe { alsa::snd_rawmidi_status_free(self.0) }; } +} + + +impl<'a> Iter<'a> { + pub fn new(c: &'a Ctl) -> Iter<'a> { Iter { ctl: c, device: -1, in_count: 0, out_count: 0, current: 0 }} +} + +impl<'a> Iterator for Iter<'a> { + type Item = Result; + fn next(&mut self) -> Option> { + if self.current < self.in_count { + self.current += 1; + return Some(Info::from_iter(self.ctl, self.device, self.current-1, Direction::Capture)); + } + if self.current - self.in_count < self.out_count { + self.current += 1; + return Some(Info::from_iter(self.ctl, self.device, self.current-1-self.in_count, Direction::Playback)); + } + + let r = acheck!(snd_ctl_rawmidi_next_device(ctl_ptr(self.ctl), &mut self.device)); + match r { + Err(e) => return Some(Err(e)), + Ok(_) if self.device == -1 => return None, + _ => {}, + } + self.current = 0; + match Info::subdev_count(self.ctl, self.device) { + Err(e) => Some(Err(e)), + Ok((oo, ii)) => { + self.in_count = ii; + self.out_count = oo; + self.next() + } + } + } +} + +/// [snd_rawmidi_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper +pub struct Rawmidi(*mut alsa::snd_rawmidi_t); + +unsafe impl Send for Rawmidi {} + +impl Drop for Rawmidi { + fn drop(&mut self) { unsafe { alsa::snd_rawmidi_close(self.0) }; } +} + +impl Rawmidi { + + /// Wrapper around open that takes a &str instead of a &CStr + pub fn new(name: &str, dir: Direction, nonblock: bool) -> Result { + Self::open(&CString::new(name).unwrap(), dir, nonblock) + } + + pub fn open(name: &CStr, dir: Direction, nonblock: bool) -> Result { + let mut h = ptr::null_mut(); + let flags = if nonblock { 2 } else { 0 }; // FIXME: alsa::SND_RAWMIDI_NONBLOCK does not exist in alsa-sys + acheck!(snd_rawmidi_open( + if dir == Direction::Capture { &mut h } else { ptr::null_mut() }, + if dir == Direction::Playback { &mut h } else { ptr::null_mut() }, + name.as_ptr(), flags)) + .map(|_| Rawmidi(h)) + } + + pub fn info(&self) -> Result { + Info::new().and_then(|i| acheck!(snd_rawmidi_info(self.0, i.0)).map(|_| i)) + } + + pub fn status(&self) -> Result { + Status::new().and_then(|i| acheck!(snd_rawmidi_status(self.0, i.0)).map(|_| i)) + } + + pub fn drop(&self) -> Result<()> { acheck!(snd_rawmidi_drop(self.0)).map(|_| ()) } + pub fn drain(&self) -> Result<()> { acheck!(snd_rawmidi_drain(self.0)).map(|_| ()) } + pub fn name(&self) -> Result { + let c = unsafe { alsa::snd_rawmidi_name(self.0) }; + from_const("snd_rawmidi_name", c).map(|s| s.to_string()) + } + + pub fn io(&self) -> IO { IO(self) } +} + +impl poll::Descriptors for Rawmidi { + fn count(&self) -> usize { + unsafe { alsa::snd_rawmidi_poll_descriptors_count(self.0) as usize } + } + fn fill(&self, p: &mut [pollfd]) -> Result { + let z = unsafe { alsa::snd_rawmidi_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) }; + from_code("snd_rawmidi_poll_descriptors", z).map(|_| z as usize) + } + fn revents(&self, p: &[pollfd]) -> Result { + let mut r = 0; + let z = unsafe { alsa::snd_rawmidi_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) }; + from_code("snd_rawmidi_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short)) + } +} + +/// Implements `std::io::Read` and `std::io::Write` for `Rawmidi` +pub struct IO<'a>(&'a Rawmidi); + +impl<'a> io::Read for IO<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let r = unsafe { alsa::snd_rawmidi_read((self.0).0, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) }; + if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) } + else { Ok(r as usize) } + } +} + +impl<'a> io::Write for IO<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + let r = unsafe { alsa::snd_rawmidi_write((self.0).0, buf.as_ptr() as *const c_void, buf.len() as size_t) }; + if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) } + else { Ok(r as usize) } + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} + + +#[test] +fn print_rawmidis() { + for a in super::card::Iter::new().map(|a| a.unwrap()) { + for b in Iter::new(&Ctl::from_card(&a, false).unwrap()).map(|b| b.unwrap()) { + println!("Rawmidi {:?} (hw:{},{},{}) {} - {}", b.get_stream(), a.get_index(), b.get_device(), b.get_subdevice(), + a.get_name().unwrap(), b.get_subdevice_name().unwrap()) + } + } +} diff --git a/alsa/src/seq.rs b/alsa/src/seq.rs new file mode 100644 index 0000000..bb1e46d --- /dev/null +++ b/alsa/src/seq.rs @@ -0,0 +1,1567 @@ +//! MIDI sequencer I/O and enumeration + +use libc::{c_uint, c_int, c_short, c_uchar, c_void, c_long, size_t, pollfd}; +use super::error::*; +use crate::alsa; +use super::{Direction, poll}; +use std::{ptr, fmt, mem, slice, time, cell}; +use std::str::{FromStr, Split}; +use std::ffi::{CStr}; +use std::borrow::Cow; + +// Workaround for improper alignment of snd_seq_ev_ext_t in alsa-sys +#[repr(packed)] +struct EvExtPacked { + len: c_uint, + ptr: *mut c_void, +} + +/// [snd_seq_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___sequencer.html) wrapper +/// +/// To access the functions `event_input`, `event_input_pending` and `set_input_buffer_size`, +/// you first have to obtain an instance of `Input` by calling `input()`. Only one instance of +/// `Input` may exist at any time for a given `Seq`. +pub struct Seq(*mut alsa::snd_seq_t, cell::Cell); + +unsafe impl Send for Seq {} + +impl Drop for Seq { + fn drop(&mut self) { unsafe { alsa::snd_seq_close(self.0) }; } +} + +impl Seq { + fn check_has_input(&self) { + if self.1.get() { panic!("No additional Input object allowed")} + } + + /// Opens the sequencer. + /// + /// If name is None, "default" will be used. That's almost always what you usually want to use anyway. + pub fn open(name: Option<&CStr>, dir: Option, nonblock: bool) -> Result { + let n2 = name.unwrap_or(unsafe { CStr::from_bytes_with_nul_unchecked(b"default\0") }); + let mut h = ptr::null_mut(); + let mode = if nonblock { alsa::SND_SEQ_NONBLOCK } else { 0 }; + let streams = match dir { + None => alsa::SND_SEQ_OPEN_DUPLEX, + Some(Direction::Playback) => alsa::SND_SEQ_OPEN_OUTPUT, + Some(Direction::Capture) => alsa::SND_SEQ_OPEN_INPUT, + }; + acheck!(snd_seq_open(&mut h, n2.as_ptr(), streams, mode)) + .map(|_| Seq(h, cell::Cell::new(false))) + } + + pub fn set_client_name(&self, name: &CStr) -> Result<()> { + acheck!(snd_seq_set_client_name(self.0, name.as_ptr())).map(|_| ()) + } + + pub fn set_client_event_filter(&self, event_type: i32) -> Result<()> { + acheck!(snd_seq_set_client_event_filter(self.0, event_type as c_int)).map(|_| ()) + } + + pub fn set_client_pool_output(&self, size: u32) -> Result<()> { + acheck!(snd_seq_set_client_pool_output(self.0, size as size_t)).map(|_| ()) + } + + pub fn set_client_pool_input(&self, size: u32) -> Result<()> { + acheck!(snd_seq_set_client_pool_input(self.0, size as size_t)).map(|_| ()) + } + + pub fn set_client_pool_output_room(&self, size: u32) -> Result<()> { + acheck!(snd_seq_set_client_pool_output_room(self.0, size as size_t)).map(|_| ()) + } + + pub fn client_id(&self) -> Result { + acheck!(snd_seq_client_id(self.0)).map(|q| q as i32) + } + + pub fn drain_output(&self) -> Result { + acheck!(snd_seq_drain_output(self.0)).map(|q| q as i32) + } + + pub fn get_any_client_info(&self, client: i32) -> Result { + let c = ClientInfo::new()?; + acheck!(snd_seq_get_any_client_info(self.0, client, c.0)).map(|_| c) + } + + pub fn get_any_port_info(&self, a: Addr) -> Result { + let c = PortInfo::new()?; + acheck!(snd_seq_get_any_port_info(self.0, a.client as c_int, a.port as c_int, c.0)).map(|_| c) + } + + pub fn create_port(&self, port: &PortInfo) -> Result<()> { + acheck!(snd_seq_create_port(self.0, port.0)).map(|_| ()) + } + + pub fn create_simple_port(&self, name: &CStr, caps: PortCap, t: PortType) -> Result { + acheck!(snd_seq_create_simple_port(self.0, name.as_ptr(), caps.bits() as c_uint, t.bits() as c_uint)).map(|q| q as i32) + } + + pub fn set_port_info(&self, port: i32, info: &mut PortInfo) -> Result<()> { + acheck!(snd_seq_set_port_info(self.0, port, info.0)).map(|_| ()) + } + + pub fn delete_port(&self, port: i32) -> Result<()> { + acheck!(snd_seq_delete_port(self.0, port as c_int)).map(|_| ()) + } + + pub fn subscribe_port(&self, info: &PortSubscribe) -> Result<()> { + acheck!(snd_seq_subscribe_port(self.0, info.0)).map(|_| ()) + } + + pub fn unsubscribe_port(&self, sender: Addr, dest: Addr) -> Result<()> { + let z = PortSubscribe::new()?; + z.set_sender(sender); + z.set_dest(dest); + acheck!(snd_seq_unsubscribe_port(self.0, z.0)).map(|_| ()) + } + + pub fn control_queue(&self, q: i32, t: EventType, value: i32, e: Option<&mut Event>) -> Result<()> { + assert!(EvQueueControl::<()>::has_data(t) || EvQueueControl::::has_data(t) || EvQueueControl::::has_data(t)); + let p = e.map(|e| &mut e.0 as *mut _).unwrap_or(ptr::null_mut()); + acheck!(snd_seq_control_queue(self.0, q as c_int, t as c_int, value as c_int, p)).map(|_| ()) + } + + pub fn event_output(&self, e: &mut Event) -> Result { + e.ensure_buf(); + acheck!(snd_seq_event_output(self.0, &mut e.0)).map(|q| q as u32) + } + + pub fn event_output_buffer(&self, e: &mut Event) -> Result { + e.ensure_buf(); + acheck!(snd_seq_event_output_buffer(self.0, &mut e.0)).map(|q| q as u32) + } + + pub fn event_output_direct(&self, e: &mut Event) -> Result { + e.ensure_buf(); + acheck!(snd_seq_event_output_direct(self.0, &mut e.0)).map(|q| q as u32) + } + + pub fn get_queue_tempo(&self, q: i32) -> Result { + let value = QueueTempo::new()?; + acheck!(snd_seq_get_queue_tempo(self.0, q as c_int, value.0)).map(|_| value) + } + + pub fn set_queue_tempo(&self, q: i32, value: &QueueTempo) -> Result<()> { + acheck!(snd_seq_set_queue_tempo(self.0, q as c_int, value.0)).map(|_| ()) + } + + pub fn get_queue_status(&self, q: i32) -> Result { + let value = QueueStatus::new()?; + acheck!(snd_seq_get_queue_status(self.0, q as c_int, value.0)).map(|_| value) + } + + pub fn free_queue(&self, q: i32) -> Result<()> { acheck!(snd_seq_free_queue(self.0, q)).map(|_| ()) } + pub fn alloc_queue(&self) -> Result { acheck!(snd_seq_alloc_queue(self.0)).map(|q| q as i32) } + pub fn alloc_named_queue(&self, n: &CStr) -> Result { + acheck!(snd_seq_alloc_named_queue(self.0, n.as_ptr())).map(|q| q as i32) + } + + pub fn sync_output_queue(&self) -> Result<()> { + acheck!(snd_seq_sync_output_queue(self.0)).map(|_| ()) + } + + pub fn drop_output(&self) -> Result<()> { + acheck!(snd_seq_drop_output(self.0)).map(|_| ()) + } + + /// Call this function to obtain an instance of `Input` to access the functions `event_input`, + /// `event_input_pending` and `set_input_buffer_size`. See the documentation of `Input` for details. + pub fn input(&self) -> Input { + Input::new(self) + } + + pub fn remove_events(&self, condition: RemoveEvents) -> Result<()> { + acheck!(snd_seq_remove_events(self.0, condition.0)).map(|_| ()) + } +} + +/// Struct for receiving input events from a sequencer. The methods offered by this +/// object may modify the internal input buffer of the sequencer, which must not happen +/// while an `Event` is alive that has been obtained from a call to `event_input` (which +/// takes `Input` by mutable reference for this reason). This is because the event might +/// directly reference the sequencer's input buffer for variable-length messages (e.g. Sysex). +/// +/// Note: Only one `Input` object is allowed in scope at a time. +pub struct Input<'a>(&'a Seq); + +impl<'a> Drop for Input<'a> { + fn drop(&mut self) { (self.0).1.set(false) } +} + +impl<'a> Input<'a> { + fn new(s: &'a Seq) -> Input<'a> { + s.check_has_input(); + s.1.set(true); + Input(s) + } + + pub fn event_input(&mut self) -> Result { + // The returned event might reference the input buffer of the `Seq`. + // Therefore we mutably borrow the `Input` structure, preventing any + // other function call that might change the input buffer while the + // event is alive. + let mut z = ptr::null_mut(); + acheck!(snd_seq_event_input((self.0).0, &mut z))?; + unsafe { Event::extract (&mut *z, "snd_seq_event_input") } + } + + pub fn event_input_pending(&self, fetch_sequencer: bool) -> Result { + acheck!(snd_seq_event_input_pending((self.0).0, if fetch_sequencer {1} else {0})).map(|q| q as u32) + } + + pub fn set_input_buffer_size(&self, size: u32) -> Result<()> { + acheck!(snd_seq_set_input_buffer_size((self.0).0, size as size_t)).map(|_| ()) + } + + pub fn drop_input(&self) -> Result<()> { + acheck!(snd_seq_drop_input((self.0).0)).map(|_| ()) + } +} + +fn polldir(o: Option) -> c_short { + match o { + None => poll::Flags::IN | poll::Flags::OUT, + Some(Direction::Playback) => poll::Flags::OUT, + Some(Direction::Capture) => poll::Flags::IN, + }.bits() +} + +impl<'a> poll::Descriptors for (&'a Seq, Option) { + + fn count(&self) -> usize { + unsafe { alsa::snd_seq_poll_descriptors_count((self.0).0, polldir(self.1)) as usize } + } + + fn fill(&self, p: &mut [pollfd]) -> Result { + let z = unsafe { alsa::snd_seq_poll_descriptors((self.0).0, p.as_mut_ptr(), p.len() as c_uint, polldir(self.1)) }; + from_code("snd_seq_poll_descriptors", z).map(|_| z as usize) + } + + fn revents(&self, p: &[pollfd]) -> Result { + let mut r = 0; + let z = unsafe { alsa::snd_seq_poll_descriptors_revents((self.0).0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) }; + from_code("snd_seq_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short)) + } +} + +/// [snd_seq_client_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_client.html) wrapper +pub struct ClientInfo(*mut alsa::snd_seq_client_info_t); + +unsafe impl Send for ClientInfo {} + +impl Drop for ClientInfo { + fn drop(&mut self) { + unsafe { alsa::snd_seq_client_info_free(self.0) }; + } +} + +impl ClientInfo { + fn new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_seq_client_info_malloc(&mut p)).map(|_| ClientInfo(p)) + } + + // Not sure if it's useful for this one to be public. + fn set_client(&self, client: i32) { + unsafe { alsa::snd_seq_client_info_set_client(self.0, client as c_int) }; + } + + pub fn get_client(&self) -> i32 { + unsafe { alsa::snd_seq_client_info_get_client(self.0) as i32 } + } + + pub fn get_name(&self) -> Result<&str> { + let c = unsafe { alsa::snd_seq_client_info_get_name(self.0) }; + from_const("snd_seq_client_info_get_name", c) + } +} + +impl fmt::Debug for ClientInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ClientInfo({},{:?})", self.get_client(), self.get_name()) + } +} + +#[derive(Copy, Clone)] +/// Iterates over clients connected to the seq API (both kernel and userspace clients). +pub struct ClientIter<'a>(&'a Seq, i32); + +impl<'a> ClientIter<'a> { + pub fn new(seq: &'a Seq) -> Self { ClientIter(seq, -1) } +} + +impl<'a> Iterator for ClientIter<'a> { + type Item = ClientInfo; + fn next(&mut self) -> Option { + let z = ClientInfo::new().unwrap(); + z.set_client(self.1); + let r = unsafe { alsa::snd_seq_query_next_client((self.0).0, z.0) }; + if r < 0 { self.1 = -1; return None }; + self.1 = z.get_client(); + Some(z) + } +} + +/// [snd_seq_port_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_port.html) wrapper +pub struct PortInfo(*mut alsa::snd_seq_port_info_t); + +unsafe impl Send for PortInfo {} + +impl Drop for PortInfo { + fn drop(&mut self) { + unsafe { alsa::snd_seq_port_info_free(self.0) }; + } +} + +impl PortInfo { + fn new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_seq_port_info_malloc(&mut p)).map(|_| PortInfo(p)) + } + + /// Creates a new PortInfo with all fields set to zero. + pub fn empty() -> Result { + let z = Self::new()?; + unsafe { ptr::write_bytes(z.0 as *mut u8, 0, alsa::snd_seq_port_info_sizeof()) }; + Ok(z) + } + + pub fn get_client(&self) -> i32 { + unsafe { alsa::snd_seq_port_info_get_client(self.0) as i32 } + } + + pub fn get_port(&self) -> i32 { + unsafe { alsa::snd_seq_port_info_get_port(self.0) as i32 } + } + + // Not sure if it's useful for this one to be public. + fn set_client(&self, client: i32) { + unsafe { alsa::snd_seq_port_info_set_client(self.0, client as c_int) }; + } + + // Not sure if it's useful for this one to be public. + fn set_port(&self, port: i32) { + unsafe { alsa::snd_seq_port_info_set_port(self.0, port as c_int) }; + } + + pub fn get_name(&self) -> Result<&str> { + let c = unsafe { alsa::snd_seq_port_info_get_name(self.0) }; + from_const("snd_seq_port_info_get_name", c) + } + + pub fn set_name(&mut self, name: &CStr) { + // Note: get_name returns an interior reference, so this one must take &mut self + unsafe { alsa::snd_seq_port_info_set_name(self.0, name.as_ptr()) }; + } + + pub fn get_capability(&self) -> PortCap { + PortCap::from_bits_truncate(unsafe { alsa::snd_seq_port_info_get_capability(self.0) as u32 }) + } + + pub fn get_type(&self) -> PortType { + PortType::from_bits_truncate(unsafe { alsa::snd_seq_port_info_get_type(self.0) as u32 }) + } + + pub fn set_capability(&self, c: PortCap) { + unsafe { alsa::snd_seq_port_info_set_capability(self.0, c.bits() as c_uint) } + } + + pub fn set_type(&self, c: PortType) { + unsafe { alsa::snd_seq_port_info_set_type(self.0, c.bits() as c_uint) } + } + + /// Returns an Addr containing this PortInfo's client and port id. + pub fn addr(&self) -> Addr { + Addr { + client: self.get_client(), + port: self.get_port(), + } + } + + pub fn get_midi_channels(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_midi_channels(self.0) as i32 } } + pub fn get_midi_voices(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_midi_voices(self.0) as i32 } } + pub fn get_synth_voices(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_synth_voices(self.0) as i32 } } + pub fn get_read_use(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_read_use(self.0) as i32 } } + pub fn get_write_use(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_write_use(self.0) as i32 } } + pub fn get_port_specified(&self) -> bool { unsafe { alsa::snd_seq_port_info_get_port_specified(self.0) == 1 } } + pub fn get_timestamping(&self) -> bool { unsafe { alsa::snd_seq_port_info_get_timestamping(self.0) == 1 } } + pub fn get_timestamp_real(&self) -> bool { unsafe { alsa::snd_seq_port_info_get_timestamp_real(self.0) == 1 } } + pub fn get_timestamp_queue(&self) -> i32 { unsafe { alsa::snd_seq_port_info_get_timestamp_queue(self.0) as i32 } } + + pub fn set_midi_channels(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_midi_channels(self.0, value as c_int) } } + pub fn set_midi_voices(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_midi_voices(self.0, value as c_int) } } + pub fn set_synth_voices(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_synth_voices(self.0, value as c_int) } } + pub fn set_port_specified(&self, value: bool) { unsafe { alsa::snd_seq_port_info_set_port_specified(self.0, if value { 1 } else { 0 } ) } } + pub fn set_timestamping(&self, value: bool) { unsafe { alsa::snd_seq_port_info_set_timestamping(self.0, if value { 1 } else { 0 } ) } } + pub fn set_timestamp_real(&self, value: bool) { unsafe { alsa::snd_seq_port_info_set_timestamp_real(self.0, if value { 1 } else { 0 } ) } } + pub fn set_timestamp_queue(&self, value: i32) { unsafe { alsa::snd_seq_port_info_set_timestamp_queue(self.0, value as c_int) } } +} + +impl fmt::Debug for PortInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PortInfo({}:{},{:?})", self.get_client(), self.get_port(), self.get_name()) + } +} + +#[derive(Copy, Clone)] +/// Iterates over clients connected to the seq API (both kernel and userspace clients). +pub struct PortIter<'a>(&'a Seq, i32, i32); + +impl<'a> PortIter<'a> { + pub fn new(seq: &'a Seq, client: i32) -> Self { PortIter(seq, client, -1) } +} + +impl<'a> Iterator for PortIter<'a> { + type Item = PortInfo; + fn next(&mut self) -> Option { + let z = PortInfo::new().unwrap(); + z.set_client(self.1); + z.set_port(self.2); + let r = unsafe { alsa::snd_seq_query_next_port((self.0).0, z.0) }; + if r < 0 { self.2 = -1; return None }; + self.2 = z.get_port(); + Some(z) + } +} + +bitflags! { + /// [SND_SEQ_PORT_CAP_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_port.html) constants + pub struct PortCap: u32 { + const READ = 1<<0; + const WRITE = 1<<1; + const SYNC_READ = 1<<2; + const SYNC_WRITE = 1<<3; + const DUPLEX = 1<<4; + const SUBS_READ = 1<<5; + const SUBS_WRITE = 1<<6; + const NO_EXPORT = 1<<7; + } +} + +bitflags! { + /// [SND_SEQ_PORT_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_port.html) constants + pub struct PortType: u32 { + const SPECIFIC = (1<<0); + const MIDI_GENERIC = (1<<1); + const MIDI_GM = (1<<2); + const MIDI_GS = (1<<3); + const MIDI_XG = (1<<4); + const MIDI_MT32 = (1<<5); + const MIDI_GM2 = (1<<6); + const SYNTH = (1<<10); + const DIRECT_SAMPLE = (1<<11); + const SAMPLE = (1<<12); + const HARDWARE = (1<<16); + const SOFTWARE = (1<<17); + const SYNTHESIZER = (1<<18); + const PORT = (1<<19); + const APPLICATION = (1<<20); + } +} + +bitflags! { + /// [SND_SEQ_REMOVE_xxx](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html) constants + pub struct Remove: u32 { + const INPUT = (1<<0); + const OUTPUT = (1<<1); + const DEST = (1<<2); + const DEST_CHANNEL = (1<<3); + const TIME_BEFORE = (1<<4); + const TIME_AFTER = (1<<5); + const TIME_TICK = (1<<6); + const EVENT_TYPE = (1<<7); + const IGNORE_OFF = (1<<8); + const TAG_MATCH = (1<<9); + } +} + + +/// [snd_seq_addr_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__addr__t.html) wrapper +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] +pub struct Addr { + pub client: i32, + pub port: i32, +} + +impl FromStr for Addr { + type Err = Box; + + fn from_str(s: &str) -> std::result::Result { + let mut split: Split<'_, char> = s.trim().split(':'); + let client = split.next() + .ok_or("no client provided")? + .parse::()?; + let port = split.next() + .ok_or("no port provided")? + .parse::()?; + match split.next() { + Some(_) => { + Err("too many arguments".into()) + }, + None => { + Ok(Addr { client, port }) + } + } + } +} + +impl Addr { + pub fn system_timer() -> Addr { Addr { client: alsa::SND_SEQ_CLIENT_SYSTEM as i32, port: alsa::SND_SEQ_PORT_SYSTEM_TIMER as i32 } } + pub fn system_announce() -> Addr { Addr { client: alsa::SND_SEQ_CLIENT_SYSTEM as i32, port: alsa::SND_SEQ_PORT_SYSTEM_ANNOUNCE as i32 } } + pub fn broadcast() -> Addr { Addr { client: alsa::SND_SEQ_ADDRESS_BROADCAST as i32, port: alsa::SND_SEQ_ADDRESS_BROADCAST as i32 } } +} + +/// [snd_seq_port_subscribe_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html) wrapper +pub struct PortSubscribe(*mut alsa::snd_seq_port_subscribe_t); + +unsafe impl Send for PortSubscribe {} + +impl Drop for PortSubscribe { + fn drop(&mut self) { unsafe { alsa::snd_seq_port_subscribe_free(self.0) }; } +} + +impl PortSubscribe { + fn new() -> Result { + let mut p = ptr::null_mut(); + acheck!(snd_seq_port_subscribe_malloc(&mut p)).map(|_| PortSubscribe(p)) + } + + /// Creates a new PortSubscribe with all fields set to zero. + pub fn empty() -> Result { + let z = Self::new()?; + unsafe { ptr::write_bytes(z.0 as *mut u8, 0, alsa::snd_seq_port_subscribe_sizeof()) }; + Ok(z) + } + + pub fn get_sender(&self) -> Addr { unsafe { + let z = alsa::snd_seq_port_subscribe_get_sender(self.0); + Addr { client: (*z).client as i32, port: (*z).port as i32 } + } } + + pub fn get_dest(&self) -> Addr { unsafe { + let z = alsa::snd_seq_port_subscribe_get_dest(self.0); + Addr { client: (*z).client as i32, port: (*z).port as i32 } + } } + + pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_port_subscribe_get_queue(self.0) as i32 } } + pub fn get_exclusive(&self) -> bool { unsafe { alsa::snd_seq_port_subscribe_get_exclusive(self.0) == 1 } } + pub fn get_time_update(&self) -> bool { unsafe { alsa::snd_seq_port_subscribe_get_time_update(self.0) == 1 } } + pub fn get_time_real(&self) -> bool { unsafe { alsa::snd_seq_port_subscribe_get_time_real(self.0) == 1 } } + + pub fn set_sender(&self, value: Addr) { + let z = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar }; + unsafe { alsa::snd_seq_port_subscribe_set_sender(self.0, &z) }; + } + + pub fn set_dest(&self, value: Addr) { + let z = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar }; + unsafe { alsa::snd_seq_port_subscribe_set_dest(self.0, &z) }; + } + + pub fn set_queue(&self, value: i32) { unsafe { alsa::snd_seq_port_subscribe_set_queue(self.0, value as c_int) } } + pub fn set_exclusive(&self, value: bool) { unsafe { alsa::snd_seq_port_subscribe_set_exclusive(self.0, if value { 1 } else { 0 } ) } } + pub fn set_time_update(&self, value: bool) { unsafe { alsa::snd_seq_port_subscribe_set_time_update(self.0, if value { 1 } else { 0 } ) } } + pub fn set_time_real(&self, value: bool) { unsafe { alsa::snd_seq_port_subscribe_set_time_real(self.0, if value { 1 } else { 0 } ) } } + +} + +/// [snd_seq_query_subs_type_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html) wrapper +#[derive(Copy, Clone)] +pub enum QuerySubsType { + READ = alsa::SND_SEQ_QUERY_SUBS_READ as isize, + WRITE = alsa::SND_SEQ_QUERY_SUBS_WRITE as isize, +} + +/// [snd_seq_query_subscribe_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html) wrapper +//(kept private, functionality exposed by PortSubscribeIter) +struct QuerySubscribe(*mut alsa::snd_seq_query_subscribe_t); + +unsafe impl Send for QuerySubscribe {} + +impl Drop for QuerySubscribe { + fn drop(&mut self) { unsafe { alsa::snd_seq_query_subscribe_free(self.0) } } +} + +impl QuerySubscribe { + pub fn new() -> Result { + let mut q = ptr::null_mut(); + acheck!(snd_seq_query_subscribe_malloc(&mut q)).map(|_| QuerySubscribe(q)) + } + + pub fn get_index(&self) -> i32 { unsafe { alsa::snd_seq_query_subscribe_get_index(self.0) as i32 } } + pub fn get_addr(&self) -> Addr { unsafe { + let a = &(*alsa::snd_seq_query_subscribe_get_addr(self.0)); + Addr { client: a.client as i32, port: a.port as i32 } + } } + pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_query_subscribe_get_queue(self.0) as i32 } } + pub fn get_exclusive(&self) -> bool { unsafe { alsa::snd_seq_query_subscribe_get_exclusive(self.0) == 1 } } + pub fn get_time_update(&self) -> bool { unsafe { alsa::snd_seq_query_subscribe_get_time_update(self.0) == 1 } } + pub fn get_time_real(&self) -> bool { unsafe { alsa::snd_seq_query_subscribe_get_time_real(self.0) == 1 } } + + pub fn set_root(&self, value: Addr) { unsafe { + let a = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar}; + alsa::snd_seq_query_subscribe_set_root(self.0, &a); + } } + pub fn set_type(&self, value: QuerySubsType) { unsafe { + alsa::snd_seq_query_subscribe_set_type(self.0, value as alsa::snd_seq_query_subs_type_t) + } } + pub fn set_index(&self, value: i32) { unsafe { alsa::snd_seq_query_subscribe_set_index(self.0, value as c_int) } } +} + +#[derive(Copy, Clone)] +/// Iterates over port subscriptions for a given client:port/type. +pub struct PortSubscribeIter<'a> { + seq: &'a Seq, + addr: Addr, + query_subs_type: QuerySubsType, + index: i32 +} + +impl<'a> PortSubscribeIter<'a> { + pub fn new(seq: &'a Seq, addr: Addr, query_subs_type: QuerySubsType) -> Self { + PortSubscribeIter {seq, addr, query_subs_type, index: 0 } + } +} + +impl<'a> Iterator for PortSubscribeIter<'a> { + type Item = PortSubscribe; + + fn next(&mut self) -> Option { + let query = QuerySubscribe::new().unwrap(); + + query.set_root(self.addr); + query.set_type(self.query_subs_type); + query.set_index(self.index); + + let r = unsafe { alsa::snd_seq_query_port_subscribers((self.seq).0, query.0) }; + if r < 0 { + self.index = 0; + return None; + } + + self.index = query.get_index() + 1; + let vtr = PortSubscribe::new().unwrap(); + match self.query_subs_type { + QuerySubsType::READ => { + vtr.set_sender(self.addr); + vtr.set_dest(query.get_addr()); + }, + QuerySubsType:: WRITE => { + vtr.set_sender(query.get_addr()); + vtr.set_dest(self.addr); + } + }; + vtr.set_queue(query.get_queue()); + vtr.set_exclusive(query.get_exclusive()); + vtr.set_time_update(query.get_time_update()); + vtr.set_time_real(query.get_time_real()); + + Some(vtr) + } +} + +/// [snd_seq_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html) wrapper +/// +/// Fields of the event is not directly exposed. Instead call `Event::new` to set data (which can be, e g, an EvNote). +/// Use `get_type` and `get_data` to retrieve data. +/// +/// The lifetime parameter refers to the lifetime of an associated external buffer that might be used for +/// variable-length messages (e.g. SysEx). +pub struct Event<'a>(alsa::snd_seq_event_t, EventType, Option>); + +unsafe impl<'a> Send for Event<'a> {} + +impl<'a> Event<'a> { + /// Creates a new event. For events that carry variable-length data (e.g. Sysex), `new_ext` has to be used instead. + pub fn new(t: EventType, data: &D) -> Event<'static> { + assert!(!Event::has_ext_data(t), "event type must not carry variable-length data"); + let mut z = Event(unsafe { mem::zeroed() }, t, None); + (z.0).type_ = t as c_uchar; + (z.0).flags |= Event::get_length_flag(t); + debug_assert!(D::has_data(t)); + data.set_data(&mut z); + z + } + + /// Creates a new event carrying variable-length data. This is required for event types `Sysex`, `Bounce`, and the `UsrVar` types. + pub fn new_ext>>(t: EventType, data: D) -> Event<'a> { + assert!(Event::has_ext_data(t), "event type must carry variable-length data"); + let mut z = Event(unsafe { mem::zeroed() }, t, Some(data.into())); + (z.0).type_ = t as c_uchar; + (z.0).flags |= Event::get_length_flag(t); + z + } + + /// Consumes this event and returns an (otherwise unchanged) event where the externally referenced + /// buffer for variable length messages (e.g. SysEx) has been copied into the event. + /// The returned event has a static lifetime, i e, it's decoupled from the original buffer. + pub fn into_owned(self) -> Event<'static> { + Event(self.0, self.1, self.2.map(|cow| Cow::Owned(cow.into_owned()))) + } + + fn get_length_flag(t: EventType) -> u8 { + match t { + EventType::Sysex => alsa::SND_SEQ_EVENT_LENGTH_VARIABLE, + EventType::Bounce => alsa::SND_SEQ_EVENT_LENGTH_VARIABLE, // not clear whether this should be VARIABLE or VARUSR + EventType::UsrVar0 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR, + EventType::UsrVar1 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR, + EventType::UsrVar2 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR, + EventType::UsrVar3 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR, + EventType::UsrVar4 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR, + _ => alsa::SND_SEQ_EVENT_LENGTH_FIXED + } + } + + fn has_ext_data(t: EventType) -> bool { + Event::get_length_flag(t) != alsa::SND_SEQ_EVENT_LENGTH_FIXED + } + + /// Extracts event type and data. Produces a result with an arbitrary lifetime, hence the unsafety. + unsafe fn extract<'any>(z: &mut alsa::snd_seq_event_t, func: &'static str) -> Result> { + let t = EventType::from_c_int((*z).type_ as c_int, func)?; + let ext_data = if Event::has_ext_data(t) { + assert_ne!((*z).flags & alsa::SND_SEQ_EVENT_LENGTH_MASK, alsa::SND_SEQ_EVENT_LENGTH_FIXED); + Some(Cow::Borrowed({ + let zz: &EvExtPacked = &*(&(*z).data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _); + slice::from_raw_parts((*zz).ptr as *mut u8, (*zz).len as usize) + })) + } else { + None + }; + Ok(Event(ptr::read(z), t, ext_data)) + } + + /// Ensures that the ev.ext union element points to the correct resize_buffer for events + /// with variable length content + fn ensure_buf(&mut self) { + if !Event::has_ext_data(self.1) { return; } + let slice: &[u8] = match self.2 { + Some(Cow::Owned(ref mut vec)) => &vec[..], + Some(Cow::Borrowed(buf)) => buf, + // The following case is always a logic error in the program, thus panicking is okay. + None => panic!("event type requires variable-length data, but none was provided") + }; + let z: &mut EvExtPacked = unsafe { &mut *(&mut self.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) }; + z.len = slice.len() as c_uint; + z.ptr = slice.as_ptr() as *mut c_void; + } + + #[inline] + pub fn get_type(&self) -> EventType { self.1 } + + /// Extract the event data from an event. + /// Use `get_ext` instead for events carrying variable-length data. + pub fn get_data(&self) -> Option { if D::has_data(self.1) { Some(D::get_data(self)) } else { None } } + + /// Extract the variable-length data carried by events of type `Sysex`, `Bounce`, or the `UsrVar` types. + pub fn get_ext(&self) -> Option<&[u8]> { + if Event::has_ext_data(self.1) { + match self.2 { + Some(Cow::Owned(ref vec)) => Some(&vec[..]), + Some(Cow::Borrowed(buf)) => Some(buf), + // The following case is always a logic error in the program, thus panicking is okay. + None => panic!("event type requires variable-length data, but none was found") + } + } else { + None + } + } + + pub fn set_subs(&mut self) { + self.0.dest.client = alsa::SND_SEQ_ADDRESS_SUBSCRIBERS; + self.0.dest.port = alsa::SND_SEQ_ADDRESS_UNKNOWN; + } + + pub fn set_source(&mut self, p: i32) { self.0.source.port = p as u8 } + pub fn set_dest(&mut self, d: Addr) { self.0.dest.client = d.client as c_uchar; self.0.dest.port = d.port as c_uchar; } + pub fn set_tag(&mut self, t: u8) { self.0.tag = t as c_uchar; } + pub fn set_queue(&mut self, q: i32) { self.0.queue = q as c_uchar; } + + pub fn get_source(&self) -> Addr { Addr { client: self.0.source.client as i32, port: self.0.source.port as i32 } } + pub fn get_dest(&self) -> Addr { Addr { client: self.0.dest.client as i32, port: self.0.dest.port as i32 } } + pub fn get_tag(&self) -> u8 { self.0.tag as u8 } + pub fn get_queue(&self) -> i32 { self.0.queue as i32 } + + pub fn schedule_real(&mut self, queue: i32, relative: bool, rtime: time::Duration) { + self.0.flags &= !(alsa::SND_SEQ_TIME_STAMP_MASK | alsa::SND_SEQ_TIME_MODE_MASK); + self.0.flags |= alsa::SND_SEQ_TIME_STAMP_REAL | (if relative { alsa::SND_SEQ_TIME_MODE_REL } else { alsa::SND_SEQ_TIME_MODE_ABS }); + self.0.queue = queue as u8; + let t = unsafe { &mut self.0.time.time }; + t.tv_sec = rtime.as_secs() as c_uint; + t.tv_nsec = rtime.subsec_nanos() as c_uint; + } + + pub fn schedule_tick(&mut self, queue: i32, relative: bool, ttime: u32) { + self.0.flags &= !(alsa::SND_SEQ_TIME_STAMP_MASK | alsa::SND_SEQ_TIME_MODE_MASK); + self.0.flags |= alsa::SND_SEQ_TIME_STAMP_TICK | (if relative { alsa::SND_SEQ_TIME_MODE_REL } else { alsa::SND_SEQ_TIME_MODE_ABS }); + self.0.queue = queue as u8; + let t = unsafe { &mut self.0.time.tick }; + *t = ttime as c_uint; + } + + pub fn set_direct(&mut self) { self.0.queue = alsa::SND_SEQ_QUEUE_DIRECT } + + pub fn get_relative(&self) -> bool { (self.0.flags & alsa::SND_SEQ_TIME_MODE_REL) != 0 } + + pub fn get_time(&self) -> Option { + if (self.0.flags & alsa::SND_SEQ_TIME_STAMP_REAL) != 0 { + let d = self.0.time; + let t = unsafe { &d.time }; + Some(time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32)) + } else { None } + } + + pub fn get_tick(&self) -> Option { + if (self.0.flags & alsa::SND_SEQ_TIME_STAMP_REAL) == 0 { + let d = self.0.time; + let t = unsafe { &d.tick }; + Some(*t) + } else { None } + } + + /// Returns true if the message is high priority. + pub fn get_priority(&self) -> bool { (self.0.flags & alsa::SND_SEQ_PRIORITY_HIGH) != 0 } + + pub fn set_priority(&mut self, is_high_prio: bool) { + if is_high_prio { self.0.flags |= alsa::SND_SEQ_PRIORITY_HIGH; } + else { self.0.flags &= !alsa::SND_SEQ_PRIORITY_HIGH; } + } +} + +impl<'a> Clone for Event<'a> { + fn clone(&self) -> Self { Event(unsafe { ptr::read(&self.0) }, self.1, self.2.clone()) } +} + +impl<'a> fmt::Debug for Event<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut x = f.debug_tuple("Event"); + x.field(&self.1); + if let Some(z) = self.get_data::() { x.field(&z); } + if let Some(z) = self.get_data::() { x.field(&z); } + if let Some(z) = self.get_data::() { x.field(&z); } + if let Some(z) = self.get_data::() { x.field(&z); } + if let Some(z) = self.get_data::>() { x.field(&z); } + if let Some(z) = self.get_data::>() { x.field(&z); } + if let Some(z) = self.get_data::>() { x.field(&z); } + if let Some(z) = self.get_data::>() { x.field(&z); } + if let Some(z) = self.get_data::() { x.field(&z); } + if let Some(z) = self.get_data::<[u8; 12]>() { x.field(&z); } + if let Some(z) = self.get_ext() { x.field(&z); } + x.finish() + } +} + +/// Low level methods to set/get data on an Event. Don't use these directly, use generic methods on Event instead. +pub trait EventData { + fn get_data(ev: &Event) -> Self; + fn has_data(e: EventType) -> bool; + fn set_data(&self, ev: &mut Event); +} + +impl EventData for () { + fn get_data(_: &Event) -> Self {} + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::TuneRequest | + EventType::Reset | + EventType::Sensing | + EventType::None) + } + fn set_data(&self, _: &mut Event) {} +} + +impl EventData for [u8; 12] { + fn get_data(ev: &Event) -> Self { + let d = unsafe { ptr::read(&ev.0.data) }; + let z = unsafe { &d.raw8 }; + z.d + } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::Echo | + EventType::Oss | + EventType::Usr0 | + EventType::Usr1 | + EventType::Usr2 | + EventType::Usr3 | + EventType::Usr4 | + EventType::Usr5 | + EventType::Usr6 | + EventType::Usr7 | + EventType::Usr8 | + EventType::Usr9) + } + fn set_data(&self, ev: &mut Event) { + let z = unsafe { &mut ev.0.data.raw8 }; + z.d = *self; + } +} + + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)] +pub struct EvNote { + pub channel: u8, + pub note: u8, + pub velocity: u8, + pub off_velocity: u8, + pub duration: u32, +} + +impl EventData for EvNote { + fn get_data(ev: &Event) -> Self { + let z: &alsa::snd_seq_ev_note_t = unsafe { &*(&ev.0.data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _) }; + EvNote { channel: z.channel as u8, note: z.note as u8, velocity: z.velocity as u8, off_velocity: z.off_velocity as u8, duration: z.duration as u32 } + } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::Note | + EventType::Noteon | + EventType::Noteoff | + EventType::Keypress) + } + fn set_data(&self, ev: &mut Event) { + let z: &mut alsa::snd_seq_ev_note_t = unsafe { &mut *(&mut ev.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) }; + z.channel = self.channel as c_uchar; + z.note = self.note as c_uchar; + z.velocity = self.velocity as c_uchar; + z.off_velocity = self.off_velocity as c_uchar; + z.duration = self.duration as c_uint; + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)] +pub struct EvCtrl { + pub channel: u8, + pub param: u32, + pub value: i32, +} + +impl EventData for EvCtrl { + fn get_data(ev: &Event) -> Self { + let z: &alsa::snd_seq_ev_ctrl_t = unsafe { &*(&ev.0.data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _) }; + EvCtrl { channel: z.channel as u8, param: z.param as u32, value: z.value as i32 } + } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::Controller | + EventType::Pgmchange | + EventType::Chanpress | + EventType::Pitchbend | + EventType::Control14 | + EventType::Nonregparam | + EventType::Regparam | + EventType::Songpos | + EventType::Songsel | + EventType::Qframe | + EventType::Timesign | + EventType::Keysign) + } + fn set_data(&self, ev: &mut Event) { + let z: &mut alsa::snd_seq_ev_ctrl_t = unsafe { &mut *(&mut ev.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) }; + z.channel = self.channel as c_uchar; + z.param = self.param as c_uint; + z.value = self.value as c_int; + } +} + +impl EventData for Addr { + fn get_data(ev: &Event) -> Self { + let z: &alsa::snd_seq_addr_t = unsafe { &*(&ev.0.data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _) }; + Addr { client: z.client as i32, port: z.port as i32 } + } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::ClientStart | + EventType::ClientExit | + EventType::ClientChange | + EventType::PortStart | + EventType::PortExit | + EventType::PortChange) + } + fn set_data(&self, ev: &mut Event) { + let z: &mut alsa::snd_seq_addr_t = unsafe { &mut *(&mut ev.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) }; + z.client = self.client as c_uchar; + z.port = self.port as c_uchar; + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)] +/// [snd_seq_connect_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__connect__t.html) wrapper +pub struct Connect { + pub sender: Addr, + pub dest: Addr, +} + +impl EventData for Connect { + fn get_data(ev: &Event) -> Self { + let d = unsafe { ptr::read(&ev.0.data) }; + let z = unsafe { &d.connect }; + Connect { + sender: Addr { client: z.sender.client as i32, port: z.sender.port as i32 }, + dest: Addr { client: z.dest.client as i32, port: z.dest.port as i32 } + } + } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::PortSubscribed | + EventType::PortUnsubscribed) + } + fn set_data(&self, ev: &mut Event) { + let z = unsafe { &mut ev.0.data.connect }; + z.sender.client = self.sender.client as c_uchar; + z.sender.port = self.sender.port as c_uchar; + z.dest.client = self.dest.client as c_uchar; + z.dest.port = self.dest.port as c_uchar; + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)] +/// [snd_seq_ev_queue_control_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__ev__queue__control__t.html) wrapper +/// +/// Note: This struct is generic, but what types of T are required for the different EvQueueControl messages is +/// not very well documented in alsa-lib. Right now, Tempo is i32, Tick, SetposTick and SyncPos are u32, SetposTime is time::Duration, +/// and the rest is (). If I guessed wrong, let me know. +pub struct EvQueueControl { + pub queue: i32, + pub value: T, +} + +impl EventData for EvQueueControl<()> { + fn get_data(ev: &Event) -> Self { + let d = unsafe { ptr::read(&ev.0.data) }; + let z = unsafe { &d.queue }; + EvQueueControl { queue: z.queue as i32, value: () } + } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::Start | + EventType::Continue | + EventType::Stop | + EventType::Clock | + EventType::QueueSkew) + } + fn set_data(&self, ev: &mut Event) { + let z = unsafe { &mut ev.0.data.queue }; + z.queue = self.queue as c_uchar; + } +} + +impl EventData for EvQueueControl { + fn get_data(ev: &Event) -> Self { unsafe { + let mut d = ptr::read(&ev.0.data); + let z = &mut d.queue; + EvQueueControl { queue: z.queue as i32, value: z.param.value as i32 } + } } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::Tempo) + } + fn set_data(&self, ev: &mut Event) { unsafe { + let z = &mut ev.0.data.queue; + z.queue = self.queue as c_uchar; + z.param.value = self.value as c_int; + } } +} + +impl EventData for EvQueueControl { + fn get_data(ev: &Event) -> Self { unsafe { + let mut d = ptr::read(&ev.0.data); + let z = &mut d.queue; + EvQueueControl { queue: z.queue as i32, value: z.param.position as u32 } + } } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::SyncPos | + EventType::Tick | + EventType::SetposTick) + } + fn set_data(&self, ev: &mut Event) { unsafe { + let z = &mut ev.0.data.queue; + z.queue = self.queue as c_uchar; + z.param.position = self.value as c_uint; + } } +} + +impl EventData for EvQueueControl { + fn get_data(ev: &Event) -> Self { unsafe { + let mut d = ptr::read(&ev.0.data); + let z = &mut d.queue; + let t = &mut z.param.time.time; + EvQueueControl { queue: z.queue as i32, value: time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32) } + } } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::SetposTime) + } + fn set_data(&self, ev: &mut Event) { unsafe { + let z = &mut ev.0.data.queue; + z.queue = self.queue as c_uchar; + let t = &mut z.param.time.time; + t.tv_sec = self.value.as_secs() as c_uint; + t.tv_nsec = self.value.subsec_nanos() as c_uint; + } } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)] +/// [snd_seq_result_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__result__t.html) wrapper +/// +/// It's called EvResult instead of Result, in order to not be confused with Rust's Result type. +pub struct EvResult { + pub event: i32, + pub result: i32, +} + +impl EventData for EvResult { + fn get_data(ev: &Event) -> Self { + let d = unsafe { ptr::read(&ev.0.data) }; + let z = unsafe { &d.result }; + EvResult { event: z.event as i32, result: z.result as i32 } + } + fn has_data(e: EventType) -> bool { + matches!(e, + EventType::System | + EventType::Result) + } + fn set_data(&self, ev: &mut Event) { + let z = unsafe { &mut ev.0.data.result }; + z.event = self.event as c_int; + z.result = self.result as c_int; + } +} + + + +alsa_enum!( + /// [SND_SEQ_EVENT_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_events.html) constants + + EventType, ALL_EVENT_TYPES[59], + + Bounce = SND_SEQ_EVENT_BOUNCE, + Chanpress = SND_SEQ_EVENT_CHANPRESS, + ClientChange = SND_SEQ_EVENT_CLIENT_CHANGE, + ClientExit = SND_SEQ_EVENT_CLIENT_EXIT, + ClientStart = SND_SEQ_EVENT_CLIENT_START, + Clock = SND_SEQ_EVENT_CLOCK, + Continue = SND_SEQ_EVENT_CONTINUE, + Control14 = SND_SEQ_EVENT_CONTROL14, + Controller = SND_SEQ_EVENT_CONTROLLER, + Echo = SND_SEQ_EVENT_ECHO, + Keypress = SND_SEQ_EVENT_KEYPRESS, + Keysign = SND_SEQ_EVENT_KEYSIGN, + None = SND_SEQ_EVENT_NONE, + Nonregparam = SND_SEQ_EVENT_NONREGPARAM, + Note = SND_SEQ_EVENT_NOTE, + Noteoff = SND_SEQ_EVENT_NOTEOFF, + Noteon = SND_SEQ_EVENT_NOTEON, + Oss = SND_SEQ_EVENT_OSS, + Pgmchange = SND_SEQ_EVENT_PGMCHANGE, + Pitchbend = SND_SEQ_EVENT_PITCHBEND, + PortChange = SND_SEQ_EVENT_PORT_CHANGE, + PortExit = SND_SEQ_EVENT_PORT_EXIT, + PortStart = SND_SEQ_EVENT_PORT_START, + PortSubscribed = SND_SEQ_EVENT_PORT_SUBSCRIBED, + PortUnsubscribed = SND_SEQ_EVENT_PORT_UNSUBSCRIBED, + Qframe = SND_SEQ_EVENT_QFRAME, + QueueSkew = SND_SEQ_EVENT_QUEUE_SKEW, + Regparam = SND_SEQ_EVENT_REGPARAM, + Reset = SND_SEQ_EVENT_RESET, + Result = SND_SEQ_EVENT_RESULT, + Sensing = SND_SEQ_EVENT_SENSING, + SetposTick = SND_SEQ_EVENT_SETPOS_TICK, + SetposTime = SND_SEQ_EVENT_SETPOS_TIME, + Songpos = SND_SEQ_EVENT_SONGPOS, + Songsel = SND_SEQ_EVENT_SONGSEL, + Start = SND_SEQ_EVENT_START, + Stop = SND_SEQ_EVENT_STOP, + SyncPos = SND_SEQ_EVENT_SYNC_POS, + Sysex = SND_SEQ_EVENT_SYSEX, + System = SND_SEQ_EVENT_SYSTEM, + Tempo = SND_SEQ_EVENT_TEMPO, + Tick = SND_SEQ_EVENT_TICK, + Timesign = SND_SEQ_EVENT_TIMESIGN, + TuneRequest = SND_SEQ_EVENT_TUNE_REQUEST, + Usr0 = SND_SEQ_EVENT_USR0, + Usr1 = SND_SEQ_EVENT_USR1, + Usr2 = SND_SEQ_EVENT_USR2, + Usr3 = SND_SEQ_EVENT_USR3, + Usr4 = SND_SEQ_EVENT_USR4, + Usr5 = SND_SEQ_EVENT_USR5, + Usr6 = SND_SEQ_EVENT_USR6, + Usr7 = SND_SEQ_EVENT_USR7, + Usr8 = SND_SEQ_EVENT_USR8, + Usr9 = SND_SEQ_EVENT_USR9, + UsrVar0 = SND_SEQ_EVENT_USR_VAR0, + UsrVar1 = SND_SEQ_EVENT_USR_VAR1, + UsrVar2 = SND_SEQ_EVENT_USR_VAR2, + UsrVar3 = SND_SEQ_EVENT_USR_VAR3, + UsrVar4 = SND_SEQ_EVENT_USR_VAR4, +); + +/// [snd_seq_queue_tempo_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_queue.html) wrapper +pub struct QueueTempo(*mut alsa::snd_seq_queue_tempo_t); + +unsafe impl Send for QueueTempo {} + +impl Drop for QueueTempo { + fn drop(&mut self) { unsafe { alsa::snd_seq_queue_tempo_free(self.0) } } +} + +impl QueueTempo { + fn new() -> Result { + let mut q = ptr::null_mut(); + acheck!(snd_seq_queue_tempo_malloc(&mut q)).map(|_| QueueTempo(q)) + } + + /// Creates a new QueueTempo with all fields set to zero. + pub fn empty() -> Result { + let q = QueueTempo::new()?; + unsafe { ptr::write_bytes(q.0 as *mut u8, 0, alsa::snd_seq_queue_tempo_sizeof()) }; + Ok(q) + } + + pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_queue_tempo_get_queue(self.0) as i32 } } + pub fn get_tempo(&self) -> u32 { unsafe { alsa::snd_seq_queue_tempo_get_tempo(self.0) as u32 } } + pub fn get_ppq(&self) -> i32 { unsafe { alsa::snd_seq_queue_tempo_get_ppq(self.0) as i32 } } + pub fn get_skew(&self) -> u32 { unsafe { alsa::snd_seq_queue_tempo_get_skew(self.0) as u32 } } + pub fn get_skew_base(&self) -> u32 { unsafe { alsa::snd_seq_queue_tempo_get_skew_base(self.0) as u32 } } + +// pub fn set_queue(&self, value: i32) { unsafe { alsa::snd_seq_queue_tempo_set_queue(self.0, value as c_int) } } + pub fn set_tempo(&self, value: u32) { unsafe { alsa::snd_seq_queue_tempo_set_tempo(self.0, value as c_uint) } } + pub fn set_ppq(&self, value: i32) { unsafe { alsa::snd_seq_queue_tempo_set_ppq(self.0, value as c_int) } } + pub fn set_skew(&self, value: u32) { unsafe { alsa::snd_seq_queue_tempo_set_skew(self.0, value as c_uint) } } + pub fn set_skew_base(&self, value: u32) { unsafe { alsa::snd_seq_queue_tempo_set_skew_base(self.0, value as c_uint) } } +} + +/// [snd_seq_queue_status_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_queue.html) wrapper +pub struct QueueStatus(*mut alsa::snd_seq_queue_status_t); + +unsafe impl Send for QueueStatus {} + +impl Drop for QueueStatus { + fn drop(&mut self) { unsafe { alsa::snd_seq_queue_status_free(self.0) } } +} + +impl QueueStatus { + fn new() -> Result { + let mut q = ptr::null_mut(); + acheck!(snd_seq_queue_status_malloc(&mut q)).map(|_| QueueStatus(q)) + } + + /// Creates a new QueueStatus with all fields set to zero. + pub fn empty() -> Result { + let q = QueueStatus::new()?; + unsafe { ptr::write_bytes(q.0 as *mut u8, 0, alsa::snd_seq_queue_status_sizeof()) }; + Ok(q) + } + + pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_queue_status_get_queue(self.0) as i32 } } + pub fn get_events(&self) -> i32 { unsafe { alsa::snd_seq_queue_status_get_events(self.0) as i32 } } + pub fn get_tick_time(&self) -> u32 { unsafe {alsa::snd_seq_queue_status_get_tick_time(self.0) as u32 } } + pub fn get_real_time(&self) -> time::Duration { unsafe { + let t = &(*alsa::snd_seq_queue_status_get_real_time(self.0)); + time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32) + } } + pub fn get_status(&self) -> u32 { unsafe { alsa::snd_seq_queue_status_get_status(self.0) as u32 } } +} + +/// [snd_seq_remove_events_t](https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html) wrapper +pub struct RemoveEvents(*mut alsa::snd_seq_remove_events_t); + +unsafe impl Send for RemoveEvents {} + +impl Drop for RemoveEvents { + fn drop(&mut self) { unsafe { alsa::snd_seq_remove_events_free(self.0) } } +} + +impl RemoveEvents { + pub fn new() -> Result { + let mut q = ptr::null_mut(); + acheck!(snd_seq_remove_events_malloc(&mut q)).map(|_| RemoveEvents(q)) + } + + pub fn get_condition(&self) -> Remove { unsafe { + Remove::from_bits_truncate(alsa::snd_seq_remove_events_get_condition(self.0) as u32) + } } + pub fn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_remove_events_get_queue(self.0) as i32 } } + pub fn get_time(&self) -> time::Duration { unsafe { + let d = ptr::read(alsa::snd_seq_remove_events_get_time(self.0)); + let t = &d.time; + + time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32) + } } + pub fn get_dest(&self) -> Addr { unsafe { + let a = &(*alsa::snd_seq_remove_events_get_dest(self.0)); + + Addr { client: a.client as i32, port: a.port as i32 } + } } + pub fn get_channel(&self) -> i32 { unsafe { alsa::snd_seq_remove_events_get_channel(self.0) as i32 } } + pub fn get_event_type(&self) -> Result { unsafe { + EventType::from_c_int(alsa::snd_seq_remove_events_get_event_type(self.0), "snd_seq_remove_events_get_event_type") + } } + pub fn get_tag(&self) -> u8 { unsafe { alsa::snd_seq_remove_events_get_tag(self.0) as u8 } } + + + pub fn set_condition(&self, value: Remove) { unsafe { + alsa::snd_seq_remove_events_set_condition(self.0, value.bits() as c_uint); + } } + pub fn set_queue(&self, value: i32) { unsafe { alsa::snd_seq_remove_events_set_queue(self.0, value as c_int) } } + pub fn set_time(&self, value: time::Duration) { unsafe { + let mut d: alsa::snd_seq_timestamp_t = mem::zeroed(); + let mut t = &mut d.time; + + t.tv_sec = value.as_secs() as c_uint; + t.tv_nsec = value.subsec_nanos() as c_uint; + + alsa::snd_seq_remove_events_set_time(self.0, &d); + } } + pub fn set_dest(&self, value: Addr) { unsafe { + let a = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar}; + + alsa::snd_seq_remove_events_set_dest(self.0, &a); + } } + pub fn set_channel(&self, value: i32) { unsafe { alsa::snd_seq_remove_events_set_channel(self.0, value as c_int) } } + pub fn set_event_type(&self, value: EventType) { unsafe { alsa::snd_seq_remove_events_set_event_type(self.0, value as i32); } } + pub fn set_tag(&self, value: u8) { unsafe { alsa::snd_seq_remove_events_set_tag(self.0, value as c_int) } } +} + +/// [snd_midi_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___m_i_d_i___event.html) Wrapper +/// +/// Sequencer event <-> MIDI byte stream coder +pub struct MidiEvent(*mut alsa::snd_midi_event_t); + +impl Drop for MidiEvent { + fn drop(&mut self) { unsafe { alsa::snd_midi_event_free(self.0) } } +} + +impl MidiEvent { + pub fn new(bufsize: u32) -> Result { + let mut q = ptr::null_mut(); + acheck!(snd_midi_event_new(bufsize as size_t, &mut q)).map(|_| MidiEvent(q)) + } + + pub fn resize_buffer(&self, bufsize: u32) -> Result<()> { acheck!(snd_midi_event_resize_buffer(self.0, bufsize as size_t)).map(|_| ()) } + + /// Note: this corresponds to snd_midi_event_no_status, but on and off are switched. + /// + /// Alsa-lib is a bit confusing here. Anyhow, set "enable" to true to enable running status. + pub fn enable_running_status(&self, enable: bool) { unsafe { alsa::snd_midi_event_no_status(self.0, if enable {0} else {1}) } } + + /// Resets both encoder and decoder + pub fn init(&self) { unsafe { alsa::snd_midi_event_init(self.0) } } + + pub fn reset_encode(&self) { unsafe { alsa::snd_midi_event_reset_encode(self.0) } } + + pub fn reset_decode(&self) { unsafe { alsa::snd_midi_event_reset_decode(self.0) } } + + pub fn decode(&self, buf: &mut [u8], ev: &mut Event) -> Result { + ev.ensure_buf(); + acheck!(snd_midi_event_decode(self.0, buf.as_mut_ptr() as *mut c_uchar, buf.len() as c_long, &ev.0)).map(|r| r as usize) + } + + /// In case of success, returns a tuple of (bytes consumed from buf, found Event). + pub fn encode<'a>(&'a mut self, buf: &[u8]) -> Result<(usize, Option>)> { + // The ALSA documentation clearly states that the event will be valid as long as the Encoder + // is not messed with (because the data pointer for sysex events may point into the Encoder's + // buffer). We make this safe by taking self by unique reference and coupling it to + // the event's lifetime. + let mut ev = unsafe { mem::zeroed() }; + let r = acheck!(snd_midi_event_encode(self.0, buf.as_ptr() as *const c_uchar, buf.len() as c_long, &mut ev))?; + let e = if ev.type_ == alsa::SND_SEQ_EVENT_NONE as u8 { + None + } else { + Some(unsafe { Event::extract(&mut ev, "snd_midi_event_encode") }?) + }; + Ok((r as usize, e)) + } +} + +#[test] +fn print_seqs() { + use std::ffi::CString; + let s = super::Seq::open(None, None, false).unwrap(); + s.set_client_name(&CString::new("rust_test_print_seqs").unwrap()).unwrap(); + let clients: Vec<_> = ClientIter::new(&s).collect(); + for a in &clients { + let ports: Vec<_> = PortIter::new(&s, a.get_client()).collect(); + println!("{:?}: {:?}", a, ports); + } +} + +#[test] +fn seq_subscribe() { + use std::ffi::CString; + let s = super::Seq::open(None, None, false).unwrap(); + s.set_client_name(&CString::new("rust_test_seq_subscribe").unwrap()).unwrap(); + let timer_info = s.get_any_port_info(Addr { client: 0, port: 0 }).unwrap(); + assert_eq!(timer_info.get_name().unwrap(), "Timer"); + let info = PortInfo::empty().unwrap(); + let _port = s.create_port(&info); + let subs = PortSubscribe::empty().unwrap(); + subs.set_sender(Addr { client: 0, port: 0 }); + subs.set_dest(Addr { client: s.client_id().unwrap(), port: info.get_port() }); + s.subscribe_port(&subs).unwrap(); +} + +#[test] +fn seq_loopback() { + use std::ffi::CString; + let s = super::Seq::open(Some(&CString::new("default").unwrap()), None, false).unwrap(); + s.set_client_name(&CString::new("rust_test_seq_loopback").unwrap()).unwrap(); + + // Create ports + let sinfo = PortInfo::empty().unwrap(); + sinfo.set_capability(PortCap::READ | PortCap::SUBS_READ); + sinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION); + s.create_port(&sinfo).unwrap(); + let sport = sinfo.get_port(); + let dinfo = PortInfo::empty().unwrap(); + dinfo.set_capability(PortCap::WRITE | PortCap::SUBS_WRITE); + dinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION); + s.create_port(&dinfo).unwrap(); + let dport = dinfo.get_port(); + + // Connect them + let subs = PortSubscribe::empty().unwrap(); + subs.set_sender(Addr { client: s.client_id().unwrap(), port: sport }); + subs.set_dest(Addr { client: s.client_id().unwrap(), port: dport }); + s.subscribe_port(&subs).unwrap(); + println!("Connected {:?} to {:?}", subs.get_sender(), subs.get_dest()); + + // Send a note! + let note = EvNote { channel: 0, note: 64, duration: 100, velocity: 100, off_velocity: 64 }; + let mut e = Event::new(EventType::Noteon, ¬e); + e.set_subs(); + e.set_direct(); + e.set_source(sport); + println!("Sending {:?}", e); + s.event_output(&mut e).unwrap(); + s.drain_output().unwrap(); + + // Receive the note! + let mut input = s.input(); + let e2 = input.event_input().unwrap(); + println!("Receiving {:?}", e2); + assert_eq!(e2.get_type(), EventType::Noteon); + assert_eq!(e2.get_data(), Some(note)); +} + +#[test] +fn seq_encode_sysex() { + let mut me = MidiEvent::new(16).unwrap(); + let sysex = &[0xf0, 1, 2, 3, 4, 5, 6, 7, 0xf7]; + let (s, ev) = me.encode(sysex).unwrap(); + assert_eq!(s, 9); + let ev = ev.unwrap(); + let v = ev.get_ext().unwrap(); + assert_eq!(&*v, sysex); +} + +#[test] +fn seq_decode_sysex() { + let sysex = [0xf0, 1, 2, 3, 4, 5, 6, 7, 0xf7]; + let mut ev = Event::new_ext(EventType::Sysex, &sysex[..]); + let me = MidiEvent::new(0).unwrap(); + let mut buffer = vec![0; sysex.len()]; + assert_eq!(me.decode(&mut buffer[..], &mut ev).unwrap(), sysex.len()); + assert_eq!(buffer, sysex); +} + +#[test] +#[should_panic] +fn seq_get_input_twice() { + use std::ffi::CString; + let s = super::Seq::open(None, None, false).unwrap(); + s.set_client_name(&CString::new("rust_test_seq_get_input_twice").unwrap()).unwrap(); + let input1 = s.input(); + let input2 = s.input(); // this should panic + let _ = (input1, input2); +} + +#[test] +fn seq_has_data() { + for v in EventType::all() { + let v = *v; + let mut i = 0; + if <() as EventData>::has_data(v) { i += 1; } + if <[u8; 12] as EventData>::has_data(v) { i += 1; } + if Event::has_ext_data(v) { i += 1; } + if EvNote::has_data(v) { i += 1; } + if EvCtrl::has_data(v) { i += 1; } + if Addr::has_data(v) { i += 1; } + if Connect::has_data(v) { i += 1; } + if EvResult::has_data(v) { i += 1; } + if EvQueueControl::<()>::has_data(v) { i += 1; } + if EvQueueControl::::has_data(v) { i += 1; } + if EvQueueControl::::has_data(v) { i += 1; } + if EvQueueControl::::has_data(v) { i += 1; } + if i != 1 { panic!("{:?}: {} has_data", v, i) } + } +} + +#[test] +fn seq_remove_events() -> std::result::Result<(), Box> { + let info = RemoveEvents::new()?; + + + info.set_condition(Remove::INPUT | Remove::DEST | Remove::TIME_BEFORE | Remove::TAG_MATCH); + info.set_queue(123); + info.set_time(time::Duration::new(456, 789)); + info.set_dest(Addr { client: 212, port: 121 }); + info.set_channel(15); + info.set_event_type(EventType::Noteon); + info.set_tag(213); + + assert_eq!(info.get_condition(), Remove::INPUT | Remove::DEST | Remove::TIME_BEFORE | Remove::TAG_MATCH); + assert_eq!(info.get_queue(), 123); + assert_eq!(info.get_time(), time::Duration::new(456, 789)); + assert_eq!(info.get_dest(), Addr { client: 212, port: 121 }); + assert_eq!(info.get_channel(), 15); + assert_eq!(info.get_event_type()?, EventType::Noteon); + assert_eq!(info.get_tag(), 213); + + Ok(()) +} + +#[test] +fn seq_portsubscribeiter() { + let s = super::Seq::open(None, None, false).unwrap(); + + // Create ports + let sinfo = PortInfo::empty().unwrap(); + sinfo.set_capability(PortCap::READ | PortCap::SUBS_READ); + sinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION); + s.create_port(&sinfo).unwrap(); + let sport = sinfo.get_port(); + let dinfo = PortInfo::empty().unwrap(); + dinfo.set_capability(PortCap::WRITE | PortCap::SUBS_WRITE); + dinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION); + s.create_port(&dinfo).unwrap(); + let dport = dinfo.get_port(); + + // Connect them + let subs = PortSubscribe::empty().unwrap(); + subs.set_sender(Addr { client: s.client_id().unwrap(), port: sport }); + subs.set_dest(Addr { client: s.client_id().unwrap(), port: dport }); + s.subscribe_port(&subs).unwrap(); + + // Query READ subs from sport's point of view + let read_subs: Vec = PortSubscribeIter::new(&s, + Addr {client: s.client_id().unwrap(), port: sport }, + QuerySubsType::READ).collect(); + assert_eq!(read_subs.len(), 1); + assert_eq!(read_subs[0].get_sender(), subs.get_sender()); + assert_eq!(read_subs[0].get_dest(), subs.get_dest()); + + let write_subs: Vec = PortSubscribeIter::new(&s, + Addr {client: s.client_id().unwrap(), port: sport }, + QuerySubsType::WRITE).collect(); + assert_eq!(write_subs.len(), 0); + + // Now query WRITE subs from dport's point of view + let write_subs: Vec = PortSubscribeIter::new(&s, + Addr {client: s.client_id().unwrap(), port: dport }, + QuerySubsType::WRITE).collect(); + assert_eq!(write_subs.len(), 1); + assert_eq!(write_subs[0].get_sender(), subs.get_sender()); + assert_eq!(write_subs[0].get_dest(), subs.get_dest()); +} diff --git a/alsa/synth-example/Cargo.toml b/alsa/synth-example/Cargo.toml new file mode 100644 index 0000000..0390f2a --- /dev/null +++ b/alsa/synth-example/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "synth-example" +version = "0.1.0" +authors = ["David Henningsson "] +edition = "2018" + +[dependencies] +dasp = { version = "0.11", features = ["signal"] } +alsa = { path = "..", version = "0.6" } diff --git a/alsa/synth-example/src/main.rs b/alsa/synth-example/src/main.rs new file mode 100644 index 0000000..5ef98e4 --- /dev/null +++ b/alsa/synth-example/src/main.rs @@ -0,0 +1,311 @@ +// A quickly made Hammond organ. + +use std::{iter, error}; +use alsa::{seq, pcm}; +use std::ffi::CString; +use dasp::signal; + +type Res = Result>; + +fn connect_midi_source_ports(s: &alsa::Seq, our_port: i32) -> Res<()> { + // Iterate over clients and clients' ports + let our_id = s.client_id()?; + let ci = seq::ClientIter::new(&s); + for client in ci { + if client.get_client() == our_id { continue; } // Skip ourselves + let pi = seq::PortIter::new(&s, client.get_client()); + for port in pi { + let caps = port.get_capability(); + + // Check that it's a normal input port + if !caps.contains(seq::PortCap::READ) || !caps.contains(seq::PortCap::SUBS_READ) { continue; } + if !port.get_type().contains(seq::PortType::MIDI_GENERIC) { continue; } + + // Connect source and dest ports + let subs = seq::PortSubscribe::empty()?; + subs.set_sender(seq::Addr { client: port.get_client(), port: port.get_port() }); + subs.set_dest(seq::Addr { client: our_id, port: our_port }); + println!("Reading from midi input {:?}", port); + s.subscribe_port(&subs)?; + } + } + + Ok(()) +} + +fn open_midi_dev() -> Res { + // Open the sequencer. + let s = alsa::Seq::open(None, Some(alsa::Direction::Capture), true)?; + let cstr = CString::new("rust_synth_example").unwrap(); + s.set_client_name(&cstr)?; + + // Create a destination port we can read from + let mut dinfo = seq::PortInfo::empty().unwrap(); + dinfo.set_capability(seq::PortCap::WRITE | seq::PortCap::SUBS_WRITE); + dinfo.set_type(seq::PortType::MIDI_GENERIC | seq::PortType::APPLICATION); + dinfo.set_name(&cstr); + s.create_port(&dinfo).unwrap(); + let dport = dinfo.get_port(); + + // source ports should ideally be configurable, but right now we're just reading them all. + connect_midi_source_ports(&s, dport)?; + + Ok(s) +} + +fn open_audio_dev() -> Res<(alsa::PCM, u32)> { + let args: Vec<_> = std::env::args().collect(); + if args.len() < 2 { + println!("Usage: 'cargo run --release CARD_NAME SAMPLE_RATE BUF_SIZE'"); + Err("No card name specified")? + } + let req_devname = format!("hw:{}", args[1]); + let req_samplerate = args.get(2).map(|x| x.parse()).unwrap_or(Ok(48000))?; + let req_bufsize = args.get(3).map(|x| x.parse()).unwrap_or(Ok(256))?; // A few ms latency by default, that should be nice + + // Open the device + let p = alsa::PCM::new(&req_devname, alsa::Direction::Playback, false)?; + + // Set hardware parameters + { + let hwp = pcm::HwParams::any(&p)?; + hwp.set_channels(2)?; + hwp.set_rate(req_samplerate, alsa::ValueOr::Nearest)?; + hwp.set_format(pcm::Format::s16())?; + hwp.set_access(pcm::Access::MMapInterleaved)?; + hwp.set_buffer_size(req_bufsize)?; + hwp.set_period_size(req_bufsize / 4, alsa::ValueOr::Nearest)?; + p.hw_params(&hwp)?; + } + + // Set software parameters + let rate = { + let hwp = p.hw_params_current()?; + let swp = p.sw_params_current()?; + let (bufsize, periodsize) = (hwp.get_buffer_size()?, hwp.get_period_size()?); + swp.set_start_threshold(bufsize - periodsize)?; + swp.set_avail_min(periodsize)?; + p.sw_params(&swp)?; + println!("Opened audio output {:?} with parameters: {:?}, {:?}", req_devname, hwp, swp); + hwp.get_rate()? + }; + + Ok((p, rate)) +} + +// Sample format +type SF = i16; + +type SigGen = signal::Sine; + +// Standard Hammond drawbar. +const BAR_FREQS: [f64; 9] = [16., 5.+1./3., 8., 4., 2.+2./3., 2., 1.+3./5., 1.+1./3., 1.]; + +#[derive(Clone)] +struct Sig { + note: u8, + sig: SigGen, + targetvol: f64, + curvol: f64, + baridx: usize, +} + + +struct Synth { + sigs: Vec>, + sample_rate: signal::Rate, + stored_sample: Option, + bar_values: [f64; 9], +} + +impl Synth { + fn add_note(&mut self, note: u8, vol: f64) { + let hz = 440. * 2_f64.powf((note as f64 - 69.)/12.); + + for (baridx, barfreq) in BAR_FREQS.iter().enumerate() { + let idx = self.sigs.iter().position(|s| s.is_none()); + let idx = if let Some(idx) = idx { idx } else { + println!("Voice overflow!"); return; + }; + let hz = self.sample_rate.const_hz(hz * 8. / barfreq); + let s = Sig { sig: hz.sine(), note, targetvol: vol, curvol: 0., baridx }; + self.sigs[idx] = Some(s); + } + } + fn remove_note(&mut self, note: u8) { + for i in self.sigs.iter_mut() { + if let &mut Some(ref mut i) = i { + if i.note == note { i.targetvol = 0. } + } + } + } + fn cc(&mut self, ctrl: u32, value: i32) { + let idx = match ctrl { + // Standard knobs on UMA25S, modify to your liking + 1 => 0, + 74 => 1, + 71 => 2, + 73 => 3, + 75 => 4, + 72 => 5, + 91 => 6, + 93 => 7, + 10 => 8, + _ => return, + }; + self.bar_values[idx] = f64::from(value) / 255.; + } +} + +impl Iterator for Synth { + type Item = SF; + fn next(&mut self) -> Option { + use dasp::{signal::Signal, Sample}; + + // Mono -> Stereo + if let Some(s) = self.stored_sample.take() { return Some(s) }; + + let mut z = 0f64; + for sig in &mut self.sigs { + let mut remove = false; + if let &mut Some(ref mut i) = sig { + let barvalue = self.bar_values[i.baridx]; + if barvalue > 0.0 { + let s = i.sig.next(); + z += s.mul_amp(i.curvol * barvalue); + } + + // Quick and dirty volume envelope to avoid clicks. + if i.curvol != i.targetvol { + if i.targetvol == 0. { + i.curvol -= 0.002; + if i.curvol <= 0. { remove = true; } + } else { + i.curvol += 0.002; + if i.curvol >= i.targetvol { i.curvol = i.targetvol; } + } + } + } + if remove { *sig = None }; + } + let z = z.min(0.999).max(-0.999); + let z: Option = Some(SF::from_sample(z)); + self.stored_sample = z; + z + } +} + +fn write_samples_direct(p: &alsa::PCM, mmap: &mut alsa::direct::pcm::MmapPlayback, synth: &mut Synth) + -> Res { + + if mmap.avail() > 0 { + // Write samples to DMA area from iterator + mmap.write(synth); + } + use alsa::pcm::State; + match mmap.status().state() { + State::Running => { return Ok(false); }, // All fine + State::Prepared => { println!("Starting audio output stream"); p.start()? }, + State::XRun => { println!("Underrun in audio output stream!"); p.prepare()? }, + State::Suspended => { println!("Resuming audio output stream"); p.resume()? }, + n @ _ => Err(format!("Unexpected pcm state {:?}", n))?, + } + Ok(true) // Call us again, please, there might be more data to write +} + +fn write_samples_io(p: &alsa::PCM, io: &mut alsa::pcm::IO, synth: &mut Synth) -> Res { + let avail = match p.avail_update() { + Ok(n) => n, + Err(e) => { + println!("Recovering from {}", e); + p.recover(e.errno() as std::os::raw::c_int, true)?; + p.avail_update()? + } + } as usize; + + if avail > 0 { + io.mmap(avail, |buf| { + for sample in buf.iter_mut() { + *sample = synth.next().unwrap() + }; + buf.len() / 2 + })?; + } + use alsa::pcm::State; + match p.state() { + State::Running => Ok(false), // All fine + State::Prepared => { println!("Starting audio output stream"); p.start()?; Ok(true) }, + State::Suspended | State::XRun => Ok(true), // Recover from this in next round + n @ _ => Err(format!("Unexpected pcm state {:?}", n))?, + } +} + +fn read_midi_event(input: &mut seq::Input, synth: &mut Synth) -> Res { + if input.event_input_pending(true)? == 0 { return Ok(false); } + let ev = input.event_input()?; + // println!("Received: {:?}", ev); + match ev.get_type() { + seq::EventType::Noteon => { + let data: seq::EvNote = ev.get_data().unwrap(); + if data.velocity == 0 { + synth.remove_note(data.note); + } else { + synth.add_note(data.note, f64::from(data.velocity + 64) / 2048.); + } + }, + seq::EventType::Noteoff => { + let data: seq::EvNote = ev.get_data().unwrap(); + synth.remove_note(data.note); + }, + seq::EventType::Controller => { + let data: seq::EvCtrl = ev.get_data().unwrap(); + synth.cc(data.param, data.value); + } + _ => {}, + } + Ok(true) +} + + +fn run() -> Res<()> { + let (audio_dev, rate) = open_audio_dev()?; + let midi_dev = open_midi_dev()?; + + let mut midi_input = midi_dev.input(); + + // 256 Voices synth + let mut synth = Synth { + sigs: iter::repeat(None).take(256).collect(), + sample_rate: signal::rate(f64::from(rate)), + stored_sample: None, + bar_values: [1., 0.75, 1., 0.75, 0., 0., 0., 0., 0.75], // Some Gospel-ish default. + }; + + // Create an array of fds to poll. + use alsa::PollDescriptors; + let mut fds = audio_dev.get()?; + fds.append(&mut (&midi_dev, Some(alsa::Direction::Capture)).get()?); + + // Let's use the fancy new "direct mode" for minimum overhead! + let mut mmap = audio_dev.direct_mmap_playback::(); + + // Direct mode unavailable, use alsa-lib's mmap emulation instead + let mut io = if mmap.is_err() { + Some(audio_dev.io_i16()?) + } else { None }; + + loop { + if let Ok(ref mut mmap) = mmap { + if write_samples_direct(&audio_dev, mmap, &mut synth)? { continue; } + } else if let Some(ref mut io) = io { + if write_samples_io(&audio_dev, io, &mut synth)? { continue; } + } + if read_midi_event(&mut midi_input, &mut synth)? { continue; } + // Nothing to do, let's sleep until woken up by the kernel. + alsa::poll::poll(&mut fds, 100)?; + } +} + +fn main() { + if let Err(e) = run() { println!("Error: {}", e); } +} diff --git a/j314.conf b/j314.conf new file mode 100644 index 0000000..adbf06e --- /dev/null +++ b/j314.conf @@ -0,0 +1,59 @@ +[Left Tweeter] +r_shunt = 1 +r_dc = 2 +r_amp = 3 +tau_coil = 4 +tau_magnet = 5 +tr_coil = 6 +ramp_factor = 7 +temp_limit = 100 + +[Right Tweeter] +r_shunt = 1 +r_dc = 2 +r_amp = 3 +tau_coil = 4 +tau_magnet = 5 +tr_coil = 6 +ramp_factor = 7 +temp_limit = 100 + +[Left Woofer 1] +r_shunt = 1 +r_dc = 2 +r_amp = 3 +tau_coil = 4 +tau_magnet = 5 +tr_coil = 6 +ramp_factor = 7 +temp_limit = 100 + +[Right Woofer 1] +r_shunt = 1 +r_dc = 2 +r_amp = 3 +tau_coil = 4 +tau_magnet = 5 +tr_coil = 6 +ramp_factor = 7 +temp_limit = 100 + +[Left Woofer 2] +r_shunt = 1 +r_dc = 2 +r_amp = 3 +tau_coil = 4 +tau_magnet = 5 +tr_coil = 6 +ramp_factor = 7 +temp_limit = 100 + +[Right Woofer 2] +r_shunt = 1 +r_dc = 2 +r_amp = 3 +tau_coil = 4 +tau_magnet = 5 +tr_coil = 6 +ramp_factor = 7 +temp_limit = 100 diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..f9eb071 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +// (C) 2022 The Asahi Linux Contributors + +use configparser::ini::Ini; +use alsa::mixer::MilliBel; + +/** + Failsafe: Limit speaker volume massively and bail. + + TODO: enable TAS safe mode with IOCTL. +*/ +pub fn fail() { + println!("A catastrophic error has occurred."); + std::process::exit(1); +} + +pub fn open_card(card: &str) -> alsa::ctl::Ctl { + let ctldev: alsa::ctl::Ctl = match alsa::ctl::Ctl::new(card, false) { + Ok(ctldev) => ctldev, + Err(e) => { + println!("{}: Could not open sound card! Error: {}", card, e); + fail(); + std::process::exit(1); + }, + }; + + return ctldev; +} + +/** + Wrapper around configparser::ini::Ini.getfloat() + to safely unwrap the Result, E> returned by + it. +*/ +pub fn parse_float(config: &Ini, section: &str, key: &str) -> f64 { + let _result: Option = match config.getfloat(section, key) { + Ok(result) => match result{ + Some(inner) => { + let float: f64 = inner; + return float; + }, + None => { + println!("{}: Failed to parse {}", section, key); + fail(); + std::process::exit(1); + }, + }, + Err(e) => { + println!("{}: Invalid value for {}. Error: {}", section, key, e); + fail(); + std::process::exit(1); + }, + }; + +} + +/** + Wrapper around alsa::ctl::ElemValue::new(). Lets us bail on errors and + pass in the Bytes type for V/ISENSE +*/ +pub fn new_elemvalue(t: alsa::ctl::ElemType) -> alsa::ctl::ElemValue { + let val = match alsa::ctl::ElemValue::new(t) { + Ok(val) => val, + Err(_e) => { + println!("Could not open a handle to an element!"); + fail(); + std::process::exit(1); + }, + }; + + return val; +} + + +/** + Wrapper for alsa::ctl::Ctl::elem_read(). +*/ +pub fn read_ev(card: &alsa::ctl::Ctl, ev: &mut alsa::ctl::ElemValue, name: &str) { + let _val = match card.elem_read(ev) { // alsa:Result<()> + Ok(val) => val, + Err(e) => { + println!("Could not read elem value {}. alsa-lib error: {:?}", name, e); + fail(); + std::process::exit(1); + }, + }; +} + +/** + Wrapper for alsa::ctl::Ctl::elem_write(). +*/ +pub fn write_ev(card: &alsa::ctl::Ctl, ev: &alsa::ctl::ElemValue, name: &str) { + let _val = match card.elem_write(ev) { // alsa:Result<()> + Ok(val) => val, + Err(e) => { + println!("Could not write elem value {}. alsa-lib error: {:?}", name, e); + fail(); + std::process::exit(1); + }, + }; +} + +pub fn int_to_db(card: &alsa::ctl::Ctl, id: &alsa::ctl::ElemId, val: i32) -> MilliBel { + let db = match card.convert_to_db(id, val.into()) { + Ok(inner) => inner, + Err(e) => { + println!("Could not convert val {} to dB! alsa-lib error: {:?}", val, e); + fail(); + std::process::exit(1); + }, + }; + + return db; +} + +pub fn db_to_int(card: &alsa::ctl::Ctl, id: &alsa::ctl::ElemId, val: f32) -> i32 { + let mb: MilliBel = MilliBel((val * 100.0) as i64); + let new_int = match card.convert_from_db(id, mb, alsa::Round::Floor) { + Ok(inner) => inner as i32, + Err(e) => { + println!("Could not convert MilliBel {:?} to int! alsa-lib error: {:?}", val, e); + fail(); + std::process::exit(1); + }, + }; + + return new_int; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..56e212c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +// (C) 2022 The Asahi Linux Contributors + + +/** + Handles speaker safety on Apple Silicon machines. This code is designed to + fail safe. The speaker should not be enabled until this daemon has successfully + initialised. If at any time we run into an unrecoverable error (we shouldn't), + we gracefully bail and use an IOCTL to shut off the speakers. +*/ + + +use std::io; +use std::fs::read_to_string; + +use configparser::ini::Ini; + +mod types; +mod helpers; + +use crate::types::SafetyMonitor; + +static ASAHI_DEVICE: &str = "hw:0"; + +// Will eventually be /etc/speakersafetyd/ or similar +static CONFIG_DIR: &str = "./"; +static SUPPORTED: [&str; 2] = [ + "j314", + "j316", +]; + +fn get_machine() -> String { + let _compat: io::Result = match read_to_string("/proc/device-tree/compatible") { + Ok(compat) => { + return compat[6..10].to_string(); + }, + Err(e) => { + println!("Could not read devicetree compatible: {}", e); + std::process::exit(1); + } + }; + +} + + +fn get_drivers(config: &Ini) -> Vec { + + let drivers = config.sections(); + + return drivers; +} + + +fn main() { + let model: String = get_machine(); + let mut cfg: Ini = Ini::new_cs(); + let mut speakers: Vec = Vec::new(); + let card: alsa::ctl::Ctl = helpers::open_card(&ASAHI_DEVICE); + + if SUPPORTED.contains(&model.as_str()) { + cfg.load(CONFIG_DIR.to_owned() + &model + ".conf").unwrap(); + } else { + println!("Unsupported machine {}", model); + std::process::exit(1); + } + + let list_drivers = get_drivers(&cfg); + + for i in list_drivers { + let new_speaker: types::Speaker = types::SafetyMonitor::new(&i, &cfg, &card); + speakers.push(new_speaker); + } + + // Temporary to check that everything works. Threaded eventually if necessary. + for mut i in speakers { + i.run(&card); + } + + +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..6bcf0d4 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: MIT +// (C) 2022 The Asahi Linux Contributors + +use std::ffi::{CString, CStr}; +use half::f16; +use configparser::ini::Ini; +use alsa::ctl::Ctl; + +use crate::helpers; + +/** + Struct with fields necessary for manipulating an ALSA elem. + + The val field is created using a wrapper so that we can handle + any errors. This is also necessary so that we can create one of type + Bytes for the V/ISENSE elems. +*/ +struct Elem { + elem_name: String, + id: alsa::ctl::ElemId, + val: alsa::ctl::ElemValue, +} + +trait ALSAElem { + fn new(name: String, card: &Ctl, t: alsa::ctl::ElemType) -> Self; +} + +impl ALSAElem for Elem { + fn new(name: String, card: &Ctl, t: alsa::ctl::ElemType) -> Elem { + // CString::new() cannot borrow a String. We want name for the elem + // for error identification though, so it can't consume name directly. + let borrow: String = name.clone(); + + let mut new_elem: Elem = { Elem { + elem_name: name, + id: alsa::ctl::ElemId::new(alsa::ctl::ElemIface::Mixer), + val: helpers::new_elemvalue(t), + }}; + + let cname: CString = CString::new(borrow).unwrap(); + let cstr: &CStr = cname.as_c_str(); + + new_elem.id.set_name(cstr); + new_elem.val.set_id(&new_elem.id); + helpers::read_ev(card, &mut new_elem.val, &new_elem.elem_name); + + return new_elem; + } +} + +/** + Mixer struct representing the controls associated with a given + Speaker. Populated with the important ALSA controls at runtime. + + level: mixer volume control + vsense: VSENSE as reported by the driver (V, readonly) + isense: ISENSE as reported by the driver (A, readonly) + +*/ +struct Mixer { + drv: String, + level: Elem, + vsense: Elem, + isense: Elem, +} + +trait ALSACtl { + fn new(name: &str, card: &Ctl) -> Self; + + fn get_vsense(&mut self, card: &Ctl) -> f16; + fn get_isense(&mut self, card: &Ctl) -> f16; + fn get_lvl(&mut self, card: &Ctl) -> f32; + fn set_lvl(&mut self, card: &Ctl, lvl: f32); +} + +impl ALSACtl for Mixer { + // TODO: wire up real V/ISENSE elems (pending driver support) + fn new(name: &str, card: &Ctl) -> Mixer { + let new_mixer: Mixer = { Mixer { + drv: name.to_owned(), + level: ALSAElem::new(name.to_owned() + " Speaker Volume", card, + alsa::ctl::ElemType::Integer), + vsense: ALSAElem::new(name.to_owned() + " VSENSE Switch", card, + alsa::ctl::ElemType::Boolean), + isense: ALSAElem::new(name.to_owned() + " ISENSE Switch", card, + alsa::ctl::ElemType::Boolean), + }}; + + return new_mixer; + } + + /** + MOCK IMPLEMENTATIONS + + V/ISENSE are 16-bit floats sent in a 32-bit TDM slot by the codec. + This is expressed by the driver as a byte array, with rightmost 16 + bits as padding. + + TODO: Condense into a single function and pass in a borrowed Elem + */ + fn get_vsense(&mut self, card: &Ctl) -> f16 { + helpers::read_ev(card, &mut self.vsense.val, &self.vsense.elem_name); + let val: &[u8] = match self.vsense.val.get_bytes() { + Some(inner) => inner, + None => { + println!("Could not read VSENSE from {}", self.drv); + helpers::fail(); + std::process::exit(1); + } + }; + + + let vs = f16::from_ne_bytes([val[0], val[1]]); + + return vs; + } + + fn get_isense(&mut self, card: &Ctl) -> f16 { + helpers::read_ev(card, &mut self.isense.val, &self.isense.elem_name); + let val: &[u8] = match self.vsense.val.get_bytes() { + Some(inner) => inner, + None => { + println!("Could not read ISENSE from {}", self.drv); + helpers::fail(); + std::process::exit(1); + } + }; + + + let is = f16::from_ne_bytes([val[0], val[1]]); + + return is; + } + + fn get_lvl(&mut self, card: &Ctl) -> f32 { + helpers::read_ev(card, &mut self.level.val, &self.level.elem_name); + + let val: i32 = match self.level.val.get_integer(0) { + Some(inner) => inner, + None => { + println!("Could not read level from {}", self.drv); + helpers::fail(); + std::process::exit(1); + }, + }; + + let db: f32 = helpers::int_to_db(card, &self.level.id, val).to_db(); + + return db; + } + + fn set_lvl(&mut self, card: &Ctl, lvl: f32) { + + let new_val: i32 = helpers::db_to_int(card, &self.level.id, lvl); + + match self.level.val.set_integer(0, new_val) { + Some(_) => {}, + None => { + println!("Could not set level for {}", self.drv); + helpers::fail(); + std::process::exit(1); + }, + }; + + helpers::write_ev(card, &self.level.val, &self.level.elem_name); + + } +} + + +/** + Struct representing a driver. Parameters are parsed out of a config + file, which is loaded at runtime based on the machine's DT compatible + string. + + name: driver name as it appears in ALSA + alsa_iface: Mixer struct with handles to the driver's control elements + r_dc: dc resistance of the voice coil (ohms) + tau_coil: voice coil ramp time constant (seconds) + tau_magnet: magnet ramp time constant (seconds) + tr_coil: thermal resistance of voice coil (*C/W) + temp_limit: absolute max temp of the voice coil (*C) + + Borrows the handle to the control interface to do calculations. +*/ +pub struct Speaker { + name: String, + alsa_iface: Mixer, + tau_coil: f64, + tr_coil: f64, + temp_limit: f64, +} + + +pub trait SafetyMonitor { + fn new(driver_name: &str, config: &Ini, card: &Ctl) -> Self; + + fn run(&mut self, card: &Ctl); +} + +impl SafetyMonitor for Speaker { + fn new(driver_name: &str, config: &Ini, card: &Ctl) -> Speaker { + let new_speaker: Speaker = { Speaker { + name: driver_name.to_string(), + alsa_iface: ALSACtl::new(&driver_name, card), + tau_coil: helpers::parse_float(config, driver_name, "tau_coil"), + tr_coil: helpers::parse_float(config, driver_name, "tr_coil"), + temp_limit: helpers::parse_float(config, driver_name, "temp_limit"), + }}; + + return new_speaker; + } + + // I'm not sure on the maths here for determining when to start dropping the volume. + fn run(&mut self, card: &Ctl) { + //let v: f16 = self.alsa_iface.get_vsense(card); + //let i: f16 = self.alsa_iface.get_isense(card); + let lvl: f32 = self.alsa_iface.get_lvl(card); + + // Technically, this is the temp ~tau_coil seconds in the future + //let temp: f64 = ((v * i).to_f64()) * self.tr_coil; + + // if temp < self.temp_limit && lvl < 0f32 { + // println!("Voice coil for {} below temp limit, ramping back up.", self.name); + // + // // For every degree below temp_limit, raise level by 0.5 dB + // let new_lvl: f32 = lvl + ((self.temp_limit - temp) as f32 * 0.5); + // self.alsa_iface.set_lvl(card, new_lvl); + // } + // + // if temp > self.temp_limit { + // println!("Voice coil at {}*C in {} on {}! Dropping volume!", temp, self.tau_coil, self.name); + // + // // For every degree above temp_limit, drop the level by 1.5 dB + // let new_lvl: f32 = lvl - ((temp - self.temp_limit) as f32 * 1.5); + // self.alsa_iface.set_lvl(card, new_lvl); + // } + + // TEMPORARY PROOF THAT THIS WORKS! + + println!("Volume on {} is currently {} dB. Setting to -18 dB.", self.name, lvl); + + let new_lvl: f32 = -18.0; + self.alsa_iface.set_lvl(card, new_lvl); + + println!("Volume on {} is now {} dB", self.name, self.alsa_iface.get_lvl(card)); + + } + +}