Bug 1440538 - P4: Update cubeb-pulse-rs to commit f58dc34. r=kinetik

MozReview-Commit-ID: 5eQV3nUceQe

--HG--
extra : rebase_source : c182a15ceda6ef3bb9e25ca3a4465aba325f32bf
This commit is contained in:
Dan Glastonbury 2018-02-23 12:54:14 +10:00
Родитель 16617a8283
Коммит b486a46d6a
15 изменённых файлов: 784 добавлений и 1452 удалений

Просмотреть файл

@ -1,6 +1,6 @@
[package]
name = "cubeb-pulse"
version = "0.0.2"
version = "0.1.1"
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
description = "Cubeb backed for PulseAudio written in Rust"
@ -11,7 +11,7 @@ pulse-dlopen = ["pulse-ffi/dlopen"]
crate-type = ["staticlib", "rlib"]
[dependencies]
cubeb-ffi = { path = "cubeb-ffi" }
cubeb-backend = "0.4"
pulse-ffi = { path = "pulse-ffi" }
pulse = { path = "pulse-rs" }
semver = "^0.6"

Просмотреть файл

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git
The git commit ID used was deadde7d14e0010628471e33a538a0a1e59765ff (2018-01-25 10:00:16 +1000)
The git commit ID used was f58dc34c5af519352aed4b4cd79bb34060e59c65 (2018-02-23 11:16:40 +1000)

Просмотреть файл

@ -1,8 +0,0 @@
[package]
name = "cubeb-ffi"
version = "0.0.2"
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
description = "FFI bindings for implementing cubeb backends"
[dependencies]
bitflags = "1.0"

Просмотреть файл

@ -1,508 +0,0 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use std::default::Default;
use std::os::raw::{c_char, c_int, c_long, c_uint, c_void};
use std::ptr;
pub enum Context {}
pub enum Stream {}
// These need to match cubeb_sample_format
pub type SampleFormat = c_int;
pub const SAMPLE_S16LE: SampleFormat = 0;
pub const SAMPLE_S16BE: SampleFormat = 1;
pub const SAMPLE_FLOAT32LE: SampleFormat = 2;
pub const SAMPLE_FLOAT32BE: SampleFormat = 3;
#[cfg(target_endian = "little")]
pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16LE;
#[cfg(target_endian = "little")]
pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32LE;
#[cfg(target_endian = "big")]
pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16BE;
#[cfg(target_endian = "big")]
pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32BE;
pub type DeviceId = *const c_void;
// These need to match cubeb_channel_layout
pub type ChannelLayout = c_int;
pub const LAYOUT_UNDEFINED: ChannelLayout = 0;
pub const LAYOUT_DUAL_MONO: ChannelLayout = 1;
pub const LAYOUT_DUAL_MONO_LFE: ChannelLayout = 2;
pub const LAYOUT_MONO: ChannelLayout = 3;
pub const LAYOUT_MONO_LFE: ChannelLayout = 4;
pub const LAYOUT_STEREO: ChannelLayout = 5;
pub const LAYOUT_STEREO_LFE: ChannelLayout = 6;
pub const LAYOUT_3F: ChannelLayout = 7;
pub const LAYOUT_3F_LFE: ChannelLayout = 8;
pub const LAYOUT_2F1: ChannelLayout = 9;
pub const LAYOUT_2F1_LFE: ChannelLayout = 10;
pub const LAYOUT_3F1: ChannelLayout = 11;
pub const LAYOUT_3F1_LFE: ChannelLayout = 12;
pub const LAYOUT_2F2: ChannelLayout = 13;
pub const LAYOUT_2F2_LFE: ChannelLayout = 14;
pub const LAYOUT_3F2: ChannelLayout = 15;
pub const LAYOUT_3F2_LFE: ChannelLayout = 16;
pub const LAYOUT_3F3R_LFE: ChannelLayout = 17;
pub const LAYOUT_3F4_LFE: ChannelLayout = 18;
pub const LAYOUT_MAX: ChannelLayout = 256;
// These need to match cubeb_device_type
bitflags! {
#[repr(C)]
pub struct StreamPrefs : u32 {
const STREAM_PREF_NONE = 0x00;
const STREAM_PREF_LOOPBACK = 0x01;
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct StreamParams {
pub format: SampleFormat,
pub rate: u32,
pub channels: u32,
pub layout: ChannelLayout,
pub prefs: StreamPrefs,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Device {
pub output_name: *mut c_char,
pub input_name: *mut c_char,
}
impl Default for Device {
fn default() -> Self {
Device {
output_name: ptr::null_mut(),
input_name: ptr::null_mut(),
}
}
}
// These need to match cubeb_state
pub type State = c_int;
pub const STATE_STARTED: State = 0;
pub const STATE_STOPPED: State = 1;
pub const STATE_DRAINED: State = 2;
pub const STATE_ERROR: State = 3;
pub const OK: i32 = 0;
pub const ERROR: i32 = -1;
pub const ERROR_INVALID_FORMAT: i32 = -2;
pub const ERROR_INVALID_PARAMETER: i32 = -3;
pub const ERROR_NOT_SUPPORTED: i32 = -4;
pub const ERROR_DEVICE_UNAVAILABLE: i32 = -5;
// These need to match cubeb_device_type
bitflags! {
#[repr(C)]
pub struct DeviceType : u32 {
const UNKNOWN = 0b00;
const INPUT = 0b01;
const OUTPUT = 0b10;
const ALL = 0b11;
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DeviceState {
Disabled = 0,
Unplugged = 1,
Enabled = 2,
}
// These need to match cubeb_device_fmt
bitflags! {
#[repr(C)]
pub struct DeviceFmt: u32 {
const S16LE = 0x0010;
const S16BE = 0x0020;
const S16NE = {
#[cfg(target_endian = "little")] { DeviceFmt::S16LE }
#[cfg(target_endian = "big")] { DeviceFmt::S16BE }
}.bits;
const F32LE = 0x1000;
const F32BE = 0x2000;
const F32NE = {
#[cfg(target_endian = "little")] { DeviceFmt::F32LE }
#[cfg(target_endian = "big")] { DeviceFmt::F32BE }
}.bits;
const S16_MASK = DeviceFmt::S16LE.bits | DeviceFmt::S16BE.bits;
const F32_MASK = DeviceFmt::F32LE.bits | DeviceFmt::F32BE.bits;
const ALL = DeviceFmt::S16_MASK.bits | DeviceFmt::F32_MASK.bits;
}
}
// These need to match cubeb_device_pref
bitflags! {
#[repr(C)]
pub struct DevicePref : u32 {
const MULTIMEDIA = 0x1;
const VOICE = 0x2;
const NOTIFICATION = 0x4;
const ALL = 0xF;
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DeviceInfo {
pub devid: DeviceId,
pub device_id: *const c_char,
pub friendly_name: *const c_char,
pub group_id: *const c_char,
pub vendor_name: *const c_char,
pub devtype: DeviceType,
pub state: DeviceState,
pub preferred: DevicePref,
pub format: DeviceFmt,
pub default_format: DeviceFmt,
pub max_channels: u32,
pub default_rate: u32,
pub max_rate: u32,
pub min_rate: u32,
pub latency_lo: u32,
pub latency_hi: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DeviceCollection {
/// Array of device info.
pub device: *const DeviceInfo,
/// Device count in collection.
pub count: usize,
}
pub type DataCallback = Option<unsafe extern "C" fn(stream: *mut Stream,
user_ptr: *mut c_void,
input_buffer: *const c_void,
output_buffer: *mut c_void,
nframes: c_long)
-> c_long>;
pub type StateCallback = Option<unsafe extern "C" fn(stream: *mut Stream, user_ptr: *mut c_void, state: State)>;
pub type DeviceChangedCallback = Option<unsafe extern "C" fn(user_ptr: *mut c_void)>;
pub type DeviceCollectionChangedCallback = Option<unsafe extern "C" fn(context: *mut Context, user_ptr: *mut c_void)>;
pub type StreamInitFn = Option<unsafe extern "C" fn(context: *mut Context,
stream: *mut *mut Stream,
stream_name: *const c_char,
input_device: DeviceId,
input_stream_params: *mut StreamParams,
output_device: DeviceId,
output_stream_params: *mut StreamParams,
latency: u32,
data_callback: DataCallback,
state_callback: StateCallback,
user_ptr: *mut c_void)
-> i32>;
pub type RegisterDeviceCollectionChangedFn = Option<unsafe extern "C" fn(context: *mut Context,
devtype: DeviceType,
callback: DeviceCollectionChangedCallback,
user_ptr: *mut c_void)
-> i32>;
#[repr(C)]
pub struct Ops {
pub init: Option<unsafe extern "C" fn(context: *mut *mut Context, context_name: *const c_char) -> i32>,
pub get_backend_id: Option<unsafe extern "C" fn(context: *mut Context) -> *const c_char>,
pub get_max_channel_count: Option<unsafe extern "C" fn(context: *mut Context, max_channels: *mut u32) -> i32>,
pub get_min_latency: Option<unsafe extern "C" fn(context: *mut Context,
params: StreamParams,
latency_ms: *mut u32)
-> i32>,
pub get_preferred_sample_rate: Option<unsafe extern "C" fn(context: *mut Context, rate: *mut u32) -> i32>,
pub get_preferred_channel_layout:
Option<unsafe extern "C" fn(context: *mut Context, layout: *mut ChannelLayout) -> i32>,
pub enumerate_devices: Option<unsafe extern "C" fn(context: *mut Context,
devtype: DeviceType,
collection: *mut DeviceCollection)
-> i32>,
pub device_collection_destroy:
Option<unsafe extern "C" fn(context: *mut Context, collection: *mut DeviceCollection) -> i32>,
pub destroy: Option<unsafe extern "C" fn(context: *mut Context)>,
pub stream_init: StreamInitFn,
pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut Stream)>,
pub stream_start: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
pub stream_stop: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
pub stream_reset_default_device: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
pub stream_get_position: Option<unsafe extern "C" fn(stream: *mut Stream, position: *mut u64) -> i32>,
pub stream_get_latency: Option<unsafe extern "C" fn(stream: *mut Stream, latency: *mut u32) -> i32>,
pub stream_set_volume: Option<unsafe extern "C" fn(stream: *mut Stream, volumes: f32) -> i32>,
pub stream_set_panning: Option<unsafe extern "C" fn(stream: *mut Stream, panning: f32) -> i32>,
pub stream_get_current_device: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut *const Device) -> i32>,
pub stream_device_destroy: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut Device) -> i32>,
pub stream_register_device_changed_callback:
Option<unsafe extern "C" fn(stream: *mut Stream,
device_changed_callback: DeviceChangedCallback)
-> i32>,
pub register_device_collection_changed: RegisterDeviceCollectionChangedFn,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct LayoutMap {
pub name: *const c_char,
pub channels: u32,
pub layout: ChannelLayout,
}
// cubeb_mixer.h
pub type Channel = c_int;
// These need to match cubeb_channel
pub const CHANNEL_INVALID: Channel = -1;
pub const CHANNEL_MONO: Channel = 0;
pub const CHANNEL_LEFT: Channel = 1;
pub const CHANNEL_RIGHT: Channel = 2;
pub const CHANNEL_CENTER: Channel = 3;
pub const CHANNEL_LS: Channel = 4;
pub const CHANNEL_RS: Channel = 5;
pub const CHANNEL_RLS: Channel = 6;
pub const CHANNEL_RCENTER: Channel = 7;
pub const CHANNEL_RRS: Channel = 8;
pub const CHANNEL_LFE: Channel = 9;
pub const CHANNEL_MAX: Channel = 10;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ChannelMap {
pub channels: c_uint,
pub map: [Channel; CHANNEL_MAX as usize],
}
impl ::std::default::Default for ChannelMap {
fn default() -> Self {
ChannelMap {
channels: 0,
map: [CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID],
}
}
}
extern "C" {
pub fn cubeb_channel_map_to_layout(channel_map: *const ChannelMap) -> ChannelLayout;
pub fn cubeb_should_upmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
pub fn cubeb_should_downmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
pub fn cubeb_downmix_float(input: *const f32,
inframes: c_long,
output: *mut f32,
in_channels: u32,
out_channels: u32,
in_layout: ChannelLayout,
out_layout: ChannelLayout);
pub fn cubeb_upmix_float(input: *const f32,
inframes: c_long,
output: *mut f32,
in_channels: u32,
out_channels: u32);
}
#[test]
fn bindgen_test_layout_stream_params() {
assert_eq!(::std::mem::size_of::<StreamParams>(),
20usize,
concat!("Size of: ", stringify!(StreamParams)));
assert_eq!(::std::mem::align_of::<StreamParams>(),
4usize,
concat!("Alignment of ", stringify!(StreamParams)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).format as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(format)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).rate as *const _ as usize },
4usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(rate)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).channels as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(channels)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).layout as *const _ as usize },
12usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(layout)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).prefs as *const _ as usize },
16usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(layout)));
}
#[test]
fn bindgen_test_layout_cubeb_device() {
assert_eq!(::std::mem::size_of::<Device>(),
16usize,
concat!("Size of: ", stringify!(Device)));
assert_eq!(::std::mem::align_of::<Device>(),
8usize,
concat!("Alignment of ", stringify!(Device)));
assert_eq!(unsafe { &(*(0 as *const Device)).output_name as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(Device),
"::",
stringify!(output_name)));
assert_eq!(unsafe { &(*(0 as *const Device)).input_name as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(Device),
"::",
stringify!(input_name)));
}
#[test]
fn bindgen_test_layout_cubeb_device_info() {
assert_eq!(::std::mem::size_of::<DeviceInfo>(),
88usize,
concat!("Size of: ", stringify!(DeviceInfo)));
assert_eq!(::std::mem::align_of::<DeviceInfo>(),
8usize,
concat!("Alignment of ", stringify!(DeviceInfo)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devid as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(devid)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).device_id as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(device_id)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).friendly_name as *const _ as usize },
16usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(friendly_name)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).group_id as *const _ as usize },
24usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(group_id)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).vendor_name as *const _ as usize },
32usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(vendor_name)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devtype as *const _ as usize },
40usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(type_)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).state as *const _ as usize },
44usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(state)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).preferred as *const _ as usize },
48usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(preferred)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).format as *const _ as usize },
52usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(format)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_format as *const _ as usize },
56usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(default_format)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_channels as *const _ as usize },
60usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(max_channels)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_rate as *const _ as usize },
64usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(default_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_rate as *const _ as usize },
68usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(max_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).min_rate as *const _ as usize },
72usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(min_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_lo as *const _ as usize },
76usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(latency_lo)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_hi as *const _ as usize },
80usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(latency_hi)));
}
#[test]
fn bindgen_test_layout_cubeb_device_collection() {
assert_eq!(::std::mem::size_of::<DeviceCollection>(),
8usize,
concat!("Size of: ", stringify!(DeviceCollection)));
assert_eq!(::std::mem::align_of::<DeviceCollection>(),
8usize,
concat!("Alignment of ", stringify!(DeviceCollection)));
assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).count as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(DeviceCollection),
"::",
stringify!(count)));
assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).device as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(DeviceCollection),
"::",
stringify!(device)));
}

Просмотреть файл

@ -1,14 +0,0 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
#[macro_use]
extern crate bitflags;
mod ffi;
mod log;
pub mod mixer;
pub use ffi::*;
pub use log::*;

Просмотреть файл

@ -1,75 +0,0 @@
use std::os::raw::c_char;
#[macro_export]
macro_rules! log_internal {
($level: expr, $msg: expr) => {
#[allow(unused_unsafe)]
unsafe {
if $level <= $crate::g_cubeb_log_level {
if let Some(log_callback) = $crate::g_cubeb_log_callback {
let cstr = ::std::ffi::CString::new(concat!("%s:%d: ", $msg, "\n")).unwrap();
log_callback(cstr.as_ptr(), file!(), line!());
}
}
}
};
($level: expr, $fmt: expr, $($arg:tt)+) => {
#[allow(unused_unsafe)]
unsafe {
if $level <= $crate::g_cubeb_log_level {
if let Some(log_callback) = $crate::g_cubeb_log_callback {
let cstr = ::std::ffi::CString::new(format!(concat!("%s:%d: ", $fmt, "\n"), $($arg)+)).unwrap();
log_callback(cstr.as_ptr(), file!(), line!());
}
}
}
}
}
#[macro_export]
macro_rules! logv {
($msg: expr) => (log_internal!($crate::LogLevel::Verbose, $msg));
($fmt: expr, $($arg: tt)+) => (log_internal!($crate::LogLevel::Verbose, $fmt, $($arg)*));
}
#[macro_export]
macro_rules! log {
($msg: expr) => (log_internal!($crate::LogLevel::Normal, $msg));
($fmt: expr, $($arg: tt)+) => (log_internal!($crate::LogLevel::Normal, $fmt, $($arg)*));
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LogLevel {
Disabled = 0,
Normal = 1,
Verbose = 2,
}
pub type LogCallback = Option<unsafe extern "C" fn(fmt: *const c_char, ...)>;
extern "C" {
pub static g_cubeb_log_level: LogLevel;
pub static g_cubeb_log_callback: LogCallback;
}
pub fn log_enabled() -> bool {
unsafe { g_cubeb_log_level != LogLevel::Disabled }
}
#[test]
fn test_normal_logging() {
log!("This is log at normal level");
log!("Formatted log %d", 1);
}
#[test]
fn test_verbose_logging() {
logv!("This is a log at verbose level");
logv!("Formatted log %d", 1);
}
#[test]
fn test_logging_disabled_by_default() {
assert!(!log_enabled());
}

Просмотреть файл

@ -1,80 +0,0 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use ::*;
static CHANNEL_LAYOUT_UNDEFINED: &'static [Channel] = &[CHANNEL_INVALID];
static CHANNEL_LAYOUT_DUAL_MONO: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
static CHANNEL_LAYOUT_DUAL_MONO_LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
static CHANNEL_LAYOUT_MONO: &'static [Channel] = &[CHANNEL_MONO];
static CHANNEL_LAYOUT_MONO_LFE: &'static [Channel] = &[CHANNEL_MONO, CHANNEL_LFE];
static CHANNEL_LAYOUT_STEREO: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
static CHANNEL_LAYOUT_STEREO_LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
static CHANNEL_LAYOUT_3F: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER];
static CHANNEL_LAYOUT_3FLFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE];
static CHANNEL_LAYOUT_2F1: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_2F1LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_3F1: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_3F1LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RCENTER];
static CHANNEL_LAYOUT_2F2: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS];
static CHANNEL_LAYOUT_2F2LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_LFE,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F2: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F2LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F3RLFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RCENTER,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F4LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RLS,
CHANNEL_RRS,
CHANNEL_LS,
CHANNEL_RS];
pub fn channel_index_to_order(layout: ChannelLayout) -> &'static [Channel] {
match layout {
LAYOUT_DUAL_MONO => CHANNEL_LAYOUT_DUAL_MONO,
LAYOUT_DUAL_MONO_LFE => CHANNEL_LAYOUT_DUAL_MONO_LFE,
LAYOUT_MONO => CHANNEL_LAYOUT_MONO,
LAYOUT_MONO_LFE => CHANNEL_LAYOUT_MONO_LFE,
LAYOUT_STEREO => CHANNEL_LAYOUT_STEREO,
LAYOUT_STEREO_LFE => CHANNEL_LAYOUT_STEREO_LFE,
LAYOUT_3F => CHANNEL_LAYOUT_3F,
LAYOUT_3F_LFE => CHANNEL_LAYOUT_3FLFE,
LAYOUT_2F1 => CHANNEL_LAYOUT_2F1,
LAYOUT_2F1_LFE => CHANNEL_LAYOUT_2F1LFE,
LAYOUT_3F1 => CHANNEL_LAYOUT_3F1,
LAYOUT_3F1_LFE => CHANNEL_LAYOUT_3F1LFE,
LAYOUT_2F2 => CHANNEL_LAYOUT_2F2,
LAYOUT_2F2_LFE => CHANNEL_LAYOUT_2F2LFE,
LAYOUT_3F2 => CHANNEL_LAYOUT_3F2,
LAYOUT_3F2_LFE => CHANNEL_LAYOUT_3F2LFE,
LAYOUT_3F3R_LFE => CHANNEL_LAYOUT_3F3RLFE,
LAYOUT_3F4_LFE => CHANNEL_LAYOUT_3F4LFE,
_ => CHANNEL_LAYOUT_UNDEFINED,
}
}

Просмотреть файл

@ -1,48 +1,52 @@
// Copyright © 2017 Mozilla Foundation
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use backend::*;
use capi::PULSE_OPS;
use cubeb;
use cubeb_backend::{ffi, log_enabled, ChannelLayout, Context, ContextOps, DeviceCollectionRef,
DeviceId, DeviceType, Error, Ops, Result, Stream, StreamParams,
StreamParamsRef};
use pulse::{self, ProplistExt};
use pulse_ffi::*;
use semver;
use std::cell::RefCell;
use std::default::Default;
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_void};
use std::os::raw::c_void;
use std::ptr;
use std::cell::RefCell;
fn pa_channel_to_cubeb_channel(channel: pulse::ChannelPosition) -> cubeb::Channel {
fn pa_channel_to_cubeb_channel(channel: pulse::ChannelPosition) -> ffi::cubeb_channel {
use cubeb_backend::ffi::*;
use pulse::ChannelPosition;
assert_ne!(channel, ChannelPosition::Invalid);
match channel {
ChannelPosition::Mono => cubeb::CHANNEL_MONO,
ChannelPosition::FrontLeft => cubeb::CHANNEL_LEFT,
ChannelPosition::FrontRight => cubeb::CHANNEL_RIGHT,
ChannelPosition::FrontCenter => cubeb::CHANNEL_CENTER,
ChannelPosition::SideLeft => cubeb::CHANNEL_LS,
ChannelPosition::SideRight => cubeb::CHANNEL_RS,
ChannelPosition::RearLeft => cubeb::CHANNEL_RLS,
ChannelPosition::RearCenter => cubeb::CHANNEL_RCENTER,
ChannelPosition::RearRight => cubeb::CHANNEL_RRS,
ChannelPosition::LowFreqEffects => cubeb::CHANNEL_LFE,
_ => cubeb::CHANNEL_INVALID,
ChannelPosition::Mono => CHANNEL_MONO,
ChannelPosition::FrontLeft => CHANNEL_LEFT,
ChannelPosition::FrontRight => CHANNEL_RIGHT,
ChannelPosition::FrontCenter => CHANNEL_CENTER,
ChannelPosition::SideLeft => CHANNEL_LS,
ChannelPosition::SideRight => CHANNEL_RS,
ChannelPosition::RearLeft => CHANNEL_RLS,
ChannelPosition::RearCenter => CHANNEL_RCENTER,
ChannelPosition::RearRight => CHANNEL_RRS,
ChannelPosition::LowFreqEffects => CHANNEL_LFE,
_ => CHANNEL_INVALID,
}
}
fn channel_map_to_layout(cm: &pulse::ChannelMap) -> cubeb::ChannelLayout {
fn channel_map_to_layout(cm: &pulse::ChannelMap) -> ChannelLayout {
use cubeb_backend::ffi::{cubeb_channel_map, cubeb_channel_map_to_layout};
use pulse::ChannelPosition;
let mut cubeb_map: cubeb::ChannelMap = Default::default();
let mut cubeb_map: cubeb_channel_map = unsafe { mem::zeroed() };
cubeb_map.channels = u32::from(cm.channels);
for i in 0usize..cm.channels as usize {
cubeb_map.map[i] = pa_channel_to_cubeb_channel(ChannelPosition::try_from(cm.map[i])
.unwrap_or(ChannelPosition::Invalid));
cubeb_map.map[i] = pa_channel_to_cubeb_channel(
ChannelPosition::try_from(cm.map[i]).unwrap_or(ChannelPosition::Invalid),
);
}
unsafe { cubeb::cubeb_channel_map_to_layout(&cubeb_map) }
ChannelLayout::from(unsafe { cubeb_channel_map_to_layout(&cubeb_map) })
}
#[derive(Debug)]
@ -52,104 +56,106 @@ pub struct DefaultInfo {
pub flags: pulse::SinkFlags,
}
pub const PULSE_OPS: Ops = capi_new!(PulseContext, PulseStream);
#[derive(Debug)]
pub struct Context {
pub ops: *const cubeb::Ops,
pub struct PulseContext {
_ops: *const Ops,
pub mainloop: pulse::ThreadedMainloop,
pub context: Option<pulse::Context>,
pub default_sink_info: Option<DefaultInfo>,
pub context_name: Option<CString>,
pub collection_changed_callback: cubeb::DeviceCollectionChangedCallback,
pub collection_changed_callback: ffi::cubeb_device_collection_changed_callback,
pub collection_changed_user_ptr: *mut c_void,
pub error: bool,
pub version_2_0_0: bool,
pub version_0_9_8: bool,
#[cfg(feature = "pulse-dlopen")]
pub libpulse: LibLoader,
#[cfg(feature = "pulse-dlopen")] pub libpulse: LibLoader,
devids: RefCell<Intern>,
}
impl Drop for Context {
fn drop(&mut self) {
self.destroy();
}
}
impl Context {
impl PulseContext {
#[cfg(feature = "pulse-dlopen")]
fn _new(name: Option<CString>) -> Result<Box<Self>> {
let libpulse = unsafe { open() };
if libpulse.is_none() {
return Err(cubeb::ERROR);
return Err(Error::error());
}
let ctx = Box::new(Context {
ops: &PULSE_OPS,
libpulse: libpulse.unwrap(),
mainloop: pulse::ThreadedMainloop::new(),
context: None,
default_sink_info: None,
context_name: name,
collection_changed_callback: None,
collection_changed_user_ptr: ptr::null_mut(),
error: true,
version_0_9_8: false,
version_2_0_0: false,
devids: RefCell::new(Intern::new()),
});
let ctx = Box::new(PulseContext {
_ops: &PULSE_OPS,
libpulse: libpulse.unwrap(),
mainloop: pulse::ThreadedMainloop::new(),
context: None,
default_sink_info: None,
context_name: name,
collection_changed_callback: None,
collection_changed_user_ptr: ptr::null_mut(),
error: true,
version_0_9_8: false,
version_2_0_0: false,
devids: RefCell::new(Intern::new()),
});
Ok(ctx)
}
#[cfg(not(feature = "pulse-dlopen"))]
fn _new(name: Option<CString>) -> Result<Box<Self>> {
Ok(Box::new(Context {
ops: &PULSE_OPS,
mainloop: pulse::ThreadedMainloop::new(),
context: None,
default_sink_info: None,
context_name: name,
collection_changed_callback: None,
collection_changed_user_ptr: ptr::null_mut(),
error: true,
version_0_9_8: false,
version_2_0_0: false,
devids: RefCell::new(Intern::new()),
}))
Ok(Box::new(PulseContext {
_ops: &PULSE_OPS,
mainloop: pulse::ThreadedMainloop::new(),
context: None,
default_sink_info: None,
context_name: name,
collection_changed_callback: None,
collection_changed_user_ptr: ptr::null_mut(),
error: true,
version_0_9_8: false,
version_2_0_0: false,
devids: RefCell::new(Intern::new()),
}))
}
pub fn new(name: *const c_char) -> Result<Box<Self>> {
fn new(name: Option<&CStr>) -> Result<Box<Self>> {
fn server_info_cb(context: &pulse::Context, info: &pulse::ServerInfo, u: *mut c_void) {
fn sink_info_cb(_: &pulse::Context, i: *const pulse::SinkInfo, eol: i32, u: *mut c_void) {
let ctx = unsafe { &mut *(u as *mut Context) };
fn sink_info_cb(
_: &pulse::Context,
i: *const pulse::SinkInfo,
eol: i32,
u: *mut c_void,
) {
let ctx = unsafe { &mut *(u as *mut PulseContext) };
if eol == 0 {
let info = unsafe { &*i };
let flags = pulse::SinkFlags::from_bits_truncate(info.flags);
ctx.default_sink_info = Some(DefaultInfo {
sample_spec: info.sample_spec,
channel_map: info.channel_map,
flags: flags,
});
sample_spec: info.sample_spec,
channel_map: info.channel_map,
flags: flags,
});
}
ctx.mainloop.signal();
}
let _ = context.get_sink_info_by_name(try_cstr_from(info.default_sink_name),
sink_info_cb,
u);
let _ = context.get_sink_info_by_name(
try_cstr_from(info.default_sink_name),
sink_info_cb,
u,
);
}
let name = super::try_cstr_from(name).map(|s| s.to_owned());
let mut ctx = try!(Context::_new(name));
let name = name.map(|s| s.to_owned());
let mut ctx = try!(PulseContext::_new(name));
if ctx.mainloop.start().is_err() {
ctx.destroy();
return Err(cubeb::ERROR);
return Err(Error::error());
}
if ctx.context_init() != cubeb::OK {
if ctx.context_init().is_err() {
ctx.destroy();
return Err(cubeb::ERROR);
return Err(Error::error());
}
ctx.mainloop.lock();
@ -175,63 +181,55 @@ impl Context {
self.mainloop.stop();
}
}
}
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
pub fn new_stream(&mut self,
stream_name: &CStr,
input_device: cubeb::DeviceId,
input_stream_params: Option<cubeb::StreamParams>,
output_device: cubeb::DeviceId,
output_stream_params: Option<cubeb::StreamParams>,
latency_frames: u32,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void)
-> Result<Box<Stream>> {
if self.error && self.context_init() != 0 {
return Err(cubeb::ERROR);
}
Stream::new(self,
stream_name,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency_frames,
data_callback,
state_callback,
user_ptr)
impl ContextOps for PulseContext {
fn init(context_name: Option<&CStr>) -> Result<Context> {
let ctx = try!(PulseContext::new(context_name));
Ok(unsafe { Context::from_ptr(Box::into_raw(ctx) as *mut _) })
}
pub fn max_channel_count(&self) -> Result<u32> {
fn backend_id(&mut self) -> &'static CStr {
unsafe { CStr::from_ptr(b"pulse-rust\0".as_ptr() as *const _) }
}
fn max_channel_count(&mut self) -> Result<u32> {
match self.default_sink_info {
Some(ref info) => Ok(u32::from(info.channel_map.channels)),
None => Err(cubeb::ERROR),
None => Err(Error::error()),
}
}
pub fn preferred_sample_rate(&self) -> Result<u32> {
fn min_latency(&mut self, params: StreamParams) -> Result<u32> {
// According to PulseAudio developers, this is a safe minimum.
Ok(25 * params.rate() / 1000)
}
fn preferred_sample_rate(&mut self) -> Result<u32> {
match self.default_sink_info {
Some(ref info) => Ok(info.sample_spec.rate),
None => Err(cubeb::ERROR),
None => Err(Error::error()),
}
}
pub fn min_latency(&self, params: &cubeb::StreamParams) -> Result<u32> {
// According to PulseAudio developers, this is a safe minimum.
Ok(25 * params.rate / 1000)
}
pub fn preferred_channel_layout(&self) -> Result<cubeb::ChannelLayout> {
fn preferred_channel_layout(&mut self) -> Result<ChannelLayout> {
match self.default_sink_info {
Some(ref info) => Ok(channel_map_to_layout(&info.channel_map)),
None => Err(cubeb::ERROR),
None => Err(Error::error()),
}
}
pub fn enumerate_devices(&self, devtype: cubeb::DeviceType) -> Result<cubeb::DeviceCollection> {
fn add_output_device(_: &pulse::Context, i: *const pulse::SinkInfo, eol: i32, user_data: *mut c_void) {
fn enumerate_devices(
&mut self,
devtype: DeviceType,
collection: &DeviceCollectionRef,
) -> Result<()> {
fn add_output_device(
_: &pulse::Context,
i: *const pulse::SinkInfo,
eol: i32,
user_data: *mut c_void,
) {
let list_data = unsafe { &mut *(user_data as *mut PulseDevListData) };
let ctx = &(*list_data.context);
@ -259,23 +257,23 @@ impl Context {
let info_description = unsafe { CStr::from_ptr(info.description) }.to_owned();
let preferred = if *info_name == *list_data.default_sink_name {
cubeb::DevicePref::ALL
ffi::CUBEB_DEVICE_PREF_ALL
} else {
cubeb::DevicePref::empty()
ffi::CUBEB_DEVICE_PREF_NONE
};
let device_id = ctx.devids.borrow_mut().add(info_name);
let friendly_name = info_description.into_raw();
let devinfo = cubeb::DeviceInfo {
let devinfo = ffi::cubeb_device_info {
device_id: device_id,
devid: device_id as cubeb::DeviceId,
devid: device_id as ffi::cubeb_devid,
friendly_name: friendly_name,
group_id: group_id,
vendor_name: vendor_name,
devtype: cubeb::DeviceType::OUTPUT,
device_type: ffi::CUBEB_DEVICE_TYPE_OUTPUT,
state: ctx.state_from_port(info.active_port),
preferred: preferred,
format: cubeb::DeviceFmt::all(),
format: ffi::CUBEB_DEVICE_FMT_ALL,
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
max_channels: u32::from(info.channel_map.channels),
min_rate: 1,
@ -287,7 +285,12 @@ impl Context {
list_data.devinfo.push(devinfo);
}
fn add_input_device(_: &pulse::Context, i: *const pulse::SourceInfo, eol: i32, user_data: *mut c_void) {
fn add_input_device(
_: &pulse::Context,
i: *const pulse::SourceInfo,
eol: i32,
user_data: *mut c_void,
) {
let list_data = unsafe { &mut *(user_data as *mut PulseDevListData) };
let ctx = &(*list_data.context);
@ -315,23 +318,23 @@ impl Context {
let info_description = unsafe { CStr::from_ptr(info.description) }.to_owned();
let preferred = if *info_name == *list_data.default_source_name {
cubeb::DevicePref::ALL
ffi::CUBEB_DEVICE_PREF_ALL
} else {
cubeb::DevicePref::empty()
ffi::CUBEB_DEVICE_PREF_NONE
};
let device_id = ctx.devids.borrow_mut().add(info_name);
let friendly_name = info_description.into_raw();
let devinfo = cubeb::DeviceInfo {
let devinfo = ffi::cubeb_device_info {
device_id: device_id,
devid: device_id as cubeb::DeviceId,
devid: device_id as ffi::cubeb_devid,
friendly_name: friendly_name,
group_id: group_id,
vendor_name: vendor_name,
devtype: cubeb::DeviceType::INPUT,
device_type: ffi::CUBEB_DEVICE_TYPE_INPUT,
state: ctx.state_from_port(info.active_port),
preferred: preferred,
format: cubeb::DeviceFmt::all(),
format: ffi::CUBEB_DEVICE_FMT_ALL,
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
max_channels: u32::from(info.channel_map.channels),
min_rate: 1,
@ -344,7 +347,11 @@ impl Context {
list_data.devinfo.push(devinfo);
}
fn default_device_names(_: &pulse::Context, info: &pulse::ServerInfo, user_data: *mut c_void) {
fn default_device_names(
_: &pulse::Context,
info: &pulse::ServerInfo,
user_data: *mut c_void,
) {
let list_data = unsafe { &mut *(user_data as *mut PulseDevListData) };
list_data.default_sink_name = super::try_cstr_from(info.default_sink_name)
@ -362,18 +369,24 @@ impl Context {
if let Some(ref context) = self.context {
self.mainloop.lock();
if let Ok(o) = context.get_server_info(default_device_names, &mut user_data as *mut _ as *mut _) {
if let Ok(o) =
context.get_server_info(default_device_names, &mut user_data as *mut _ as *mut _)
{
self.operation_wait(None, &o);
}
if devtype.contains(cubeb::DeviceType::OUTPUT) {
if let Ok(o) = context.get_sink_info_list(add_output_device, &mut user_data as *mut _ as *mut _) {
if devtype.contains(DeviceType::OUTPUT) {
if let Ok(o) = context
.get_sink_info_list(add_output_device, &mut user_data as *mut _ as *mut _)
{
self.operation_wait(None, &o);
}
}
if devtype.contains(cubeb::DeviceType::INPUT) {
if let Ok(o) = context.get_source_info_list(add_input_device, &mut user_data as *mut _ as *mut _) {
if devtype.contains(DeviceType::INPUT) {
if let Ok(o) = context
.get_source_info_list(add_input_device, &mut user_data as *mut _ as *mut _)
{
self.operation_wait(None, &o);
}
}
@ -385,24 +398,25 @@ impl Context {
// PulseDevListData and convert it into C representation.
let mut tmp = Vec::new();
mem::swap(&mut user_data.devinfo, &mut tmp);
let devices = tmp.into_boxed_slice();
let coll = cubeb::DeviceCollection {
device: devices.as_ptr(),
count: devices.len(),
};
let mut devices = tmp.into_boxed_slice();
let coll = unsafe { &mut *collection.as_ptr() };
coll.device = devices.as_mut_ptr();
coll.count = devices.len();
// Giving away the memory owned by devices. Don't free it!
mem::forget(devices);
Ok(coll)
Ok(())
}
pub fn device_collection_destroy(&self, collection: *mut cubeb::DeviceCollection) {
debug_assert!(!collection.is_null());
fn device_collection_destroy(&mut self, collection: &mut DeviceCollectionRef) -> Result<()> {
debug_assert!(!collection.as_ptr().is_null());
unsafe {
let coll = *collection;
let mut devices = Vec::from_raw_parts(coll.device as *mut cubeb::DeviceInfo,
coll.count,
coll.count);
let coll = &mut *collection.as_ptr();
let mut devices = Vec::from_raw_parts(
coll.device as *mut ffi::cubeb_device_info,
coll.count,
coll.count,
);
for dev in &mut devices {
if !dev.group_id.is_null() {
let _ = CString::from_raw(dev.group_id as *mut _);
@ -414,52 +428,90 @@ impl Context {
let _ = CString::from_raw(dev.friendly_name as *mut _);
}
}
coll.device = ptr::null_mut();
coll.count = 0;
}
Ok(())
}
pub fn register_device_collection_changed(&mut self,
devtype: cubeb::DeviceType,
cb: cubeb::DeviceCollectionChangedCallback,
user_ptr: *mut c_void)
-> i32 {
fn update_collection(_: &pulse::Context, event: pulse::SubscriptionEvent, index: u32, user_data: *mut c_void) {
let ctx = unsafe { &mut *(user_data as *mut Context) };
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
fn stream_init(
&mut self,
stream_name: Option<&CStr>,
input_device: DeviceId,
input_stream_params: Option<&StreamParamsRef>,
output_device: DeviceId,
output_stream_params: Option<&StreamParamsRef>,
latency_frames: u32,
data_callback: ffi::cubeb_data_callback,
state_callback: ffi::cubeb_state_callback,
user_ptr: *mut c_void,
) -> Result<Stream> {
if self.error {
let _ = try!(self.context_init());
}
let stm = try!(PulseStream::new(
self,
stream_name,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency_frames,
data_callback,
state_callback,
user_ptr,
));
Ok(unsafe { Stream::from_ptr(Box::into_raw(stm) as *mut _) })
}
fn register_device_collection_changed(
&mut self,
devtype: DeviceType,
cb: ffi::cubeb_device_collection_changed_callback,
user_ptr: *mut c_void,
) -> Result<()> {
fn update_collection(
_: &pulse::Context,
event: pulse::SubscriptionEvent,
index: u32,
user_data: *mut c_void,
) {
let ctx = unsafe { &mut *(user_data as *mut PulseContext) };
let (f, t) = (event.event_facility(), event.event_type());
match f {
pulse::SubscriptionEventFacility::Source |
pulse::SubscriptionEventFacility::Sink => {
match t {
pulse::SubscriptionEventType::Remove |
pulse::SubscriptionEventType::New => {
if cubeb::log_enabled() {
let op = if t == pulse::SubscriptionEventType::New {
"Adding"
} else {
"Removing"
};
let dev = if f == pulse::SubscriptionEventFacility::Sink {
"sink"
} else {
"source "
};
log!("{} {} index {}", op, dev, index);
unsafe {
ctx.collection_changed_callback.unwrap()(ctx as *mut _ as *mut _,
ctx.collection_changed_user_ptr);
}
}
},
_ => {},
if (f == pulse::SubscriptionEventFacility::Source)
| (f == pulse::SubscriptionEventFacility::Sink)
{
if (t == pulse::SubscriptionEventType::Remove)
| (t == pulse::SubscriptionEventType::New)
{
if log_enabled() {
let op = if t == pulse::SubscriptionEventType::New {
"Adding"
} else {
"Removing"
};
let dev = if f == pulse::SubscriptionEventFacility::Sink {
"sink"
} else {
"source "
};
cubeb_log!("{} {} index {}", op, dev, index);
}
},
_ => {},
unsafe {
ctx.collection_changed_callback.unwrap()(
ctx as *mut _ as *mut _,
ctx.collection_changed_user_ptr,
);
}
}
}
}
fn success(_: &pulse::Context, success: i32, user_data: *mut c_void) {
let ctx = unsafe { &*(user_data as *mut Context) };
let ctx = unsafe { &*(user_data as *mut PulseContext) };
debug_assert_ne!(success, 0);
ctx.mainloop.signal();
}
@ -467,7 +519,7 @@ impl Context {
self.collection_changed_callback = cb;
self.collection_changed_user_ptr = user_ptr;
let user_data: *mut c_void = self as *mut _ as *mut _;
let user_data: *mut c_void = self as *const _ as *mut _;
if let Some(ref context) = self.context {
self.mainloop.lock();
@ -477,10 +529,10 @@ impl Context {
context.clear_subscribe_callback();
} else {
context.set_subscribe_callback(update_collection, user_data);
if devtype.contains(cubeb::DeviceType::INPUT) {
if devtype.contains(DeviceType::INPUT) {
mask |= pulse::SubscriptionMask::SOURCE
};
if devtype.contains(cubeb::DeviceType::OUTPUT) {
if devtype.contains(DeviceType::OUTPUT) {
mask = pulse::SubscriptionMask::SINK
};
}
@ -489,19 +541,28 @@ impl Context {
self.operation_wait(None, &o);
} else {
self.mainloop.unlock();
log!("Context subscribe failed");
return cubeb::ERROR;
cubeb_log!("Context subscribe failed");
return Err(Error::error());
}
self.mainloop.unlock();
}
cubeb::OK
Ok(())
}
}
pub fn context_init(&mut self) -> i32 {
impl Drop for PulseContext {
fn drop(&mut self) {
self.destroy();
}
}
impl PulseContext {
/* Initialize PulseAudio Context */
fn context_init(&mut self) -> Result<()> {
fn error_state(c: &pulse::Context, u: *mut c_void) {
let ctx = unsafe { &mut *(u as *mut Context) };
let ctx = unsafe { &mut *(u as *mut PulseContext) };
if !c.get_state().is_good() {
ctx.error = true;
}
@ -523,7 +584,7 @@ impl Context {
let context_ptr: *mut c_void = self as *mut _ as *mut _;
if self.context.is_none() {
return cubeb::ERROR;
return Err(Error::error());
}
self.mainloop.lock();
@ -535,25 +596,27 @@ impl Context {
if !self.wait_until_context_ready() {
self.mainloop.unlock();
self.context_destroy();
return cubeb::ERROR;
return Err(Error::error());
}
self.mainloop.unlock();
let version_str = unsafe { CStr::from_ptr(pulse::library_version()) };
if let Ok(version) = semver::Version::parse(&version_str.to_string_lossy()) {
self.version_0_9_8 = version >= semver::Version::parse("0.9.8").expect("Failed to parse version");
self.version_2_0_0 = version >= semver::Version::parse("2.0.0").expect("Failed to parse version");
self.version_0_9_8 =
version >= semver::Version::parse("0.9.8").expect("Failed to parse version");
self.version_2_0_0 =
version >= semver::Version::parse("2.0.0").expect("Failed to parse version");
}
self.error = false;
cubeb::OK
Ok(())
}
fn context_destroy(&mut self) {
fn drain_complete(_: &pulse::Context, u: *mut c_void) {
let ctx = unsafe { &*(u as *mut Context) };
let ctx = unsafe { &*(u as *mut PulseContext) };
ctx.mainloop.signal();
}
@ -571,7 +634,8 @@ impl Context {
}
pub fn operation_wait<'a, S>(&self, s: S, o: &pulse::Operation) -> bool
where S: Into<Option<&'a pulse::Stream>>
where
S: Into<Option<&'a pulse::Stream>>,
{
let stream = s.into();
while o.get_state() == PA_OPERATION_RUNNING {
@ -609,16 +673,16 @@ impl Context {
true
}
fn state_from_port(&self, i: *const pa_port_info) -> cubeb::DeviceState {
fn state_from_port(&self, i: *const pa_port_info) -> ffi::cubeb_device_state {
if !i.is_null() {
let info = unsafe { *i };
if self.version_2_0_0 && info.available == PA_PORT_AVAILABLE_NO {
cubeb::DeviceState::Unplugged
ffi::CUBEB_DEVICE_STATE_UNPLUGGED
} else {
cubeb::DeviceState::Enabled
ffi::CUBEB_DEVICE_STATE_ENABLED
}
} else {
cubeb::DeviceState::Enabled
ffi::CUBEB_DEVICE_STATE_ENABLED
}
}
}
@ -626,13 +690,14 @@ impl Context {
struct PulseDevListData<'a> {
default_sink_name: CString,
default_source_name: CString,
devinfo: Vec<cubeb::DeviceInfo>,
context: &'a Context,
devinfo: Vec<ffi::cubeb_device_info>,
context: &'a PulseContext,
}
impl<'a> PulseDevListData<'a> {
pub fn new<'b>(context: &'b Context) -> Self
where 'b: 'a
pub fn new<'b>(context: &'b PulseContext) -> Self
where
'b: 'a,
{
PulseDevListData {
default_sink_name: CString::default(),
@ -651,13 +716,13 @@ impl<'a> Drop for PulseDevListData<'a> {
}
}
fn pulse_format_to_cubeb_format(format: pa_sample_format_t) -> cubeb::DeviceFmt {
fn pulse_format_to_cubeb_format(format: pa_sample_format_t) -> ffi::cubeb_device_fmt {
match format {
PA_SAMPLE_S16LE => cubeb::DeviceFmt::S16LE,
PA_SAMPLE_S16BE => cubeb::DeviceFmt::S16BE,
PA_SAMPLE_FLOAT32LE => cubeb::DeviceFmt::F32LE,
PA_SAMPLE_FLOAT32BE => cubeb::DeviceFmt::F32BE,
PA_SAMPLE_S16LE => ffi::CUBEB_DEVICE_FMT_S16LE,
PA_SAMPLE_S16BE => ffi::CUBEB_DEVICE_FMT_S16BE,
PA_SAMPLE_FLOAT32LE => ffi::CUBEB_DEVICE_FMT_F32LE,
PA_SAMPLE_FLOAT32BE => ffi::CUBEB_DEVICE_FMT_F32BE,
// Unsupported format, return F32NE
_ => cubeb::DeviceFmt::F32NE,
_ => ffi::CUBEB_DEVICE_FMT_F32NE,
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright © 2017 Mozilla Foundation
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright © 2017 Mozilla Foundation
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
@ -8,18 +8,18 @@ use std::os::raw::c_char;
#[derive(Debug)]
pub struct Intern {
vec: Vec<Box<CString>>
vec: Vec<Box<CString>>,
}
impl Intern {
pub fn new() -> Intern {
Intern {
vec: Vec::new()
}
Intern { vec: Vec::new() }
}
pub fn add(&mut self, string: &CStr) -> *const c_char {
fn eq(s1: &CStr, s2: &CStr) -> bool { s1 == s2 }
fn eq(s1: &CStr, s2: &CStr) -> bool {
s1 == s2
}
for s in &self.vec {
if eq(s, string) {
return s.as_ptr();
@ -33,8 +33,8 @@ impl Intern {
#[cfg(test)]
mod tests {
use std::ffi::CStr;
use super::Intern;
use std::ffi::CStr;
#[test]
fn intern() {

Просмотреть файл

@ -0,0 +1,99 @@
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use cubeb_backend::ffi::*;
static CHANNEL_LAYOUT_UNDEFINED: &'static [cubeb_channel] = &[CHANNEL_INVALID];
static CHANNEL_LAYOUT_DUAL_MONO: &'static [cubeb_channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
static CHANNEL_LAYOUT_DUAL_MONO_LFE: &'static [cubeb_channel] =
&[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
static CHANNEL_LAYOUT_MONO: &'static [cubeb_channel] = &[CHANNEL_MONO];
static CHANNEL_LAYOUT_MONO_LFE: &'static [cubeb_channel] = &[CHANNEL_MONO, CHANNEL_LFE];
static CHANNEL_LAYOUT_STEREO: &'static [cubeb_channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
static CHANNEL_LAYOUT_STEREO_LFE: &'static [cubeb_channel] =
&[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
static CHANNEL_LAYOUT_3F: &'static [cubeb_channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER];
static CHANNEL_LAYOUT_3FLFE: &'static [cubeb_channel] =
&[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE];
static CHANNEL_LAYOUT_2F1: &'static [cubeb_channel] =
&[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_2F1LFE: &'static [cubeb_channel] =
&[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_3F1: &'static [cubeb_channel] =
&[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_3F1LFE: &'static [cubeb_channel] = &[
CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RCENTER,
];
static CHANNEL_LAYOUT_2F2: &'static [cubeb_channel] =
&[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS];
static CHANNEL_LAYOUT_2F2LFE: &'static [cubeb_channel] = &[
CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_LFE,
CHANNEL_LS,
CHANNEL_RS,
];
static CHANNEL_LAYOUT_3F2: &'static [cubeb_channel] = &[
CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LS,
CHANNEL_RS,
];
static CHANNEL_LAYOUT_3F2LFE: &'static [cubeb_channel] = &[
CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_LS,
CHANNEL_RS,
];
static CHANNEL_LAYOUT_3F3RLFE: &'static [cubeb_channel] = &[
CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RCENTER,
CHANNEL_LS,
CHANNEL_RS,
];
static CHANNEL_LAYOUT_3F4LFE: &'static [cubeb_channel] = &[
CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RLS,
CHANNEL_RRS,
CHANNEL_LS,
CHANNEL_RS,
];
pub fn channel_index_to_order(layout: cubeb_channel_layout) -> &'static [cubeb_channel] {
match layout {
CUBEB_LAYOUT_DUAL_MONO => CHANNEL_LAYOUT_DUAL_MONO,
CUBEB_LAYOUT_DUAL_MONO_LFE => CHANNEL_LAYOUT_DUAL_MONO_LFE,
CUBEB_LAYOUT_MONO => CHANNEL_LAYOUT_MONO,
CUBEB_LAYOUT_MONO_LFE => CHANNEL_LAYOUT_MONO_LFE,
CUBEB_LAYOUT_STEREO => CHANNEL_LAYOUT_STEREO,
CUBEB_LAYOUT_STEREO_LFE => CHANNEL_LAYOUT_STEREO_LFE,
CUBEB_LAYOUT_3F => CHANNEL_LAYOUT_3F,
CUBEB_LAYOUT_3F_LFE => CHANNEL_LAYOUT_3FLFE,
CUBEB_LAYOUT_2F1 => CHANNEL_LAYOUT_2F1,
CUBEB_LAYOUT_2F1_LFE => CHANNEL_LAYOUT_2F1LFE,
CUBEB_LAYOUT_3F1 => CHANNEL_LAYOUT_3F1,
CUBEB_LAYOUT_3F1_LFE => CHANNEL_LAYOUT_3F1LFE,
CUBEB_LAYOUT_2F2 => CHANNEL_LAYOUT_2F2,
CUBEB_LAYOUT_2F2_LFE => CHANNEL_LAYOUT_2F2LFE,
CUBEB_LAYOUT_3F2 => CHANNEL_LAYOUT_3F2,
CUBEB_LAYOUT_3F2_LFE => CHANNEL_LAYOUT_3F2LFE,
CUBEB_LAYOUT_3F3R_LFE => CHANNEL_LAYOUT_3F3RLFE,
CUBEB_LAYOUT_3F4_LFE => CHANNEL_LAYOUT_3F4LFE,
_ => CHANNEL_LAYOUT_UNDEFINED,
}
}

Просмотреть файл

@ -1,24 +1,26 @@
// Copyright © 2017 Mozilla Foundation
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
mod context;
mod cork_state;
mod mixer;
mod stream;
mod intern;
use std::os::raw::c_char;
use std::ffi::CStr;
pub type Result<T> = ::std::result::Result<T, i32>;
pub use self::context::Context;
pub use self::stream::Device;
pub use self::stream::Stream;
pub use self::context::PulseContext;
use self::intern::Intern;
pub use self::stream::Device;
pub use self::stream::PulseStream;
use std::ffi::CStr;
use std::os::raw::c_char;
// helper to convert *const c_char to Option<CStr>
fn try_cstr_from<'str>(s: *const c_char) -> Option<&'str CStr> {
if s.is_null() { None } else { Some(unsafe { CStr::from_ptr(s) }) }
if s.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(s) })
}
}

Просмотреть файл

@ -1,47 +1,43 @@
// Copyright © 2017 Mozilla Foundation
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use backend::*;
use backend::cork_state::CorkState;
use cubeb;
use cubeb_backend::{ffi, log_enabled, ChannelLayout, DeviceId, DeviceRef, Error, Result,
SampleFormat, StreamOps, StreamParamsRef, StreamPrefs};
use pulse::{self, CVolumeExt, ChannelMapExt, SampleSpecExt, StreamLatency, USecExt};
use pulse_ffi::*;
use std::{mem, ptr};
use std::ffi::{CStr, CString};
use std::os::raw::{c_long, c_void};
use std::ptr;
const PULSE_NO_GAIN: f32 = -1.0;
fn cubeb_channel_to_pa_channel(channel: cubeb::Channel) -> pa_channel_position_t {
assert_ne!(channel, cubeb::CHANNEL_INVALID);
fn cubeb_channel_to_pa_channel(channel: ffi::cubeb_channel) -> pa_channel_position_t {
use cubeb_backend::ffi::*;
assert_ne!(channel, CHANNEL_INVALID);
// This variable may be used for multiple times, so we should avoid to
// allocate it in stack, or it will be created and removed repeatedly.
// Use static to allocate this local variable in data space instead of stack.
static MAP: [pa_channel_position_t; 10] = [
// PA_CHANNEL_POSITION_INVALID, // CHANNEL_INVALID
PA_CHANNEL_POSITION_MONO, // CHANNEL_MONO
PA_CHANNEL_POSITION_FRONT_LEFT, // CHANNEL_LEFT
PA_CHANNEL_POSITION_FRONT_RIGHT, // CHANNEL_RIGHT
PA_CHANNEL_POSITION_FRONT_CENTER, // CHANNEL_CENTER
PA_CHANNEL_POSITION_SIDE_LEFT, // CHANNEL_LS
PA_CHANNEL_POSITION_SIDE_RIGHT, // CHANNEL_RS
PA_CHANNEL_POSITION_REAR_LEFT, // CHANNEL_RLS
PA_CHANNEL_POSITION_REAR_CENTER, // CHANNEL_RCENTER
PA_CHANNEL_POSITION_REAR_RIGHT, // CHANNEL_RRS
PA_CHANNEL_POSITION_LFE // CHANNEL_LFE
];
let idx: i32 = channel;
MAP[idx as usize]
match channel {
CHANNEL_LEFT => PA_CHANNEL_POSITION_FRONT_LEFT,
CHANNEL_RIGHT => PA_CHANNEL_POSITION_FRONT_RIGHT,
CHANNEL_CENTER => PA_CHANNEL_POSITION_FRONT_CENTER,
CHANNEL_LS => PA_CHANNEL_POSITION_SIDE_LEFT,
CHANNEL_RS => PA_CHANNEL_POSITION_SIDE_RIGHT,
CHANNEL_RLS => PA_CHANNEL_POSITION_REAR_LEFT,
CHANNEL_RCENTER => PA_CHANNEL_POSITION_REAR_CENTER,
CHANNEL_RRS => PA_CHANNEL_POSITION_REAR_RIGHT,
CHANNEL_LFE => PA_CHANNEL_POSITION_LFE,
// Also handles CHANNEL_MONO case
_ => PA_CHANNEL_POSITION_MONO,
}
}
fn layout_to_channel_map(layout: cubeb::ChannelLayout) -> pulse::ChannelMap {
assert_ne!(layout, cubeb::LAYOUT_UNDEFINED);
fn layout_to_channel_map(layout: ChannelLayout) -> pulse::ChannelMap {
assert_ne!(layout, ChannelLayout::Undefined);
let order = cubeb::mixer::channel_index_to_order(layout);
let order = mixer::channel_index_to_order(layout.into());
let mut cm = pulse::ChannelMap::init();
cm.channels = order.len() as u8;
@ -51,78 +47,74 @@ fn layout_to_channel_map(layout: cubeb::ChannelLayout) -> pulse::ChannelMap {
cm
}
pub struct Device(cubeb::Device);
pub struct Device(ffi::cubeb_device);
impl Drop for Device {
fn drop(&mut self) {
unsafe {
if !self.0.input_name.is_null() {
let _ = CString::from_raw(self.0.input_name);
let _ = CString::from_raw(self.0.input_name as *mut _);
}
if !self.0.output_name.is_null() {
let _ = CString::from_raw(self.0.output_name);
let _ = CString::from_raw(self.0.output_name as *mut _);
}
}
}
}
#[derive(Debug)]
pub struct Stream<'ctx> {
context: &'ctx Context,
pub struct PulseStream<'ctx> {
context: &'ctx PulseContext,
user_ptr: *mut c_void,
output_stream: Option<pulse::Stream>,
input_stream: Option<pulse::Stream>,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void,
data_callback: ffi::cubeb_data_callback,
state_callback: ffi::cubeb_state_callback,
drain_timer: *mut pa_time_event,
output_sample_spec: pulse::SampleSpec,
input_sample_spec: pulse::SampleSpec,
shutdown: bool,
volume: f32,
state: cubeb::State,
state: ffi::cubeb_state,
}
impl<'ctx> Drop for Stream<'ctx> {
fn drop(&mut self) {
self.destroy();
}
}
impl<'ctx> Stream<'ctx> {
impl<'ctx> PulseStream<'ctx> {
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
pub fn new(context: &'ctx Context,
stream_name: &CStr,
input_device: cubeb::DeviceId,
input_stream_params: Option<cubeb::StreamParams>,
output_device: cubeb::DeviceId,
output_stream_params: Option<cubeb::StreamParams>,
latency_frames: u32,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void)
-> Result<Box<Stream<'ctx>>> {
pub fn new(
context: &'ctx PulseContext,
stream_name: Option<&CStr>,
input_device: DeviceId,
input_stream_params: Option<&StreamParamsRef>,
output_device: DeviceId,
output_stream_params: Option<&StreamParamsRef>,
latency_frames: u32,
data_callback: ffi::cubeb_data_callback,
state_callback: ffi::cubeb_state_callback,
user_ptr: *mut c_void,
) -> Result<Box<Self>> {
fn check_error(s: &pulse::Stream, u: *mut c_void) {
let stm = unsafe { &mut *(u as *mut Stream) };
let stm = unsafe { &mut *(u as *mut PulseStream) };
if !s.get_state().is_good() {
stm.state_change_callback(cubeb::STATE_ERROR);
stm.state_change_callback(ffi::CUBEB_STATE_ERROR);
}
stm.context.mainloop.signal();
}
fn read_data(s: &pulse::Stream, nbytes: usize, u: *mut c_void) {
fn read_from_input(s: &pulse::Stream, buffer: *mut *const c_void, size: *mut usize) -> i32 {
let readable_size: i32 = s.readable_size()
.and_then(|s| Ok(s as i32))
.unwrap_or(-1);
fn read_from_input(
s: &pulse::Stream,
buffer: *mut *const c_void,
size: *mut usize,
) -> i32 {
let readable_size: i32 = s.readable_size().and_then(|s| Ok(s as i32)).unwrap_or(-1);
if readable_size > 0 && unsafe { s.peek(buffer, size).is_err() } {
return -1;
}
readable_size
}
logv!("Input callback buffer size {}", nbytes);
let stm = unsafe { &mut *(u as *mut Stream) };
cubeb_logv!("Input callback buffer size {}", nbytes);
let stm = unsafe { &mut *(u as *mut PulseStream) };
if stm.shutdown {
return;
}
@ -144,11 +136,13 @@ impl<'ctx> Stream<'ctx> {
} else {
// input/capture only operation. Call callback directly
let got = unsafe {
stm.data_callback.unwrap()(stm as *mut _ as *mut _,
stm.user_ptr,
read_data,
ptr::null_mut(),
read_frames as c_long)
stm.data_callback.unwrap()(
stm as *mut _ as *mut _,
stm.user_ptr,
read_data,
ptr::null_mut(),
read_frames as c_long,
)
};
if got < 0 || got as usize != read_frames {
@ -170,9 +164,9 @@ impl<'ctx> Stream<'ctx> {
}
fn write_data(_: &pulse::Stream, nbytes: usize, u: *mut c_void) {
logv!("Output callback to be written buffer size {}", nbytes);
let stm = unsafe { &mut *(u as *mut Stream) };
if stm.shutdown || stm.state != cubeb::STATE_STARTED {
cubeb_logv!("Output callback to be written buffer size {}", nbytes);
let stm = unsafe { &mut *(u as *mut PulseStream) };
if stm.shutdown || stm.state != ffi::CUBEB_STATE_STARTED {
return;
}
@ -184,56 +178,60 @@ impl<'ctx> Stream<'ctx> {
}
}
let mut stm = Box::new(Stream {
context: context,
output_stream: None,
input_stream: None,
data_callback: data_callback,
state_callback: state_callback,
user_ptr: user_ptr,
drain_timer: ptr::null_mut(),
output_sample_spec: pulse::SampleSpec::default(),
input_sample_spec: pulse::SampleSpec::default(),
shutdown: false,
volume: PULSE_NO_GAIN,
state: cubeb::STATE_ERROR,
});
let mut stm = Box::new(PulseStream {
context: context,
output_stream: None,
input_stream: None,
data_callback: data_callback,
state_callback: state_callback,
user_ptr: user_ptr,
drain_timer: ptr::null_mut(),
output_sample_spec: pulse::SampleSpec::default(),
input_sample_spec: pulse::SampleSpec::default(),
shutdown: false,
volume: PULSE_NO_GAIN,
state: ffi::CUBEB_STATE_ERROR,
});
if let Some(ref context) = stm.context.context {
stm.context.mainloop.lock();
// Setup output stream
if let Some(ref stream_params) = output_stream_params {
match Stream::stream_init(context, stream_params, stream_name) {
if let Some(stream_params) = output_stream_params {
match PulseStream::stream_init(context, stream_params, stream_name) {
Ok(s) => {
stm.output_sample_spec = *s.get_sample_spec();
s.set_state_callback(check_error, stm.as_mut() as *mut _ as *mut _);
s.set_write_callback(write_data, stm.as_mut() as *mut _ as *mut _);
let battr = set_buffering_attribute(latency_frames, &stm.output_sample_spec);
let battr =
set_buffering_attribute(latency_frames, &stm.output_sample_spec);
let device_name = super::try_cstr_from(output_device as *const _);
let _ = s.connect_playback(device_name,
&battr,
pulse::StreamFlags::AUTO_TIMING_UPDATE | pulse::StreamFlags::INTERPOLATE_TIMING |
pulse::StreamFlags::START_CORKED |
pulse::StreamFlags::ADJUST_LATENCY,
None,
None);
let _ = s.connect_playback(
device_name,
&battr,
pulse::StreamFlags::AUTO_TIMING_UPDATE
| pulse::StreamFlags::INTERPOLATE_TIMING
| pulse::StreamFlags::START_CORKED
| pulse::StreamFlags::ADJUST_LATENCY,
None,
None,
);
stm.output_stream = Some(s);
},
}
Err(e) => {
stm.context.mainloop.unlock();
stm.destroy();
return Err(e);
},
}
}
}
// Set up input stream
if let Some(ref stream_params) = input_stream_params {
match Stream::stream_init(context, stream_params, stream_name) {
match PulseStream::stream_init(context, stream_params, stream_name) {
Ok(s) => {
stm.input_sample_spec = *s.get_sample_spec();
@ -242,19 +240,22 @@ impl<'ctx> Stream<'ctx> {
let battr = set_buffering_attribute(latency_frames, &stm.input_sample_spec);
let device_name = super::try_cstr_from(input_device as *const _);
let _ = s.connect_record(device_name,
&battr,
pulse::StreamFlags::AUTO_TIMING_UPDATE | pulse::StreamFlags::INTERPOLATE_TIMING |
pulse::StreamFlags::START_CORKED |
pulse::StreamFlags::ADJUST_LATENCY);
let _ = s.connect_record(
device_name,
&battr,
pulse::StreamFlags::AUTO_TIMING_UPDATE
| pulse::StreamFlags::INTERPOLATE_TIMING
| pulse::StreamFlags::START_CORKED
| pulse::StreamFlags::ADJUST_LATENCY,
);
stm.input_stream = Some(s);
},
}
Err(e) => {
stm.context.mainloop.unlock();
stm.destroy();
return Err(e);
},
}
}
}
@ -270,30 +271,35 @@ impl<'ctx> Stream<'ctx> {
if !r {
stm.destroy();
return Err(cubeb::ERROR);
return Err(Error::error());
}
if cubeb::log_enabled() {
// TODO:
if log_enabled() {
if let Some(ref output_stream) = stm.output_stream {
let output_att = output_stream.get_buffer_attr();
log!("Output buffer attributes maxlength {}, tlength {}, \
cubeb_log!(
"Output buffer attributes maxlength {}, tlength {}, \
prebuf {}, minreq {}, fragsize {}",
output_att.maxlength,
output_att.tlength,
output_att.prebuf,
output_att.minreq,
output_att.fragsize);
output_att.maxlength,
output_att.tlength,
output_att.prebuf,
output_att.minreq,
output_att.fragsize
);
}
if let Some(ref input_stream) = stm.input_stream {
let input_att = input_stream.get_buffer_attr();
log!("Input buffer attributes maxlength {}, tlength {}, \
prebuf {}, minreq {}, fragsize {}",
input_att.maxlength,
input_att.tlength,
input_att.prebuf,
input_att.minreq,
input_att.fragsize);
cubeb_log!(
"Input buffer attributes maxlength {}, tlength {}, \
prebuf {}, minreq {}, fragsize {}",
input_att.maxlength,
input_att.tlength,
input_att.prebuf,
input_att.minreq,
input_att.fragsize
);
}
}
}
@ -309,10 +315,7 @@ impl<'ctx> Stream<'ctx> {
if let Some(stm) = self.output_stream.take() {
if !self.drain_timer.is_null() {
/* there's no pa_rttime_free, so use this instead. */
self.context
.mainloop
.get_api()
.time_free(self.drain_timer);
self.context.mainloop.get_api().time_free(self.drain_timer);
}
stm.clear_state_callback();
stm.clear_write_callback();
@ -329,10 +332,18 @@ impl<'ctx> Stream<'ctx> {
}
self.context.mainloop.unlock();
}
}
pub fn start(&mut self) -> i32 {
impl<'ctx> Drop for PulseStream<'ctx> {
fn drop(&mut self) {
self.destroy();
}
}
impl<'ctx> StreamOps for PulseStream<'ctx> {
fn start(&mut self) -> Result<()> {
fn output_preroll(_: &pulse::MainloopApi, u: *mut c_void) {
let stm = unsafe { &mut *(u as *mut Stream) };
let stm = unsafe { &mut *(u as *mut PulseStream) };
if !stm.shutdown {
let size = stm.output_stream
.as_ref()
@ -352,14 +363,14 @@ impl<'ctx> Stream<'ctx> {
self.context
.mainloop
.get_api()
.once(output_preroll, self as *mut _ as *mut _);
.once(output_preroll, self as *const _ as *mut _);
self.context.mainloop.unlock();
}
cubeb::OK
Ok(())
}
pub fn stop(&mut self) -> i32 {
fn stop(&mut self) -> Result<()> {
{
self.context.mainloop.lock();
self.shutdown = true;
@ -371,27 +382,31 @@ impl<'ctx> Stream<'ctx> {
}
self.cork(CorkState::cork() | CorkState::notify());
cubeb::OK
Ok(())
}
pub fn position(&self) -> Result<u64> {
fn reset_default_device(&mut self) -> Result<()> {
Err(not_supported())
}
fn position(&mut self) -> Result<u64> {
let in_thread = self.context.mainloop.in_thread();
if !in_thread {
self.context.mainloop.lock();
}
let r = match self.output_stream {
None => Err(cubeb::ERROR),
Some(ref stm) => {
match stm.get_time() {
Ok(r_usec) => {
let bytes = r_usec.to_bytes(&self.output_sample_spec);
Ok((bytes / self.output_sample_spec.frame_size()) as u64)
},
Err(_) => Err(cubeb::ERROR),
}
},
if self.output_stream.is_none() {
return Err(Error::error());
}
let stm = self.output_stream.as_ref().unwrap();
let r = match stm.get_time() {
Ok(r_usec) => {
let bytes = r_usec.to_bytes(&self.output_sample_spec);
Ok((bytes / self.output_sample_spec.frame_size()) as u64)
}
Err(_) => Err(Error::error()),
};
if !in_thread {
@ -401,27 +416,26 @@ impl<'ctx> Stream<'ctx> {
r
}
pub fn latency(&self) -> Result<u32> {
fn latency(&mut self) -> Result<u32> {
match self.output_stream {
None => Err(cubeb::ERROR),
Some(ref stm) => {
match stm.get_latency() {
Ok(StreamLatency::Positive(r_usec)) => {
let latency = (r_usec * pa_usec_t::from(self.output_sample_spec.rate) / PA_USEC_PER_SEC) as u32;
Ok(latency)
},
Ok(_) => {
panic!("Can not handle negative latency values.");
},
Err(_) => Err(cubeb::ERROR),
None => Err(Error::error()),
Some(ref stm) => match stm.get_latency() {
Ok(StreamLatency::Positive(r_usec)) => {
let latency = (r_usec * pa_usec_t::from(self.output_sample_spec.rate)
/ PA_USEC_PER_SEC) as u32;
Ok(latency)
}
Ok(_) => {
panic!("Can not handle negative latency values.");
}
Err(_) => Err(Error::error()),
},
}
}
pub fn set_volume(&mut self, volume: f32) -> i32 {
fn set_volume(&mut self, volume: f32) -> Result<()> {
match self.output_stream {
None => cubeb::ERROR,
None => Err(Error::error()),
Some(ref stm) => {
if let Some(ref context) = self.context.context {
self.context.mainloop.lock();
@ -448,28 +462,38 @@ impl<'ctx> Stream<'ctx> {
let index = stm.get_index();
let context_ptr = self.context as *const _ as *mut _;
if let Ok(o) = context.set_sink_input_volume(index, &cvol, context_success, context_ptr) {
if let Ok(o) = context.set_sink_input_volume(
index,
&cvol,
context_success,
context_ptr,
) {
self.context.operation_wait(stm, &o);
}
}
self.context.mainloop.unlock();
cubeb::OK
Ok(())
} else {
cubeb::ERROR
Err(Error::error())
}
},
}
}
}
pub fn set_panning(&mut self, panning: f32) -> i32 {
fn set_panning(&mut self, panning: f32) -> Result<()> {
#[repr(C)]
struct SinkInputInfoResult<'a> {
pub cvol: pulse::CVolume,
pub mainloop: &'a pulse::ThreadedMainloop,
}
fn get_input_volume(_: &pulse::Context, info: *const pulse::SinkInputInfo, eol: i32, u: *mut c_void) {
fn get_input_volume(
_: &pulse::Context,
info: *const pulse::SinkInputInfo,
eol: i32,
u: *mut c_void,
) {
let r = unsafe { &mut *(u as *mut SinkInputInfoResult) };
if eol == 0 {
let info = unsafe { *info };
@ -478,126 +502,149 @@ impl<'ctx> Stream<'ctx> {
r.mainloop.signal();
}
match self.output_stream {
None => cubeb::ERROR,
Some(ref stm) => {
if let Some(ref context) = self.context.context {
self.context.mainloop.lock();
let map = stm.get_channel_map();
if !map.can_balance() {
self.context.mainloop.unlock();
return cubeb::ERROR;
}
let index = stm.get_index();
let mut r = SinkInputInfoResult {
cvol: pulse::CVolume::default(),
mainloop: &self.context.mainloop,
};
if let Ok(o) = context.get_sink_input_info(index, get_input_volume, &mut r as *mut _ as *mut _) {
self.context.operation_wait(stm, &o);
}
r.cvol.set_balance(map, panning);
let context_ptr = self.context as *const _ as *mut _;
if let Ok(o) = context.set_sink_input_volume(index, &r.cvol, context_success, context_ptr) {
self.context.operation_wait(stm, &o);
}
self.context.mainloop.unlock();
cubeb::OK
} else {
cubeb::ERROR
}
},
if self.output_stream.is_none() {
return Err(Error::error());
}
}
pub fn current_device(&self) -> Result<Box<cubeb::Device>> {
if self.context.version_0_9_8 {
let mut dev = Box::new(cubeb::Device::default());
let stm = self.output_stream.as_ref().unwrap();
if self.input_stream.is_some() {
if let Some(ref stm) = self.input_stream {
dev.input_name = match stm.get_device_name() {
Ok(name) => name.to_owned().into_raw(),
Err(_) => {
return Err(cubeb::ERROR);
},
}
}
if let Some(ref context) = self.context.context {
self.context.mainloop.lock();
let map = stm.get_channel_map();
if !map.can_balance() {
self.context.mainloop.unlock();
return Err(Error::error());
}
if !self.output_stream.is_some() {
if let Some(ref stm) = self.output_stream {
dev.output_name = match stm.get_device_name() {
Ok(name) => name.to_owned().into_raw(),
Err(_) => {
return Err(cubeb::ERROR);
},
}
}
let index = stm.get_index();
let mut r = SinkInputInfoResult {
cvol: pulse::CVolume::default(),
mainloop: &self.context.mainloop,
};
if let Ok(o) =
context.get_sink_input_info(index, get_input_volume, &mut r as *mut _ as *mut _)
{
self.context.operation_wait(stm, &o);
}
Ok(dev)
r.cvol.set_balance(map, panning);
let context_ptr = self.context as *const _ as *mut _;
if let Ok(o) =
context.set_sink_input_volume(index, &r.cvol, context_success, context_ptr)
{
self.context.operation_wait(stm, &o);
}
self.context.mainloop.unlock();
Ok(())
} else {
Err(cubeb::ERROR_NOT_SUPPORTED)
Err(Error::error())
}
}
fn stream_init(context: &pulse::Context,
stream_params: &cubeb::StreamParams,
stream_name: &CStr)
-> Result<pulse::Stream> {
fn current_device(&mut self) -> Result<&DeviceRef> {
if self.context.version_0_9_8 {
let mut dev: Box<ffi::cubeb_device> = Box::new(unsafe { mem::zeroed() });
if stream_params.prefs == cubeb::StreamPrefs::STREAM_PREF_LOOPBACK {
return Err(cubeb::ERROR_NOT_SUPPORTED);
if let Some(ref stm) = self.input_stream {
dev.input_name = match stm.get_device_name() {
Ok(name) => name.to_owned().into_raw(),
Err(_) => {
return Err(Error::error());
}
}
}
if let Some(ref stm) = self.output_stream {
dev.output_name = match stm.get_device_name() {
Ok(name) => name.to_owned().into_raw(),
Err(_) => {
return Err(Error::error());
}
}
}
Ok(unsafe { DeviceRef::from_ptr(Box::into_raw(dev) as *mut _) })
} else {
Err(not_supported())
}
}
fn device_destroy(&mut self, device: &DeviceRef) -> Result<()> {
if device.as_ptr().is_null() {
Err(Error::error())
} else {
unsafe {
let _: Box<Device> = Box::from_raw(device.as_ptr() as *mut _);
}
Ok(())
}
}
fn register_device_changed_callback(
&mut self,
_: ffi::cubeb_device_changed_callback,
) -> Result<()> {
Err(Error::error())
}
}
impl<'ctx> PulseStream<'ctx> {
fn stream_init(
context: &pulse::Context,
stream_params: &StreamParamsRef,
stream_name: Option<&CStr>,
) -> Result<pulse::Stream> {
if stream_params.prefs() == StreamPrefs::LOOPBACK {
return Err(not_supported());
}
fn to_pulse_format(format: cubeb::SampleFormat) -> pulse::SampleFormat {
fn to_pulse_format(format: SampleFormat) -> pulse::SampleFormat {
match format {
cubeb::SAMPLE_S16LE => pulse::SampleFormat::Signed16LE,
cubeb::SAMPLE_S16BE => pulse::SampleFormat::Signed16BE,
cubeb::SAMPLE_FLOAT32LE => pulse::SampleFormat::Float32LE,
cubeb::SAMPLE_FLOAT32BE => pulse::SampleFormat::Float32BE,
SampleFormat::S16LE => pulse::SampleFormat::Signed16LE,
SampleFormat::S16BE => pulse::SampleFormat::Signed16BE,
SampleFormat::Float32LE => pulse::SampleFormat::Float32LE,
SampleFormat::Float32BE => pulse::SampleFormat::Float32BE,
_ => pulse::SampleFormat::Invalid,
}
}
let fmt = to_pulse_format(stream_params.format);
let fmt = to_pulse_format(stream_params.format());
if fmt == pulse::SampleFormat::Invalid {
return Err(cubeb::ERROR_INVALID_FORMAT);
return Err(invalid_format());
}
let ss = pulse::SampleSpec {
channels: stream_params.channels as u8,
channels: stream_params.channels() as u8,
format: fmt.into(),
rate: stream_params.rate,
rate: stream_params.rate(),
};
let cm: Option<pa_channel_map> = match stream_params.layout {
cubeb::LAYOUT_UNDEFINED => None,
_ => Some(layout_to_channel_map(stream_params.layout)),
let cm: Option<pa_channel_map> = match stream_params.layout() {
ChannelLayout::Undefined => None,
_ => Some(layout_to_channel_map(stream_params.layout())),
};
let stream = pulse::Stream::new(context, stream_name, &ss, cm.as_ref());
let stream = pulse::Stream::new(context, stream_name.unwrap(), &ss, cm.as_ref());
match stream {
None => Err(cubeb::ERROR),
None => Err(Error::error()),
Some(stm) => Ok(stm),
}
}
pub fn cork_stream(&self, stream: Option<&pulse::Stream>, state: CorkState) {
if let Some(stm) = stream {
if let Ok(o) = stm.cork(state.is_cork() as i32,
stream_success,
self as *const _ as *mut _) {
if let Ok(o) = stm.cork(
state.is_cork() as i32,
stream_success,
self as *const _ as *mut _,
) {
self.context.operation_wait(stream, &o);
}
}
@ -613,10 +660,10 @@ impl<'ctx> Stream<'ctx> {
if state.is_notify() {
self.state_change_callback(if state.is_cork() {
cubeb::STATE_STOPPED
} else {
cubeb::STATE_STARTED
});
ffi::CUBEB_STATE_STOPPED
} else {
ffi::CUBEB_STATE_STARTED
});
}
}
@ -642,15 +689,22 @@ impl<'ctx> Stream<'ctx> {
r
}
pub fn state_change_callback(&mut self, s: cubeb::State) {
pub fn state_change_callback(&mut self, s: ffi::cubeb_state) {
self.state = s;
unsafe {
(self.state_callback.unwrap())(self as *mut Stream as *mut cubeb::Stream, self.user_ptr, s);
}
(self.state_callback.unwrap())(
self as *mut PulseStream as *mut ffi::cubeb_stream,
self.user_ptr,
s,
)
};
}
fn wait_until_ready(&self) -> bool {
fn wait_until_io_stream_ready(stm: &pulse::Stream, mainloop: &pulse::ThreadedMainloop) -> bool {
fn wait_until_io_stream_ready(
stm: &pulse::Stream,
mainloop: &pulse::ThreadedMainloop,
) -> bool {
if mainloop.is_null() {
return false;
}
@ -686,10 +740,15 @@ impl<'ctx> Stream<'ctx> {
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
fn trigger_user_callback(&mut self, input_data: *const c_void, nbytes: usize) {
fn drained_cb(a: &pulse::MainloopApi, e: *mut pa_time_event, _tv: &pulse::TimeVal, u: *mut c_void) {
let stm = unsafe { &mut *(u as *mut Stream) };
fn drained_cb(
a: &pulse::MainloopApi,
e: *mut pa_time_event,
_tv: &pulse::TimeVal,
u: *mut c_void,
) {
let stm = unsafe { &mut *(u as *mut PulseStream) };
debug_assert_eq!(stm.drain_timer, e);
stm.state_change_callback(cubeb::STATE_DRAINED);
stm.state_change_callback(ffi::CUBEB_STATE_DRAINED);
/* there's no pa_rttime_free, so use this instead. */
a.time_free(stm.drain_timer);
stm.drain_timer = ptr::null_mut();
@ -697,7 +756,6 @@ impl<'ctx> Stream<'ctx> {
}
if let Some(ref stm) = self.output_stream {
let frame_size = self.output_sample_spec.frame_size();
debug_assert_eq!(nbytes % frame_size, 0);
@ -707,21 +765,26 @@ impl<'ctx> Stream<'ctx> {
match stm.begin_write(towrite) {
Err(e) => {
panic!("Failed to write data: {}", e);
},
}
Ok((buffer, size)) => {
debug_assert!(size > 0);
debug_assert_eq!(size % frame_size, 0);
logv!("Trigger user callback with output buffer size={}, read_offset={}",
size,
read_offset);
let read_ptr = unsafe { (input_data as *const u8).offset(read_offset as isize) };
cubeb_logv!(
"Trigger user callback with output buffer size={}, read_offset={}",
size,
read_offset
);
let read_ptr =
unsafe { (input_data as *const u8).offset(read_offset as isize) };
let got = unsafe {
self.data_callback.unwrap()(self as *const _ as *mut _,
self.user_ptr,
read_ptr as *const _ as *mut _,
buffer,
(size / frame_size) as c_long)
self.data_callback.unwrap()(
self as *const _ as *mut _,
self.user_ptr,
read_ptr as *const _ as *mut _,
buffer,
(size / frame_size) as c_long,
)
};
if got < 0 {
let _ = stm.cancel_write();
@ -736,10 +799,12 @@ impl<'ctx> Stream<'ctx> {
}
if self.volume != PULSE_NO_GAIN {
let samples = (self.output_sample_spec.channels as usize * size / frame_size) as isize;
let samples = (self.output_sample_spec.channels as usize * size
/ frame_size) as isize;
if self.output_sample_spec.format == PA_SAMPLE_S16BE ||
self.output_sample_spec.format == PA_SAMPLE_S16LE {
if self.output_sample_spec.format == PA_SAMPLE_S16BE
|| self.output_sample_spec.format == PA_SAMPLE_S16LE
{
let b = buffer as *mut i16;
for i in 0..samples {
unsafe { *b.offset(i) *= self.volume as i16 };
@ -752,10 +817,12 @@ impl<'ctx> Stream<'ctx> {
}
}
let r = stm.write(buffer,
got as usize * frame_size,
0,
pulse::SeekMode::Relative);
let r = stm.write(
buffer,
got as usize * frame_size,
0,
pulse::SeekMode::Relative,
);
debug_assert!(r.is_ok());
if (got as usize) < size / frame_size {
@ -763,12 +830,15 @@ impl<'ctx> Stream<'ctx> {
Ok(StreamLatency::Positive(l)) => l,
Ok(_) => {
panic!("Can not handle negative latency values.");
},
}
Err(e) => {
debug_assert_eq!(e, pulse::ErrorCode::from_error_code(PA_ERR_NODATA));
debug_assert_eq!(
e,
pulse::ErrorCode::from_error_code(PA_ERR_NODATA)
);
/* this needs a better guess. */
100 * PA_USEC_PER_MSEC
},
}
};
/* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
@ -776,15 +846,18 @@ impl<'ctx> Stream<'ctx> {
debug_assert!(self.drain_timer.is_null());
let stream_ptr = self as *const _ as *mut _;
if let Some(ref context) = self.context.context {
self.drain_timer =
context.rttime_new(pulse::rtclock_now() + 2 * latency, drained_cb, stream_ptr);
self.drain_timer = context.rttime_new(
pulse::rtclock_now() + 2 * latency,
drained_cb,
stream_ptr,
);
}
self.shutdown = true;
return;
}
towrite -= size;
},
}
}
}
debug_assert_eq!(towrite, 0);
@ -793,13 +866,13 @@ impl<'ctx> Stream<'ctx> {
}
fn stream_success(_: &pulse::Stream, success: i32, u: *mut c_void) {
let stm = unsafe { &*(u as *mut Stream) };
let stm = unsafe { &*(u as *mut PulseStream) };
debug_assert_ne!(success, 0);
stm.context.mainloop.signal();
}
fn context_success(_: &pulse::Context, success: i32, u: *mut c_void) {
let ctx = unsafe { &*(u as *mut Context) };
let ctx = unsafe { &*(u as *mut PulseContext) };
debug_assert_ne!(success, 0);
ctx.mainloop.signal();
}
@ -815,12 +888,22 @@ fn set_buffering_attribute(latency_frames: u32, sample_spec: &pa_sample_spec) ->
fragsize: minreq,
};
log!("Requested buffer attributes maxlength {}, tlength {}, prebuf {}, minreq {}, fragsize {}",
battr.maxlength,
battr.tlength,
battr.prebuf,
battr.minreq,
battr.fragsize);
cubeb_log!(
"Requested buffer attributes maxlength {}, tlength {}, prebuf {}, minreq {}, fragsize {}",
battr.maxlength,
battr.tlength,
battr.prebuf,
battr.minreq,
battr.fragsize
);
battr
}
fn invalid_format() -> Error {
unsafe { Error::from_raw(ffi::CUBEB_ERROR_INVALID_FORMAT) }
}
fn not_supported() -> Error {
unsafe { Error::from_raw(ffi::CUBEB_ERROR_NOT_SUPPORTED) }
}

Просмотреть файл

@ -1,249 +1,17 @@
// Copyright © 2017 Mozilla Foundation
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use backend;
use cubeb;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
unsafe extern "C" fn capi_init(c: *mut *mut cubeb::Context, context_name: *const c_char) -> i32 {
match backend::Context::new(context_name) {
Ok(ctx) => {
*c = Box::into_raw(ctx) as *mut _;
cubeb::OK
},
Err(e) => e,
}
}
extern "C" fn capi_get_backend_id(_: *mut cubeb::Context) -> *const c_char {
"pulse-rust\0".as_ptr() as *const c_char
}
unsafe extern "C" fn capi_get_max_channel_count(c: *mut cubeb::Context, max_channels: *mut u32) -> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.max_channel_count() {
Ok(mc) => {
*max_channels = mc;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_get_min_latency(c: *mut cubeb::Context,
param: cubeb::StreamParams,
latency_frames: *mut u32)
-> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.min_latency(&param) {
Ok(l) => {
*latency_frames = l;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_get_preferred_sample_rate(c: *mut cubeb::Context, rate: *mut u32) -> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.preferred_sample_rate() {
Ok(r) => {
*rate = r;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_get_preferred_channel_layout(c: *mut cubeb::Context,
layout: *mut cubeb::ChannelLayout)
-> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.preferred_channel_layout() {
Ok(l) => {
*layout = l;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_enumerate_devices(c: *mut cubeb::Context,
devtype: cubeb::DeviceType,
collection: *mut cubeb::DeviceCollection)
-> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.enumerate_devices(devtype) {
Ok(dc) => {
*collection = dc;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_device_collection_destroy(c: *mut cubeb::Context,
collection: *mut cubeb::DeviceCollection)
-> i32 {
let ctx = &*(c as *mut backend::Context);
ctx.device_collection_destroy(collection);
cubeb::OK
}
unsafe extern "C" fn capi_destroy(c: *mut cubeb::Context) {
let _: Box<backend::Context> = Box::from_raw(c as *mut _);
}
unsafe extern "C" fn capi_stream_init(c: *mut cubeb::Context,
s: *mut *mut cubeb::Stream,
stream_name: *const c_char,
input_device: cubeb::DeviceId,
input_stream_params: *mut cubeb::StreamParams,
output_device: cubeb::DeviceId,
output_stream_params: *mut cubeb::StreamParams,
latency_frames: u32,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void)
-> i32 {
fn try_stream_params_from(sp: *mut cubeb::StreamParams) -> Option<cubeb::StreamParams> {
if sp.is_null() { None } else { Some(unsafe { *sp }) }
}
let ctx = &mut *(c as *mut backend::Context);
let stream_name = CStr::from_ptr(stream_name);
match ctx.new_stream(stream_name,
input_device,
try_stream_params_from(input_stream_params),
output_device,
try_stream_params_from(output_stream_params),
latency_frames,
data_callback,
state_callback,
user_ptr as _) {
Ok(stm) => {
*s = Box::into_raw(stm) as *mut _;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_destroy(s: *mut cubeb::Stream) {
let _ = Box::from_raw(s as *mut backend::Stream);
}
unsafe extern "C" fn capi_stream_start(s: *mut cubeb::Stream) -> i32 {
let stm = &mut *(s as *mut backend::Stream);
stm.start()
}
unsafe extern "C" fn capi_stream_stop(s: *mut cubeb::Stream) -> i32 {
let stm = &mut *(s as *mut backend::Stream);
stm.stop()
}
unsafe extern "C" fn capi_stream_get_position(s: *mut cubeb::Stream, position: *mut u64) -> i32 {
let stm = &*(s as *mut backend::Stream);
match stm.position() {
Ok(pos) => {
*position = pos;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_get_latency(s: *mut cubeb::Stream, latency: *mut u32) -> i32 {
let stm = &*(s as *mut backend::Stream);
match stm.latency() {
Ok(lat) => {
*latency = lat;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_set_volume(s: *mut cubeb::Stream, volume: f32) -> i32 {
let stm = &mut *(s as *mut backend::Stream);
stm.set_volume(volume)
}
unsafe extern "C" fn capi_stream_set_panning(s: *mut cubeb::Stream, panning: f32) -> i32 {
let stm = &mut *(s as *mut backend::Stream);
stm.set_panning(panning)
}
unsafe extern "C" fn capi_stream_get_current_device(s: *mut cubeb::Stream, device: *mut *const cubeb::Device) -> i32 {
let stm = &*(s as *mut backend::Stream);
match stm.current_device() {
Ok(d) => {
*device = Box::into_raw(d) as *mut _;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_device_destroy(_: *mut cubeb::Stream, device: *mut cubeb::Device) -> i32 {
let _: Box<backend::Device> = Box::from_raw(device as *mut backend::Device);
cubeb::OK
}
unsafe extern "C" fn capi_register_device_collection_changed(c: *mut cubeb::Context,
devtype: cubeb::DeviceType,
collection_changed_callback:
cubeb::DeviceCollectionChangedCallback,
user_ptr: *mut c_void) -> i32
{
let ctx = &mut *(c as *mut backend::Context);
ctx.register_device_collection_changed(devtype, collection_changed_callback, user_ptr as _)
}
pub const PULSE_OPS: cubeb::Ops = cubeb::Ops {
init: Some(capi_init),
get_backend_id: Some(capi_get_backend_id),
get_max_channel_count: Some(capi_get_max_channel_count),
get_min_latency: Some(capi_get_min_latency),
get_preferred_sample_rate: Some(capi_get_preferred_sample_rate),
get_preferred_channel_layout: Some(capi_get_preferred_channel_layout),
enumerate_devices: Some(capi_enumerate_devices),
device_collection_destroy: Some(capi_device_collection_destroy),
destroy: Some(capi_destroy),
stream_init: Some(capi_stream_init),
stream_destroy: Some(capi_stream_destroy),
stream_start: Some(capi_stream_start),
stream_stop: Some(capi_stream_stop),
stream_reset_default_device: None,
stream_get_position: Some(capi_stream_get_position),
stream_get_latency: Some(capi_stream_get_latency),
stream_set_volume: Some(capi_stream_set_volume),
stream_set_panning: Some(capi_stream_set_panning),
stream_get_current_device: Some(capi_stream_get_current_device),
stream_device_destroy: Some(capi_stream_device_destroy),
stream_register_device_changed_callback: None,
register_device_collection_changed: Some(capi_register_device_collection_changed),
};
use backend::PulseContext;
use cubeb_backend::{capi, ffi};
use std::os::raw::{c_char, c_int};
/// Entry point from C code.
#[no_mangle]
pub unsafe extern "C" fn pulse_rust_init(c: *mut *mut cubeb::Context, context_name: *const c_char) -> i32 {
capi_init(c, context_name)
pub unsafe extern "C" fn pulse_rust_init(
c: *mut *mut ffi::cubeb,
context_name: *const c_char,
) -> c_int {
capi::capi_init::<PulseContext>(c, context_name)
}

Просмотреть файл

@ -1,14 +1,14 @@
//! Cubeb backend interface to Pulse Audio
// Copyright © 2017 Mozilla Foundation
// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
#[macro_use]
extern crate cubeb_ffi as cubeb;
extern crate pulse_ffi;
extern crate cubeb_backend;
extern crate pulse;
extern crate pulse_ffi;
extern crate semver;
mod capi;