|
@@ -1,88 +1,110 @@
|
|
use super::{Open, Sink};
|
|
use super::{Open, Sink};
|
|
extern crate cpal;
|
|
extern crate cpal;
|
|
extern crate rodio;
|
|
extern crate rodio;
|
|
|
|
+use cpal::traits::{DeviceTrait, HostTrait};
|
|
use std::process::exit;
|
|
use std::process::exit;
|
|
use std::{io, thread, time};
|
|
use std::{io, thread, time};
|
|
|
|
|
|
pub struct RodioSink {
|
|
pub struct RodioSink {
|
|
rodio_sink: rodio::Sink,
|
|
rodio_sink: rodio::Sink,
|
|
|
|
+ // We have to keep hold of this object, or the Sink can't play...
|
|
|
|
+ #[allow(dead_code)]
|
|
|
|
+ stream: rodio::OutputStream,
|
|
}
|
|
}
|
|
|
|
|
|
fn list_formats(ref device: &rodio::Device) {
|
|
fn list_formats(ref device: &rodio::Device) {
|
|
- let default_fmt = match device.default_output_format() {
|
|
|
|
- Ok(fmt) => cpal::SupportedFormat::from(fmt),
|
|
|
|
|
|
+ let default_fmt = match device.default_output_config() {
|
|
|
|
+ Ok(fmt) => cpal::SupportedStreamConfig::from(fmt),
|
|
Err(e) => {
|
|
Err(e) => {
|
|
- warn!("Error getting default rodio::Sink format: {:?}", e);
|
|
|
|
|
|
+ warn!("Error getting default rodio::Sink config: {}", e);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
+ debug!(" Default config:");
|
|
|
|
+ debug!(" {:?}", default_fmt);
|
|
|
|
|
|
- let mut output_formats = match device.supported_output_formats() {
|
|
|
|
|
|
+ let mut output_configs = match device.supported_output_configs() {
|
|
Ok(f) => f.peekable(),
|
|
Ok(f) => f.peekable(),
|
|
Err(e) => {
|
|
Err(e) => {
|
|
- warn!("Error getting supported rodio::Sink formats: {:?}", e);
|
|
|
|
|
|
+ warn!("Error getting supported rodio::Sink configs: {}", e);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
- if output_formats.peek().is_some() {
|
|
|
|
- debug!(" Available formats:");
|
|
|
|
- for format in output_formats {
|
|
|
|
- let s = format!(
|
|
|
|
- "{}ch, {:?}, min {:?}, max {:?}",
|
|
|
|
- format.channels, format.data_type, format.min_sample_rate, format.max_sample_rate
|
|
|
|
- );
|
|
|
|
- if format == default_fmt {
|
|
|
|
- debug!(" (default) {}", s);
|
|
|
|
- } else {
|
|
|
|
- debug!(" {:?}", format);
|
|
|
|
- }
|
|
|
|
|
|
+ if output_configs.peek().is_some() {
|
|
|
|
+ debug!(" Available configs:");
|
|
|
|
+ for format in output_configs {
|
|
|
|
+ debug!(" {:?}", format);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn list_outputs() {
|
|
fn list_outputs() {
|
|
- let default_device = rodio::default_output_device().unwrap();
|
|
|
|
- println!("Default Audio Device:\n {}", default_device.name());
|
|
|
|
|
|
+ let default_device = get_default_device();
|
|
|
|
+ let default_device_name = default_device.name().expect("cannot get output name");
|
|
|
|
+ println!("Default Audio Device:\n {}", default_device_name);
|
|
list_formats(&default_device);
|
|
list_formats(&default_device);
|
|
|
|
|
|
println!("Other Available Audio Devices:");
|
|
println!("Other Available Audio Devices:");
|
|
- for device in rodio::output_devices() {
|
|
|
|
- if device.name() != default_device.name() {
|
|
|
|
- println!(" {}", device.name());
|
|
|
|
|
|
+ for device in cpal::default_host()
|
|
|
|
+ .output_devices()
|
|
|
|
+ .expect("cannot get list of output devices")
|
|
|
|
+ {
|
|
|
|
+ let device_name = device.name().expect("cannot get output name");
|
|
|
|
+ if device_name != default_device_name {
|
|
|
|
+ println!(" {}", device_name);
|
|
list_formats(&device);
|
|
list_formats(&device);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-impl Open for RodioSink {
|
|
|
|
- fn open(device: Option<String>) -> RodioSink {
|
|
|
|
- debug!("Using rodio sink");
|
|
|
|
-
|
|
|
|
- let mut rodio_device = rodio::default_output_device().expect("no output device available");
|
|
|
|
- if device.is_some() {
|
|
|
|
- let device_name = device.unwrap();
|
|
|
|
|
|
+fn get_default_device() -> rodio::Device {
|
|
|
|
+ cpal::default_host()
|
|
|
|
+ .default_output_device()
|
|
|
|
+ .expect("no default output device available")
|
|
|
|
+}
|
|
|
|
|
|
|
|
+fn match_device(device: Option<String>) -> rodio::Device {
|
|
|
|
+ match device {
|
|
|
|
+ Some(device_name) => {
|
|
if device_name == "?".to_string() {
|
|
if device_name == "?".to_string() {
|
|
list_outputs();
|
|
list_outputs();
|
|
exit(0)
|
|
exit(0)
|
|
}
|
|
}
|
|
- let mut found = false;
|
|
|
|
- for d in rodio::output_devices() {
|
|
|
|
- if d.name() == device_name {
|
|
|
|
- rodio_device = d;
|
|
|
|
- found = true;
|
|
|
|
- break;
|
|
|
|
|
|
+ for d in cpal::default_host()
|
|
|
|
+ .output_devices()
|
|
|
|
+ .expect("cannot get list of output devices")
|
|
|
|
+ {
|
|
|
|
+ if d.name().expect("cannot get output name") == device_name {
|
|
|
|
+ return d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if !found {
|
|
|
|
- println!("No output sink matching '{}' found.", device_name);
|
|
|
|
- exit(0)
|
|
|
|
- }
|
|
|
|
|
|
+ println!("No output sink matching '{}' found.", device_name);
|
|
|
|
+ exit(0)
|
|
}
|
|
}
|
|
- let sink = rodio::Sink::new(&rodio_device);
|
|
|
|
|
|
+ None => return get_default_device(),
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl Open for RodioSink {
|
|
|
|
+ fn open(device: Option<String>) -> RodioSink {
|
|
|
|
+ debug!(
|
|
|
|
+ "Using rodio sink with cpal host: {:?}",
|
|
|
|
+ cpal::default_host().id()
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ let rodio_device = match_device(device);
|
|
|
|
+ debug!("Using cpal device");
|
|
|
|
+ let stream = rodio::OutputStream::try_from_device(&rodio_device)
|
|
|
|
+ .expect("Couldn't open output stream.");
|
|
|
|
+ debug!("Using rodio stream");
|
|
|
|
+ let sink = rodio::Sink::try_new(&stream.1).expect("Couldn't create output sink.");
|
|
|
|
+ debug!("Using rodio sink");
|
|
|
|
|
|
- RodioSink { rodio_sink: sink }
|
|
|
|
|
|
+ RodioSink {
|
|
|
|
+ rodio_sink: sink,
|
|
|
|
+ stream: stream.0,
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|