Remove EventLoop and port the ALSA backend

This commit is contained in:
Tatsuyuki Ishi 2019-07-09 15:47:33 +09:00 committed by mitchmindtree
parent 700bef79d9
commit c97d1dd3fa
8 changed files with 519 additions and 951 deletions

View File

@ -1,37 +1,34 @@
extern crate anyhow; extern crate anyhow;
extern crate cpal; extern crate cpal;
use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait}; use cpal::traits::{DeviceTrait, StreamTrait, HostTrait};
fn main() -> Result<(), anyhow::Error> { fn main() -> Result<(), anyhow::Error> {
let host = cpal::default_host(); let host = cpal::default_host();
let device = host.default_output_device().expect("failed to find a default output device"); let device = host.default_output_device().expect("failed to find a default output device");
let format = device.default_output_format()?; let format = device.default_output_format()?;
let event_loop = host.event_loop();
let stream_id = event_loop.build_output_stream(&device, &format)?;
event_loop.play_stream(stream_id.clone())?;
let sample_rate = format.sample_rate.0 as f32; let sample_rate = format.sample_rate.0 as f32;
let channels = format.channels;
let mut sample_clock = 0f32; let mut sample_clock = 0f32;
// Produce a sinusoid of maximum amplitude. // Produce a sinusoid of maximum amplitude.
let mut next_value = || { let mut next_value = move || {
sample_clock = (sample_clock + 1.0) % sample_rate; sample_clock = (sample_clock + 1.0) % sample_rate;
(sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin() (sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin()
}; };
event_loop.run(move |id, result| { let stream = device.build_output_stream(&format, move |result| {
let data = match result { let data = match result {
Ok(data) => data, Ok(data) => data,
Err(err) => { Err(err) => {
eprintln!("an error occurred on stream {:?}: {}", id, err); eprintln!("an error occurred on stream: {}", err);
return; return;
} }
}; };
match data { match data {
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::U16(mut buffer) } => { cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::U16(mut buffer) } => {
for sample in buffer.chunks_mut(format.channels as usize) { for sample in buffer.chunks_mut(channels as usize) {
let value = ((next_value() * 0.5 + 0.5) * std::u16::MAX as f32) as u16; let value = ((next_value() * 0.5 + 0.5) * std::u16::MAX as f32) as u16;
for out in sample.iter_mut() { for out in sample.iter_mut() {
*out = value; *out = value;
@ -39,7 +36,7 @@ fn main() -> Result<(), anyhow::Error> {
} }
}, },
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => { cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => {
for sample in buffer.chunks_mut(format.channels as usize) { for sample in buffer.chunks_mut(channels as usize) {
let value = (next_value() * std::i16::MAX as f32) as i16; let value = (next_value() * std::i16::MAX as f32) as i16;
for out in sample.iter_mut() { for out in sample.iter_mut() {
*out = value; *out = value;
@ -47,7 +44,7 @@ fn main() -> Result<(), anyhow::Error> {
} }
}, },
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => { cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => {
for sample in buffer.chunks_mut(format.channels as usize) { for sample in buffer.chunks_mut(channels as usize) {
let value = next_value(); let value = next_value();
for out in sample.iter_mut() { for out in sample.iter_mut() {
*out = value; *out = value;
@ -56,5 +53,10 @@ fn main() -> Result<(), anyhow::Error> {
}, },
_ => (), _ => (),
} }
}); })?;
stream.play()?;
std::thread::sleep(std::time::Duration::from_millis(1000));
Ok(())
} }

View File

@ -10,18 +10,21 @@ extern crate anyhow;
extern crate cpal; extern crate cpal;
extern crate ringbuf; extern crate ringbuf;
use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use ringbuf::RingBuffer; use ringbuf::RingBuffer;
const LATENCY_MS: f32 = 150.0; const LATENCY_MS: f32 = 150.0;
fn main() -> Result<(), anyhow::Error> { fn main() -> Result<(), anyhow::Error> {
let host = cpal::default_host(); let host = cpal::default_host();
let event_loop = host.event_loop();
// Default devices. // Default devices.
let input_device = host.default_input_device().expect("failed to get default input device"); let input_device = host
let output_device = host.default_output_device().expect("failed to get default output device"); .default_input_device()
.expect("failed to get default input device");
let output_device = host
.default_output_device()
.expect("failed to get default output device");
println!("Using default input device: \"{}\"", input_device.name()?); println!("Using default input device: \"{}\"", input_device.name()?);
println!("Using default output device: \"{}\"", output_device.name()?); println!("Using default output device: \"{}\"", output_device.name()?);
@ -29,12 +32,6 @@ fn main() -> Result<(), anyhow::Error> {
let mut format = input_device.default_input_format()?; let mut format = input_device.default_input_format()?;
format.data_type = cpal::SampleFormat::F32; format.data_type = cpal::SampleFormat::F32;
// Build streams.
println!("Attempting to build both streams with `{:?}`.", format);
let input_stream_id = event_loop.build_input_stream(&input_device, &format)?;
let output_stream_id = event_loop.build_output_stream(&output_device, &format)?;
println!("Successfully built streams.");
// Create a delay in case the input and output devices aren't synced. // Create a delay in case the input and output devices aren't synced.
let latency_frames = (LATENCY_MS / 1_000.0) * format.sample_rate.0 as f32; let latency_frames = (LATENCY_MS / 1_000.0) * format.sample_rate.0 as f32;
let latency_samples = latency_frames as usize * format.channels as usize; let latency_samples = latency_frames as usize * format.channels as usize;
@ -50,59 +47,78 @@ fn main() -> Result<(), anyhow::Error> {
producer.push(0.0).unwrap(); producer.push(0.0).unwrap();
} }
// Play the streams. // Build streams.
println!("Starting the input and output streams with `{}` milliseconds of latency.", LATENCY_MS); println!("Attempting to build both streams with `{:?}`.", format);
event_loop.play_stream(input_stream_id.clone())?; let input_stream = input_device.build_input_stream(&format, move |result| {
event_loop.play_stream(output_stream_id.clone())?; let data = match result {
Ok(data) => data,
Err(err) => {
eprintln!("an error occurred on input stream: {}", err);
return;
},
};
// Run the event loop on a separate thread. match data {
std::thread::spawn(move || { cpal::StreamData::Input {
event_loop.run(move |id, result| { buffer: cpal::UnknownTypeInputBuffer::F32(buffer),
let data = match result { } => {
Ok(data) => data, let mut output_fell_behind = false;
Err(err) => { for &sample in buffer.iter() {
eprintln!("an error occurred on stream {:?}: {}", id, err); if producer.push(sample).is_err() {
return; output_fell_behind = true;
}
} }
}; if output_fell_behind {
eprintln!("output stream fell behind: try increasing latency");
}
},
_ => panic!("Expected input with f32 data"),
}
})?;
let output_stream = output_device.build_output_stream(&format, move |result| {
let data = match result {
Ok(data) => data,
Err(err) => {
eprintln!("an error occurred on output stream: {}", err);
return;
},
};
match data {
cpal::StreamData::Output {
buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer),
} => {
let mut input_fell_behind = None;
for sample in buffer.iter_mut() {
*sample = match consumer.pop() {
Ok(s) => s,
Err(err) => {
input_fell_behind = Some(err);
0.0
},
};
}
if let Some(err) = input_fell_behind {
eprintln!("input stream fell behind: {:?}: try increasing latency", err);
}
},
_ => panic!("Expected output with f32 data"),
}
})?;
println!("Successfully built streams.");
match data { // Play the streams.
cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::F32(buffer) } => { println!(
assert_eq!(id, input_stream_id); "Starting the input and output streams with `{}` milliseconds of latency.",
let mut output_fell_behind = false; LATENCY_MS
for &sample in buffer.iter() { );
if producer.push(sample).is_err() { input_stream.play()?;
output_fell_behind = true; output_stream.play()?;
}
}
if output_fell_behind {
eprintln!("output stream fell behind: try increasing latency");
}
},
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => {
assert_eq!(id, output_stream_id);
let mut input_fell_behind = None;
for sample in buffer.iter_mut() {
*sample = match consumer.pop() {
Ok(s) => s,
Err(err) => {
input_fell_behind = Some(err);
0.0
},
};
}
if let Some(_) = input_fell_behind {
eprintln!("input stream fell behind: try increasing latency");
}
},
_ => panic!("we're expecting f32 data"),
}
});
});
// Run for 3 seconds before closing. // Run for 3 seconds before closing.
println!("Playing for 3 seconds... "); println!("Playing for 3 seconds... ");
std::thread::sleep(std::time::Duration::from_secs(3)); std::thread::sleep(std::time::Duration::from_secs(3));
drop(input_stream);
drop(output_stream);
println!("Done!"); println!("Done!");
Ok(()) Ok(())
} }

View File

@ -6,21 +6,21 @@ extern crate anyhow;
extern crate cpal; extern crate cpal;
extern crate hound; extern crate hound;
use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
fn main() -> Result<(), anyhow::Error> { fn main() -> Result<(), anyhow::Error> {
// Use the default host for working with audio devices. // Use the default host for working with audio devices.
let host = cpal::default_host(); let host = cpal::default_host();
// Setup the default input device and stream with the default input format. // Setup the default input device and stream with the default input format.
let device = host.default_input_device().expect("Failed to get default input device"); let device = host
.default_input_device()
.expect("Failed to get default input device");
println!("Default input device: {}", device.name()?); println!("Default input device: {}", device.name()?);
let format = device.default_input_format().expect("Failed to get default input format"); let format = device
.default_input_format()
.expect("Failed to get default input format");
println!("Default input format: {:?}", format); println!("Default input format: {:?}", format);
let event_loop = host.event_loop();
let stream_id = event_loop.build_input_stream(&device, &format)?;
event_loop.play_stream(stream_id)?;
// The WAV file we're recording to. // The WAV file we're recording to.
const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav"); const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav");
let spec = wav_spec_from_format(&format); let spec = wav_spec_from_format(&format);
@ -29,63 +29,62 @@ fn main() -> Result<(), anyhow::Error> {
// A flag to indicate that recording is in progress. // A flag to indicate that recording is in progress.
println!("Begin recording..."); println!("Begin recording...");
let recording = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
// Run the input stream on a separate thread. // Run the input stream on a separate thread.
let writer_2 = writer.clone(); let writer_2 = writer.clone();
let recording_2 = recording.clone(); let stream = device.build_input_stream(&format, move |event| {
std::thread::spawn(move || { let data = match event {
event_loop.run(move |id, event| { Ok(data) => data,
let data = match event { Err(err) => {
Ok(data) => data, eprintln!("an error occurred on stream: {}", err);
Err(err) => {
eprintln!("an error occurred on stream {:?}: {}", id, err);
return;
}
};
// If we're done recording, return early.
if !recording_2.load(std::sync::atomic::Ordering::Relaxed) {
return; return;
} },
// Otherwise write to the wav writer. };
match data {
cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::U16(buffer) } => { // Otherwise write to the wav writer.
if let Ok(mut guard) = writer_2.try_lock() { match data {
if let Some(writer) = guard.as_mut() { cpal::StreamData::Input {
for sample in buffer.iter() { buffer: cpal::UnknownTypeInputBuffer::U16(buffer),
let sample = cpal::Sample::to_i16(sample); } => {
writer.write_sample(sample).ok(); if let Ok(mut guard) = writer_2.try_lock() {
} if let Some(writer) = guard.as_mut() {
for sample in buffer.iter() {
let sample = cpal::Sample::to_i16(sample);
writer.write_sample(sample).ok();
} }
} }
}, }
cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::I16(buffer) } => { },
if let Ok(mut guard) = writer_2.try_lock() { cpal::StreamData::Input {
if let Some(writer) = guard.as_mut() { buffer: cpal::UnknownTypeInputBuffer::I16(buffer),
for &sample in buffer.iter() { } => {
writer.write_sample(sample).ok(); if let Ok(mut guard) = writer_2.try_lock() {
} if let Some(writer) = guard.as_mut() {
for &sample in buffer.iter() {
writer.write_sample(sample).ok();
} }
} }
}, }
cpal::StreamData::Input { buffer: cpal::UnknownTypeInputBuffer::F32(buffer) } => { },
if let Ok(mut guard) = writer_2.try_lock() { cpal::StreamData::Input {
if let Some(writer) = guard.as_mut() { buffer: cpal::UnknownTypeInputBuffer::F32(buffer),
for &sample in buffer.iter() { } => {
writer.write_sample(sample).ok(); if let Ok(mut guard) = writer_2.try_lock() {
} if let Some(writer) = guard.as_mut() {
for &sample in buffer.iter() {
writer.write_sample(sample).ok();
} }
} }
}, }
_ => (), },
} _ => (),
}); }
}); })?;
stream.play()?;
// Let recording go for roughly three seconds. // Let recording go for roughly three seconds.
std::thread::sleep(std::time::Duration::from_secs(3)); std::thread::sleep(std::time::Duration::from_secs(3));
recording.store(false, std::sync::atomic::Ordering::Relaxed); drop(stream);
writer.lock().unwrap().take().unwrap().finalize()?; writer.lock().unwrap().take().unwrap().finalize()?;
println!("Recording {} complete!", PATH); println!("Recording {} complete!", PATH);
Ok(()) Ok(())

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ use PlayStreamError;
use StreamDataResult; use StreamDataResult;
use SupportedFormatsError; use SupportedFormatsError;
use SupportedFormat; use SupportedFormat;
use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait}; use traits::{DeviceTrait, HostTrait, StreamTrait};
#[derive(Default)] #[derive(Default)]
pub struct Devices; pub struct Devices;
@ -23,7 +23,7 @@ pub struct EventLoop;
pub struct Host; pub struct Host;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId; pub struct Stream;
pub struct SupportedInputFormats; pub struct SupportedInputFormats;
pub struct SupportedOutputFormats; pub struct SupportedOutputFormats;
@ -49,6 +49,7 @@ impl EventLoop {
impl DeviceTrait for Device { impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats; type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats; type SupportedOutputFormats = SupportedOutputFormats;
type Stream = Stream;
#[inline] #[inline]
fn name(&self) -> Result<String, DeviceNameError> { fn name(&self) -> Result<String, DeviceNameError> {
@ -74,49 +75,22 @@ impl DeviceTrait for Device {
fn default_output_format(&self) -> Result<Format, DefaultFormatError> { fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
unimplemented!() unimplemented!()
} }
}
impl EventLoopTrait for EventLoop { fn build_input_stream<F>(&self, format: &Format, callback: F) -> Result<Self::Stream, BuildStreamError>
type Device = Device; where F: FnMut(StreamDataResult) + Send + 'static {
type StreamId = StreamId;
#[inline]
fn run<F>(&self, _callback: F) -> !
where F: FnMut(StreamId, StreamDataResult)
{
loop { /* TODO: don't spin */ }
}
#[inline]
fn build_input_stream(&self, _: &Device, _: &Format) -> Result<StreamId, BuildStreamError> {
Err(BuildStreamError::DeviceNotAvailable)
}
#[inline]
fn build_output_stream(&self, _: &Device, _: &Format) -> Result<StreamId, BuildStreamError> {
Err(BuildStreamError::DeviceNotAvailable)
}
#[inline]
fn destroy_stream(&self, _: StreamId) {
unimplemented!() unimplemented!()
} }
#[inline] /// Create an output stream.
fn play_stream(&self, _: StreamId) -> Result<(), PlayStreamError> { fn build_output_stream<F>(&self, format: &Format, callback: F) -> Result<Self::Stream, BuildStreamError>
panic!() where F: FnMut(StreamDataResult) + Send + 'static{
} unimplemented!()
#[inline]
fn pause_stream(&self, _: StreamId) -> Result<(), PauseStreamError> {
panic!()
} }
} }
impl HostTrait for Host { impl HostTrait for Host {
type Device = Device; type Device = Device;
type Devices = Devices; type Devices = Devices;
type EventLoop = EventLoop;
fn is_available() -> bool { fn is_available() -> bool {
false false
@ -133,13 +107,17 @@ impl HostTrait for Host {
fn default_output_device(&self) -> Option<Device> { fn default_output_device(&self) -> Option<Device> {
None None
} }
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
} }
impl StreamIdTrait for StreamId {} impl StreamTrait for Stream {
fn play(&self) -> Result<(), PlayStreamError> {
unimplemented!()
}
fn pause(&self) -> Result<(), PauseStreamError> {
unimplemented!()
}
}
impl Iterator for Devices { impl Iterator for Devices {
type Item = Device; type Item = Device;

View File

@ -151,11 +151,10 @@ extern crate thiserror;
pub use error::*; pub use error::*;
pub use platform::{ pub use platform::{
ALL_HOSTS, Device, Devices, EventLoop, Host, HostId, SupportedInputFormats, ALL_HOSTS, available_hosts, default_host, Device, Devices, Host, host_from_id,
SupportedOutputFormats, StreamId, available_hosts, default_host, host_from_id, HostId, Stream, SupportedInputFormats, SupportedOutputFormats,
}; };
pub use samples_formats::{Sample, SampleFormat}; pub use samples_formats::{Sample, SampleFormat};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
mod error; mod error;

View File

@ -58,14 +58,9 @@ macro_rules! impl_platform_host {
/// type. /// type.
pub struct Devices(DevicesInner); pub struct Devices(DevicesInner);
/// The **EventLoop** implementation associated with the platform's dynamically dispatched /// The **Stream** implementation associated with the platform's dynamically dispatched
/// **Host** type. /// **Host** type.
pub struct EventLoop(EventLoopInner); pub struct Stream(StreamInner);
/// The **StreamId** implementation associated with the platform's dynamically dispatched
/// **Host** type.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct StreamId(StreamIdInner);
/// The **SupportedInputFormats** iterator associated with the platform's dynamically /// The **SupportedInputFormats** iterator associated with the platform's dynamically
/// dispatched **Host** type. /// dispatched **Host** type.
@ -95,22 +90,15 @@ macro_rules! impl_platform_host {
)* )*
} }
enum EventLoopInner {
$(
$HostVariant(crate::host::$host_mod::EventLoop),
)*
}
enum HostInner { enum HostInner {
$( $(
$HostVariant(crate::host::$host_mod::Host), $HostVariant(crate::host::$host_mod::Host),
)* )*
} }
#[derive(Clone, Debug, Eq, Hash, PartialEq)] enum StreamInner {
enum StreamIdInner {
$( $(
$HostVariant(crate::host::$host_mod::StreamId), $HostVariant(crate::host::$host_mod::Stream),
)* )*
} }
@ -212,6 +200,7 @@ macro_rules! impl_platform_host {
impl crate::traits::DeviceTrait for Device { impl crate::traits::DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats; type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats; type SupportedOutputFormats = SupportedOutputFormats;
type Stream = Stream;
fn name(&self) -> Result<String, crate::DeviceNameError> { fn name(&self) -> Result<String, crate::DeviceNameError> {
match self.0 { match self.0 {
@ -260,96 +249,25 @@ macro_rules! impl_platform_host {
)* )*
} }
} }
}
impl crate::traits::EventLoopTrait for EventLoop { fn build_input_stream<F>(&self, format: &crate::Format, callback: F) -> Result<Self::Stream, crate::BuildStreamError>
type StreamId = StreamId; where F: FnMut(crate::StreamDataResult) + Send + 'static {
type Device = Device;
#[allow(unreachable_patterns)]
fn build_input_stream(
&self,
device: &Self::Device,
format: &crate::Format,
) -> Result<Self::StreamId, crate::BuildStreamError> {
match (&self.0, &device.0) {
$(
(&EventLoopInner::$HostVariant(ref e), &DeviceInner::$HostVariant(ref d)) => {
e.build_input_stream(d, format)
.map(StreamIdInner::$HostVariant)
.map(StreamId)
}
)*
_ => panic!("tried to build a stream with a device from another host"),
}
}
#[allow(unreachable_patterns)]
fn build_output_stream(
&self,
device: &Self::Device,
format: &crate::Format,
) -> Result<Self::StreamId, crate::BuildStreamError> {
match (&self.0, &device.0) {
$(
(&EventLoopInner::$HostVariant(ref e), &DeviceInner::$HostVariant(ref d)) => {
e.build_output_stream(d, format)
.map(StreamIdInner::$HostVariant)
.map(StreamId)
}
)*
_ => panic!("tried to build a stream with a device from another host"),
}
}
#[allow(unreachable_patterns)]
fn play_stream(&self, stream: Self::StreamId) -> Result<(), crate::PlayStreamError> {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => {
e.play_stream(s.clone())
}
)*
_ => panic!("tried to play a stream with an ID associated with another host"),
}
}
#[allow(unreachable_patterns)]
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), crate::PauseStreamError> {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => {
e.pause_stream(s.clone())
}
)*
_ => panic!("tried to pause a stream with an ID associated with another host"),
}
}
#[allow(unreachable_patterns)]
fn destroy_stream(&self, stream: Self::StreamId) {
match (&self.0, stream.0) {
$(
(&EventLoopInner::$HostVariant(ref e), StreamIdInner::$HostVariant(ref s)) => {
e.destroy_stream(s.clone())
}
)*
_ => panic!("tried to destroy a stream with an ID associated with another host"),
}
}
fn run<F>(&self, mut callback: F) -> !
where
F: FnMut(Self::StreamId, crate::StreamDataResult) + Send
{
match self.0 { match self.0 {
$( $(
EventLoopInner::$HostVariant(ref e) => { DeviceInner::$HostVariant(ref d) => d.build_input_stream(format, callback)
e.run(|id, result| { .map(StreamInner::$HostVariant)
let result = result; .map(Stream),
callback(StreamId(StreamIdInner::$HostVariant(id)), result); )*
}); }
}, }
fn build_output_stream<F>(&self, format: &crate::Format, callback: F) -> Result<Self::Stream, crate::BuildStreamError>
where F: FnMut(crate::StreamDataResult) + Send + 'static {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.build_output_stream(format, callback)
.map(StreamInner::$HostVariant)
.map(Stream),
)* )*
} }
} }
@ -358,7 +276,6 @@ macro_rules! impl_platform_host {
impl crate::traits::HostTrait for Host { impl crate::traits::HostTrait for Host {
type Devices = Devices; type Devices = Devices;
type Device = Device; type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool { fn is_available() -> bool {
$( crate::host::$host_mod::Host::is_available() ||)* false $( crate::host::$host_mod::Host::is_available() ||)* false
@ -393,20 +310,30 @@ macro_rules! impl_platform_host {
)* )*
} }
} }
}
fn event_loop(&self) -> Self::EventLoop { impl crate::traits::StreamTrait for Stream {
fn play(&self) -> Result<(), crate::PlayStreamError> {
match self.0 { match self.0 {
$( $(
HostInner::$HostVariant(ref h) => { StreamInner::$HostVariant(ref s) => {
EventLoop(EventLoopInner::$HostVariant(h.event_loop())) s.play()
}
)*
}
}
fn pause(&self) -> Result<(), crate::PauseStreamError> {
match self.0 {
$(
StreamInner::$HostVariant(ref s) => {
s.pause()
} }
)* )*
} }
} }
} }
impl crate::traits::StreamIdTrait for StreamId {}
$( $(
impl From<crate::host::$host_mod::Device> for Device { impl From<crate::host::$host_mod::Device> for Device {
fn from(h: crate::host::$host_mod::Device) -> Self { fn from(h: crate::host::$host_mod::Device) -> Self {
@ -420,21 +347,15 @@ macro_rules! impl_platform_host {
} }
} }
impl From<crate::host::$host_mod::EventLoop> for EventLoop {
fn from(h: crate::host::$host_mod::EventLoop) -> Self {
EventLoop(EventLoopInner::$HostVariant(h))
}
}
impl From<crate::host::$host_mod::Host> for Host { impl From<crate::host::$host_mod::Host> for Host {
fn from(h: crate::host::$host_mod::Host) -> Self { fn from(h: crate::host::$host_mod::Host) -> Self {
Host(HostInner::$HostVariant(h)) Host(HostInner::$HostVariant(h))
} }
} }
impl From<crate::host::$host_mod::StreamId> for StreamId { impl From<crate::host::$host_mod::Stream> for Stream {
fn from(h: crate::host::$host_mod::StreamId) -> Self { fn from(h: crate::host::$host_mod::Stream) -> Self {
StreamId(StreamIdInner::$HostVariant(h)) Stream(StreamInner::$HostVariant(h))
} }
} }
)* )*
@ -471,9 +392,8 @@ mod platform_impl {
pub use crate::host::alsa::{ pub use crate::host::alsa::{
Device as AlsaDevice, Device as AlsaDevice,
Devices as AlsaDevices, Devices as AlsaDevices,
EventLoop as AlsaEventLoop,
Host as AlsaHost, Host as AlsaHost,
StreamId as AlsaStreamId, Stream as AlsaStream,
SupportedInputFormats as AlsaSupportedInputFormats, SupportedInputFormats as AlsaSupportedInputFormats,
SupportedOutputFormats as AlsaSupportedOutputFormats, SupportedOutputFormats as AlsaSupportedOutputFormats,
}; };

View File

@ -39,8 +39,6 @@ pub trait HostTrait {
type Devices: Iterator<Item = Self::Device>; type Devices: Iterator<Item = Self::Device>;
/// The `Device` type yielded by the host. /// The `Device` type yielded by the host.
type Device: DeviceTrait; type Device: DeviceTrait;
/// The event loop type used by the `Host`
type EventLoop: EventLoopTrait<Device = Self::Device>;
/// Whether or not the host is available on the system. /// Whether or not the host is available on the system.
fn is_available() -> bool; fn is_available() -> bool;
@ -60,9 +58,6 @@ pub trait HostTrait {
/// Returns `None` if no output device is available. /// Returns `None` if no output device is available.
fn default_output_device(&self) -> Option<Self::Device>; fn default_output_device(&self) -> Option<Self::Device>;
/// Initialise the event loop, ready for managing audio streams.
fn event_loop(&self) -> Self::EventLoop;
/// An iterator yielding all `Device`s currently available to the system that support one or more /// An iterator yielding all `Device`s currently available to the system that support one or more
/// input stream formats. /// input stream formats.
/// ///
@ -99,6 +94,8 @@ pub trait DeviceTrait {
type SupportedInputFormats: Iterator<Item = SupportedFormat>; type SupportedInputFormats: Iterator<Item = SupportedFormat>;
/// The iterator type yielding supported output stream formats. /// The iterator type yielding supported output stream formats.
type SupportedOutputFormats: Iterator<Item = SupportedFormat>; type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
/// The stream type created by `build_input_stream` and `build_output_stream`.
type Stream: StreamTrait;
/// The human-readable name of the device. /// The human-readable name of the device.
fn name(&self) -> Result<String, DeviceNameError>; fn name(&self) -> Result<String, DeviceNameError>;
@ -118,81 +115,19 @@ pub trait DeviceTrait {
/// The default output stream format for the device. /// The default output stream format for the device.
fn default_output_format(&self) -> Result<Format, DefaultFormatError>; fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
/// Create an input stream.
fn build_input_stream<F>(&self, format: &Format, callback: F) -> Result<Self::Stream, BuildStreamError>
where F: FnMut(StreamDataResult) + Send + 'static;
/// Create an output stream.
fn build_output_stream<F>(&self, format: &Format, callback: F) -> Result<Self::Stream, BuildStreamError>
where F: FnMut(StreamDataResult) + Send + 'static;
} }
/// Collection of streams managed together. /// A stream created from `Device`, with methods to control playback.
/// pub trait StreamTrait {
/// Created with the `Host::event_loop` method. fn play(&self) -> Result<(), PlayStreamError>;
pub trait EventLoopTrait {
/// The `Device` type yielded by the host.
type Device: DeviceTrait;
/// The type used to uniquely distinguish between streams.
type StreamId: StreamIdTrait;
/// Creates a new input stream that will run from the given device and with the given format. fn pause(&self) -> Result<(), PauseStreamError>;
/// }
/// On success, returns an identifier for the stream.
///
/// Can return an error if the device is no longer valid, or if the input stream format is not
/// supported by the device.
fn build_input_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError>;
/// Creates a new output stream that will play on the given device and with the given format.
///
/// On success, returns an identifier for the stream.
///
/// Can return an error if the device is no longer valid, or if the output stream format is not
/// supported by the device.
fn build_output_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError>;
/// Instructs the audio device that it should start playing the stream with the given ID.
///
/// Has no effect is the stream was already playing.
///
/// Only call this after you have submitted some data, otherwise you may hear some glitches.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError>;
/// Instructs the audio device that it should stop playing the stream with the given ID.
///
/// Has no effect is the stream was already paused.
///
/// If you call `play` afterwards, the playback will resume where it was.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError>;
/// Destroys an existing stream.
///
/// # Panic
///
/// If the stream does not exist, this function can either panic or be a no-op.
fn destroy_stream(&self, stream: Self::StreamId);
/// Takes control of the current thread and begins the stream processing.
///
/// > **Note**: Since it takes control of the thread, this method is best called on a separate
/// > thread.
///
/// Whenever a stream needs to be fed some data, the closure passed as parameter is called.
/// You can call the other methods of `EventLoop` without getting a deadlock.
fn run<F>(&self, callback: F) -> !
where
F: FnMut(Self::StreamId, StreamDataResult) + Send;
}
/// The set of required bounds for host `StreamId` types.
pub trait StreamIdTrait: Clone + std::fmt::Debug + std::hash::Hash + PartialEq + Eq {}