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 114 extern crate libpulse_binding as pulse; 115 extern crate libpulse_sys as pcapi; 116 extern crate libpulse_simple_sys as capi; 117 118 use std::os::raw::{c_char, c_void}; 119 use std::{ffi::CString, ptr::null}; 120 use std::mem; 121 use pulse::{error::PAErr, time::MicroSeconds}; 122 use pulse::{stream, sample, channelmap, def}; 123 124 use capi::pa_simple as SimpleInternal; 125 126 /// An opaque simple connection object. 127 pub struct Simple { 128 /// The actual C object. 129 ptr: *mut SimpleInternal, 130 } 131 132 unsafe impl Send for Simple {} 133 unsafe impl Sync for Simple {} 134 135 impl Simple { 136 /// Creates a new connection to the server. 137 /// 138 /// # Params 139 /// 140 /// * `server`: Server name, or `None` for default. 141 /// * `name`: A descriptive name for this client (application name, ...). 142 /// * `dir`: Open this stream for recording or playback? 143 /// * `dev`: Sink (resp. source) name, or `None` for default. 144 /// * `stream_name`: A descriptive name for this stream (application name, song title, ...). 145 /// * `ss`: The sample type to use. 146 /// * `map`: The channel map to use, or `None` for default. 147 /// * `attr`: Buffering attributes, or `None` for default. 148 pub fn new(server: Option<&str>, name: &str, dir: stream::Direction, dev: Option<&str>, 149 stream_name: &str, ss: &sample::Spec, map: Option<&channelmap::Map>, 150 attr: Option<&def::BufferAttr>) -> Result<Self, PAErr> 151 { 152 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to 153 // as_ptr() giving dangling pointers! 154 let c_server = match server { 155 Some(server) => CString::new(server.clone()).unwrap(), 156 None => CString::new("").unwrap(), 157 }; 158 let c_dev = match dev { 159 Some(dev) => CString::new(dev.clone()).unwrap(), 160 None => CString::new("").unwrap(), 161 }; 162 163 let p_map = map.map_or(null::<pcapi::pa_channel_map>(), |m| m.as_ref()); 164 let p_attr = attr.map_or(null::<pcapi::pa_buffer_attr>(), |a| a.as_ref()); 165 let p_server = server.map_or(null::<c_char>(), |_| c_server.as_ptr() as *const c_char); 166 let p_dev = dev.map_or(null::<c_char>(), |_| c_dev.as_ptr() as *const c_char); 167 let c_name = CString::new(name.clone()).unwrap(); 168 let c_stream_name = CString::new(stream_name.clone()).unwrap(); 169 170 let mut error: i32 = 0; 171 let ptr = unsafe { 172 capi::pa_simple_new( 173 p_server, 174 c_name.as_ptr(), 175 dir, 176 p_dev, 177 c_stream_name.as_ptr(), 178 mem::transmute(ss), 179 p_map, 180 p_attr, 181 &mut error 182 ) 183 }; 184 match ptr.is_null() { 185 false => Ok(Self::from_raw(ptr)), 186 true => Err(PAErr(error)), 187 } 188 } 189 190 /// Creates a new `Simple` from an existing [`SimpleInternal`] pointer. 191 fn from_raw(ptr: *mut SimpleInternal) -> Self { 192 assert_eq!(false, ptr.is_null()); 193 Self { ptr } 194 } 195 196 /// Writes some data to the server. 197 pub fn write(&self, data: &[u8]) -> Result<(), PAErr> { 198 let mut error: i32 = 0; 199 match unsafe { capi::pa_simple_write(self.ptr, data.as_ptr() as *mut c_void, data.len(), 200 &mut error) } 201 { 202 0 => Ok(()), 203 _ => Err(PAErr(error)), 204 } 205 } 206 207 /// Waits until all data already written is played by the daemon. 208 pub fn drain(&self) -> Result<(), PAErr> { 209 let mut error: i32 = 0; 210 match unsafe { capi::pa_simple_drain(self.ptr, &mut error) } { 211 0 => Ok(()), 212 _ => Err(PAErr(error)), 213 } 214 } 215 216 /// Reads some data from the server. 217 /// 218 /// This function blocks until `data.len()` amount of data has been received from the server, 219 /// or until an error occurs. 220 pub fn read(&self, data: &mut [u8]) -> Result<(), PAErr> { 221 let mut error: i32 = 0; 222 match unsafe { capi::pa_simple_read(self.ptr, data.as_mut_ptr() as *mut c_void, data.len(), 223 &mut error) } 224 { 225 0 => Ok(()), 226 _ => Err(PAErr(error)), 227 } 228 } 229 230 /// Gets the playback or record latency. 231 pub fn get_latency(&self) -> Option<MicroSeconds> { 232 let mut error: i32 = 0; 233 let ret = unsafe { capi::pa_simple_get_latency(self.ptr, &mut error) }; 234 if error != 0 { 235 return None; 236 } 237 Some(MicroSeconds(ret)) 238 } 239 240 /// Flushes the playback or record buffer. 241 /// 242 /// This discards any audio in the buffer. 243 pub fn flush(&self) -> Result<(), PAErr> { 244 let mut error: i32 = 0; 245 match unsafe { capi::pa_simple_flush(self.ptr, &mut error) } { 246 0 => Ok(()), 247 _ => Err(PAErr(error)), 248 } 249 } 250 } 251 252 impl Drop for Simple { 253 fn drop(&mut self) { 254 // Close and free the connection to the server. 255 unsafe { capi::pa_simple_free(self.ptr) }; 256 self.ptr = null::<SimpleInternal>() as *mut SimpleInternal; 257 } 258 } 259