libpulse_simple_binding/
lib.rs

1// Copyright 2017 Lyndon Brown
2//
3// This file is part of the PulseAudio Rust language binding.
4//
5// Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
6// copy, modify, or distribute this file except in compliance with said license. You can find copies
7// of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
8// <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
9// respectively.
10//
11// Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a
12// fair-use basis, as discussed in the overall project readme (available in the git repository).
13
14//! A binding for the PulseAudio binding ‘simple’ interface (`libpulse-simple` system library).
15//!
16//! # About
17//!
18//! This binding enables Rust projects to make use of the ‘simple’ interface of the [PulseAudio]
19//! client system library. It builds upon the [separate raw FFI crate][sys] to provide a more
20//! “Rusty” interface.
21//!
22//! The ‘simple’ interface provides a simple but limited synchronous playback and recording API. It
23//! is a synchronous, simplified wrapper around the standard asynchronous API.
24//!
25//! Note that you will need components of the primary [`libpulse-binding`] crate to make use of
26//! this.
27//!
28//! # Introduction
29//!
30//! The simple API is designed for applications with very basic sound playback or capture needs. It
31//! can only support a single stream per connection and has no support for handling of complex
32//! features like events, channel mappings and volume control. It is, however, very simple to use
33//! and quite sufficient for many programs.
34//!
35//! # Usage
36//!
37//! Start by adding a dependency on the crate, along with the main binding crate, in your program’s
38//! `Cargo.toml` file. Note that it is recommended that you rename the crates such that you can
39//! refer to them by shorter names within your code (such as `pulse` and `psimple` as used below).
40//! Such renaming can be done [within your `Cargo.toml` file][rename] with cargo version 1.31 or
41//! newer, or otherwise with `extern crate` statements.
42//!
43//! Finally, establish a connection, as below.
44//!
45//! # Connecting
46//!
47//! The first step before using the sound system is to connect to the server. This is normally done
48//! this way:
49//!
50//! ```rust
51//! # extern crate libpulse_binding as pulse;
52//! # extern crate libpulse_simple_binding as psimple;
53//! #
54//! use psimple::Simple;
55//! use pulse::stream::Direction;
56//! use pulse::sample::{Spec, Format};
57//!
58//! # fn main() {
59//! let spec = Spec {
60//!     format: Format::S16NE,
61//!     channels: 2,
62//!     rate: 44100,
63//! };
64//! assert!(spec.is_valid());
65//!
66//! let s = Simple::new(
67//!     None,                // Use the default server
68//!     "FooApp",            // Our application’s name
69//!     Direction::Playback, // We want a playback stream
70//!     None,                // Use the default device
71//!     "Music",             // Description of our stream
72//!     &spec,               // Our sample format
73//!     None,                // Use default channel map
74//!     None                 // Use default buffering attributes
75//! ).unwrap();
76//! # }
77//! ```
78//!
79//! # Transferring data
80//!
81//! Once the connection is established to the server, data can start flowing. Using the connection
82//! is very similar to the normal read() and write() system calls using [`Simple::read()`] and
83//! [`Simple::write()`] methods of the [`Simple`] object. Note that these operations always block.
84//!
85//! # Buffer control
86//!
87//! * [`Simple::get_latency()`]: Will return the total latency of the playback or record pipeline,
88//!   respectively.
89//! * [`Simple::flush()`]: Will throw away all data currently in buffers.
90//!
91//! If a playback stream is used then the following operation is available:
92//!
93//! * [`Simple::drain()`]: Will wait for all sent data to finish playing.
94//!
95//! # Cleanup
96//!
97//! Once playback or capture is complete, the connection should be closed and resources freed. This
98//! is done automatically once the [`Simple`] object is dropped.
99//!
100//! [sys]: https://docs.rs/libpulse-simple-sys
101//! [`libpulse-binding`]: https://docs.rs/libpulse-binding
102//! [PulseAudio]: https://en.wikipedia.org/wiki/PulseAudio
103//! [rename]: https://doc.rust-lang.org/1.31.0/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml
104
105#![doc(
106    html_logo_url = "https://github.com/jnqnfe/pulse-binding-rust/raw/master/logo.svg",
107    html_favicon_url = "https://github.com/jnqnfe/pulse-binding-rust/raw/master/favicon.ico"
108)]
109
110#![warn(missing_docs)]
111
112#![cfg_attr(docsrs, feature(doc_cfg))]
113
114extern crate libpulse_binding as pulse;
115extern crate libpulse_sys as pcapi;
116extern crate libpulse_simple_sys as capi;
117
118use std::os::raw::{c_char, c_void};
119use std::{ffi::CString, ptr::null};
120use std::mem;
121use pulse::error::{Code, PAErr};
122use pulse::time::MicroSeconds;
123use pulse::{stream, sample, channelmap, def};
124
125use capi::pa_simple as SimpleInternal;
126
127/// An opaque simple connection object.
128pub struct Simple {
129    /// The actual C object.
130    ptr: *mut SimpleInternal,
131}
132
133unsafe impl Send for Simple {}
134unsafe impl Sync for Simple {}
135
136impl Simple {
137    /// Creates a new connection to the server.
138    ///
139    /// # Params
140    ///
141    /// * `server`: Server name, or `None` for default.
142    /// * `name`: A descriptive name for this client (application name, ...).
143    /// * `dir`: Open this stream for recording or playback?
144    /// * `dev`: Sink (resp. source) name, or `None` for default.
145    /// * `stream_name`: A descriptive name for this stream (application name, song title, ...).
146    /// * `ss`: The sample type to use.
147    /// * `map`: The channel map to use, or `None` for default.
148    /// * `attr`: Buffering attributes, or `None` for default.
149    pub fn new(server: Option<&str>, name: &str, dir: stream::Direction, dev: Option<&str>,
150        stream_name: &str, ss: &sample::Spec, map: Option<&channelmap::Map>,
151        attr: Option<&def::BufferAttr>) -> Result<Self, PAErr>
152    {
153        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
154        // as_ptr() giving dangling pointers!
155        let c_server = match server {
156            Some(server) => CString::new(server).unwrap(),
157            None => CString::new("").unwrap(),
158        };
159        let c_dev = match dev {
160            Some(dev) => CString::new(dev).unwrap(),
161            None => CString::new("").unwrap(),
162        };
163
164        let p_map = map.map_or(null::<pcapi::pa_channel_map>(), |m| m.as_ref());
165        let p_attr = attr.map_or(null::<pcapi::pa_buffer_attr>(), |a| a.as_ref());
166        let p_server = server.map_or(null::<c_char>(), |_| c_server.as_ptr() as *const c_char);
167        let p_dev = dev.map_or(null::<c_char>(), |_| c_dev.as_ptr() as *const c_char);
168        let c_name = CString::new(name).unwrap();
169        let c_stream_name = CString::new(stream_name).unwrap();
170
171        let mut error: i32 = 0;
172        let ptr = unsafe {
173            capi::pa_simple_new(
174                p_server,
175                c_name.as_ptr(),
176                dir,
177                p_dev,
178                c_stream_name.as_ptr(),
179                mem::transmute(ss),
180                p_map,
181                p_attr,
182                &mut error
183            )
184        };
185        match ptr.is_null() {
186            false => Ok(Self::from_raw(ptr)),
187            true => Err(PAErr(error)),
188        }
189    }
190
191    /// Creates a new `Simple` from an existing [`SimpleInternal`] pointer.
192    fn from_raw(ptr: *mut SimpleInternal) -> Self {
193        assert_eq!(false, ptr.is_null());
194        Self { ptr }
195    }
196
197    /// Writes some data to the server.
198    pub fn write(&self, data: &[u8]) -> Result<(), PAErr> {
199        let mut error: i32 = 0;
200        match unsafe { capi::pa_simple_write(self.ptr, data.as_ptr() as *mut c_void, data.len(),
201            &mut error) }
202        {
203            0 => Ok(()),
204            _ => Err(PAErr(error)),
205        }
206    }
207
208    /// Waits until all data already written is played by the daemon.
209    pub fn drain(&self) -> Result<(), PAErr> {
210        let mut error: i32 = 0;
211        match unsafe { capi::pa_simple_drain(self.ptr, &mut error) } {
212            0 => Ok(()),
213            _ => Err(PAErr(error)),
214        }
215    }
216
217    /// Reads some data from the server.
218    ///
219    /// This function blocks until `data.len()` amount of data has been received from the server,
220    /// or until an error occurs.
221    pub fn read(&self, data: &mut [u8]) -> Result<(), PAErr> {
222        let mut error: i32 = 0;
223        match unsafe { capi::pa_simple_read(self.ptr, data.as_mut_ptr() as *mut c_void, data.len(),
224            &mut error) }
225        {
226            0 => Ok(()),
227            _ => Err(PAErr(error)),
228        }
229    }
230
231    /// Gets the playback or record latency.
232    pub fn get_latency(&self) -> Result<MicroSeconds, PAErr> {
233        let mut error: i32 = 0;
234        let ret = unsafe { capi::pa_simple_get_latency(self.ptr, &mut error) };
235        if error != 0 {
236            return Err(PAErr(error));
237        }
238        match ret {
239            pcapi::PA_USEC_INVALID => Err(Code::Invalid.into()),
240            r => Ok(MicroSeconds(r)),
241        }
242    }
243
244    /// Flushes the playback or record buffer.
245    ///
246    /// This discards any audio in the buffer.
247    pub fn flush(&self) -> Result<(), PAErr> {
248        let mut error: i32 = 0;
249        match unsafe { capi::pa_simple_flush(self.ptr, &mut error) } {
250            0 => Ok(()),
251            _ => Err(PAErr(error)),
252        }
253    }
254}
255
256impl Drop for Simple {
257    fn drop(&mut self) {
258        // Close and free the connection to the server.
259        unsafe { capi::pa_simple_free(self.ptr) };
260        self.ptr = null::<SimpleInternal>() as *mut SimpleInternal;
261    }
262}