portaudio 0.7.1

PortAudio bindings for Rust.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// 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.

//! Types used in the PortAudio API

#![allow(dead_code)]

use ffi;
use num::FromPrimitive;
use std::os::raw;

pub use self::sample_format_flags::SampleFormatFlags;

/// The type used to refer to audio devices.
///
/// Values of this type usually range from 0 to (PortAudio::device_count-1).
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct DeviceIndex(pub u32);

/// The device to be used by some stream.
///
/// This is used as a field within the Settings for a **Stream**.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DeviceKind {
    /// An index to some Device.
    Index(DeviceIndex),
    /// Indicates that the device(s) to be used are specified in the host api specific stream info
    /// structure.
    UseHostApiSpecificDeviceSpecification,
}

impl From<DeviceIndex> for ffi::PaDeviceIndex {
    fn from(idx: DeviceIndex) -> ffi::PaDeviceIndex {
        let DeviceIndex(idx) = idx;
        idx as ffi::PaDeviceIndex
    }
}

impl From<DeviceIndex> for DeviceKind {
    fn from(idx: DeviceIndex) -> DeviceKind {
        DeviceKind::Index(idx)
    }
}

impl From<DeviceKind> for ffi::PaDeviceIndex {
    fn from(kind: DeviceKind) -> ffi::PaDeviceIndex {
        match kind {
            DeviceKind::Index(idx) => idx.into(),
            DeviceKind::UseHostApiSpecificDeviceSpecification => -2,
        }
    }
}

/// The special value may be used to request that the stream callback will receive an optimal (and
/// possibly varying) number of frames based on host requirements and the requested latency
/// settings.
pub const FRAMES_PER_BUFFER_UNSPECIFIED: u32 = 0;

/// The type used to enumerate to host APIs at runtime.
/// Values of this type range from 0 to (pa::get_host_api_count()-1).
pub type HostApiIndex = ffi::PaHostApiIndex;

/// The type used to represent monotonic time in seconds.
pub type Time = ffi::PaTime;

/// An type alias used to represent a given number of frames.
pub type Frames = i64;

/// A type used to dynamically represent the various standard sample formats (usually) supported by
/// all PortAudio implementations.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SampleFormat {
    /// Uses -1.0 and +1.0 as the minimum and maximum respectively.
    F32,
    /// 32-bit signed integer sample representation.
    I32,
    /// 24-bit signed integer sample representation.
    ///
    /// TODO: Should work out how to support this properly.
    I24,
    /// 16-bit signed integer sample representation.
    I16,
    /// 8-bit signed integer sample representation.
    I8,
    /// An unsigned 8 bit format where 128 is considered "ground"
    U8,
    /// Some custom sample format.
    ///
    /// TODO: Need to work out how to support this properly. I was unable to find any official
    /// info.
    ///
    /// The following e-mail by Bencina (2004) touches on the topic of custom formats:
    ///
    /// > "It is theoretically possible to pass "custom" data formats to PortAudio using the
    /// paCustomFormat SampleFormat flag. I think the general idea is that when this bit is set,
    /// the low word of the sample format byte is device specific. I know of no implementation that
    /// has ever used this feature so it has not been extensively developed. That said, much of
    /// PortAudio (V19 at least) assumes a frame based sample format, accomodating a block based
    /// format such as mpeg would probably require bypassing some of the internal infrastructure
    /// (such as the block adapter in pa_process). PortAudio has been designed for linear, frame
    /// based i/o, so it would be up to you to propose/suggest ways in which to accomodate your
    /// requirements." - http://music.columbia.edu/pipermail/portaudio/2004-February/003237.html
    Custom,
    /// This variant is used when none of the above variants can be inferred from a given
    /// set of **SampleFormatFlags** via the `SampleFormat::from_flags` function.
    Unknown,
}

impl SampleFormat {
    /// Inspects the given **SampleFormatFlags** for the format.
    ///
    /// Returns `Some(SampleFormat)` if a matching format is found.
    ///
    /// Returns `None` if no matching format is found.
    pub fn from_flags(flags: SampleFormatFlags) -> Self {
        if flags.contains(sample_format_flags::FLOAT_32) {
            SampleFormat::F32
        } else if flags.contains(sample_format_flags::INT_32) {
            SampleFormat::I32
        } else if flags.contains(sample_format_flags::INT_24) {
            SampleFormat::I24
        } else if flags.contains(sample_format_flags::INT_16) {
            SampleFormat::I16
        } else if flags.contains(sample_format_flags::INT_8) {
            SampleFormat::I8
        } else if flags.contains(sample_format_flags::UINT_8) {
            SampleFormat::U8
        } else if flags.contains(sample_format_flags::CUSTOM_FORMAT) {
            SampleFormat::Custom
        } else {
            SampleFormat::Unknown
        }
    }

    /// Converts `self` into the respective **SampleFormatFlags**.
    pub fn flags(self) -> SampleFormatFlags {
        match self {
            SampleFormat::F32 => sample_format_flags::FLOAT_32,
            SampleFormat::I32 => sample_format_flags::INT_32,
            SampleFormat::I24 => sample_format_flags::INT_24,
            SampleFormat::I16 => sample_format_flags::INT_16,
            SampleFormat::I8 => sample_format_flags::INT_8,
            SampleFormat::U8 => sample_format_flags::UINT_8,
            SampleFormat::Custom => sample_format_flags::CUSTOM_FORMAT,
            SampleFormat::Unknown => SampleFormatFlags::empty(),
        }
    }

    /// Returns the size of the **SampleFormat** in bytes.
    ///
    /// Returns `0` if the **SampleFormat** is **Custom** or **Unknown**.
    pub fn size_in_bytes(&self) -> u8 {
        match *self {
            SampleFormat::F32 | SampleFormat::I32 => 4,
            SampleFormat::I24 => 3,
            SampleFormat::I16 => 2,
            SampleFormat::I8 | SampleFormat::U8 => 1,
            SampleFormat::Custom | SampleFormat::Unknown => 0,
        }
    }
}

pub mod sample_format_flags {
    //! A type safe wrapper around PortAudio's `PaSampleFormat` flags.
    use ffi;
    bitflags! {
        /// A type used to specify one or more sample formats. Each value indicates a possible
        /// format for sound data passed to and from the stream callback, Pa_ReadStream and
        /// Pa_WriteStream.
        ///
        /// The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8 and aUInt8 are
        /// usually implemented by all implementations.
        ///
        /// The floating point representation (FLOAT_32) uses +1.0 and -1.0 as the maximum and
        /// minimum respectively.
        ///
        /// UINT_8 is an unsigned 8 bit format where 128 is considered "ground"
        ///
        /// The paNonInterleaved flag indicates that audio data is passed as an array of pointers
        /// to separate buffers, one buffer for each channel. Usually, when this flag is not used,
        /// audio data is passed as a single buffer with all channels interleaved.
        pub flags SampleFormatFlags: ::std::os::raw::c_ulong {
            /// 32 bits float sample format
            const FLOAT_32 = ffi::PA_FLOAT_32,
            /// 32 bits int sample format
            const INT_32 = ffi::PA_INT_32,
            /// Packed 24 bits int sample format
            const INT_24 = ffi::PA_INT_24,
            /// 16 bits int sample format
            const INT_16 = ffi::PA_INT_16,
            /// 8 bits int sample format
            const INT_8 = ffi::PA_INT_8,
            /// 8 bits unsigned int sample format
            const UINT_8 = ffi::PA_UINT_8,
            /// Custom sample format
            const CUSTOM_FORMAT = ffi::PA_CUSTOM_FORMAT,
            /// Non interleaved sample format
            const NON_INTERLEAVED = ffi::PA_NON_INTERLEAVED,
        }
    }

    impl From<ffi::SampleFormat> for SampleFormatFlags {
        fn from(format: ffi::SampleFormat) -> Self {
            SampleFormatFlags::from_bits(format).unwrap_or_else(|| SampleFormatFlags::empty())
        }
    }

    impl ::std::fmt::Display for SampleFormatFlags {
        fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
            write!(
                f,
                "{:?}",
                match self.bits() {
                    ffi::PA_FLOAT_32 => "FLOAT_32",
                    ffi::PA_INT_32 => "INT_32",
                    //ffi::PA_INT_24 => "INT_24",
                    ffi::PA_INT_16 => "INT_16",
                    ffi::PA_INT_8 => "INT_8",
                    ffi::PA_UINT_8 => "UINT_8",
                    ffi::PA_CUSTOM_FORMAT => "CUSTOM_FORMAT",
                    ffi::PA_NON_INTERLEAVED => "NON_INTERLEAVED",
                    _ => "<Unknown SampleFormatFlags>",
                }
            )
        }
    }
}

enum_from_primitive! {
/// Unchanging unique identifiers for each supported host API
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub enum HostApiTypeId {
    /// In development host
    InDevelopment = ffi::PaHostApiTypeId_paInDevelopment,
    /// Direct sound
    DirectSound = ffi::PaHostApiTypeId_paDirectSound,
    /// MMe API
    MME = ffi::PaHostApiTypeId_paMME,
    /// ASIO API
    ASIO = ffi::PaHostApiTypeId_paASIO,
    /// Sound manager API
    SoundManager = ffi::PaHostApiTypeId_paSoundManager,
    /// Core Audio API
    CoreAudio = ffi::PaHostApiTypeId_paCoreAudio,
    /// OSS API
    OSS = ffi::PaHostApiTypeId_paOSS,
    /// Alsa API
    ALSA = ffi::PaHostApiTypeId_paALSA,
    /// AL API
    AL = ffi::PaHostApiTypeId_paAL,
    /// BeOS API
    BeOS = ffi::PaHostApiTypeId_paBeOS,
    /// WDMKS
    WDMKS = ffi::PaHostApiTypeId_paWDMKS,
    /// Jack API
    JACK = ffi::PaHostApiTypeId_paJACK,
    /// WASAPI
    WASAPI = ffi::PaHostApiTypeId_paWASAPI,
    /// Audio Science HPI
    AudioScienceHPI = ffi::PaHostApiTypeId_paAudioScienceHPI,
}
}

/// A structure containing information about a particular host API.
#[derive(Clone, Debug, PartialEq)]
pub struct HostApiInfo<'a> {
    /// The version of the struct
    pub struct_version: i32,
    /// The type of the current host
    pub host_type: HostApiTypeId,
    /// The name of the host
    pub name: &'a str,
    /// The total count of device in the host
    pub device_count: u32,
    /// The index to the default input device or None if no input device is available
    pub default_input_device: Option<DeviceIndex>,
    /// The index to the default output device or None if no output device is available
    pub default_output_device: Option<DeviceIndex>,
}

impl<'a> HostApiInfo<'a> {
    /// Construct the HostApiInfo from the equivalent C struct.
    ///
    /// Returns `None` if:
    /// - either of the given device indices are invalid.
    /// - the device_count is less than `0`.
    /// - a valid `HostApiTypeId` can't be constructed from the given `host_type`.
    pub fn from_c_info(c_info: ffi::PaHostApiInfo) -> Option<HostApiInfo<'a>> {
        let default_input_device = match c_info.defaultInputDevice {
            idx if idx >= 0 => Some(DeviceIndex(idx as u32)),
            ffi::PA_NO_DEVICE => None,
            _ => return None,
        };
        let default_output_device = match c_info.defaultOutputDevice {
            idx if idx >= 0 => Some(DeviceIndex(idx as u32)),
            ffi::PA_NO_DEVICE => None,
            _ => return None,
        };
        let device_count = match c_info.deviceCount {
            n if n >= 0 => n as u32,
            _ => return None,
        };
        let host_type = match FromPrimitive::from_u32(c_info.type_) {
            Some(ty) => ty,
            None => return None,
        };
        Some(HostApiInfo {
            struct_version: c_info.structVersion,
            host_type: host_type,
            name: ffi::c_str_to_str(c_info.name).unwrap_or("<Failed to convert str from CStr>"),
            device_count: device_count,
            default_input_device: default_input_device,
            default_output_device: default_output_device,
        })
    }
}

impl<'a> From<HostApiInfo<'a>> for ffi::PaHostApiInfo {
    fn from(info: HostApiInfo<'a>) -> Self {
        let default_input_device = match info.default_input_device {
            Some(i) => i.into(),
            None => ffi::PA_NO_DEVICE,
        };
        let default_output_device = match info.default_output_device {
            Some(i) => i.into(),
            None => ffi::PA_NO_DEVICE,
        };
        ffi::PaHostApiInfo {
            structVersion: info.struct_version as raw::c_int,
            type_: info.host_type as ffi::PaHostApiTypeId,
            name: ffi::str_to_c_str(info.name),
            deviceCount: info.device_count as raw::c_int,
            defaultInputDevice: default_input_device,
            defaultOutputDevice: default_output_device,
        }
    }
}

/// Structure used to return information about a host error condition.
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub struct HostErrorInfo<'a> {
    /// The host API which returned the error code
    pub host_api_type: HostApiTypeId,
    /// The code of the error
    pub code: u32,
    /// The string which explain the error
    pub text: &'a str,
}

impl<'a> HostErrorInfo<'a> {
    /// Construct a HostErrorInfo from the equivalent C struct.
    pub fn from_c_error_info(c_error: ffi::PaHostErrorInfo) -> HostErrorInfo<'a> {
        HostErrorInfo {
            host_api_type: FromPrimitive::from_u32(c_error.hostApiType).unwrap(),
            code: c_error.errorCode as u32,
            text: ffi::c_str_to_str(c_error.errorText)
                .unwrap_or("<Failed to convert str from CStr>"),
        }
    }
}

impl<'a> From<HostErrorInfo<'a>> for ffi::PaHostErrorInfo {
    fn from(error: HostErrorInfo<'a>) -> Self {
        ffi::PaHostErrorInfo {
            hostApiType: FromPrimitive::from_i32(error.host_api_type as i32).unwrap(),
            errorCode: error.code as raw::c_long,
            errorText: ffi::str_to_c_str(error.text),
        }
    }
}

/// A structure providing information and capabilities of PortAudio devices.
///
/// Devices may support input, output or both input and output.
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub struct DeviceInfo<'a> {
    /// The version of the struct
    pub struct_version: i32,
    /// The name of the device
    pub name: &'a str,
    /// Host API identifier
    pub host_api: HostApiIndex,
    /// Maximal number of input channels for this device
    pub max_input_channels: i32,
    /// maximal number of output channel for this device
    pub max_output_channels: i32,
    /// The default low latency for input with this device
    pub default_low_input_latency: Time,
    /// The default low latency for output with this device
    pub default_low_output_latency: Time,
    /// The default high latency for input with this device
    pub default_high_input_latency: Time,
    /// The default high latency for output with this device
    pub default_high_output_latency: Time,
    /// The default sample rate for this device
    pub default_sample_rate: f64,
}

impl<'a> DeviceInfo<'a> {
    /// Construct a **DeviceInfo** from the equivalent C struct.
    pub fn from_c_info(c_info: ffi::PaDeviceInfo) -> DeviceInfo<'a> {
        DeviceInfo {
            struct_version: c_info.structVersion,
            name: ffi::c_str_to_str(c_info.name).unwrap_or("<Failed to convert str from CStr>"),
            host_api: c_info.hostApi,
            max_input_channels: c_info.maxInputChannels,
            max_output_channels: c_info.maxOutputChannels,
            default_low_input_latency: c_info.defaultLowInputLatency,
            default_low_output_latency: c_info.defaultLowOutputLatency,
            default_high_input_latency: c_info.defaultHighInputLatency,
            default_high_output_latency: c_info.defaultHighOutputLatency,
            default_sample_rate: c_info.defaultSampleRate,
        }
    }
}

impl<'a> From<DeviceInfo<'a>> for ffi::PaDeviceInfo {
    fn from(info: DeviceInfo<'a>) -> Self {
        ffi::PaDeviceInfo {
            structVersion: info.struct_version as raw::c_int,
            name: ffi::str_to_c_str(info.name),
            hostApi: info.host_api as ffi::PaHostApiIndex,
            maxInputChannels: info.max_input_channels as raw::c_int,
            maxOutputChannels: info.max_output_channels as raw::c_int,
            defaultLowInputLatency: info.default_low_input_latency,
            defaultLowOutputLatency: info.default_low_output_latency,
            defaultHighInputLatency: info.default_high_input_latency,
            defaultHighOutputLatency: info.default_high_output_latency,
            defaultSampleRate: info.default_sample_rate,
        }
    }
}