Remove EventLoop and port the ALSA backend
This commit is contained in:
parent
700bef79d9
commit
c97d1dd3fa
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
Loading…
Reference in New Issue