Implement `Host` API for emscripten backend
This commit is contained in:
parent
69cd058d28
commit
6b6830ab57
|
@ -10,17 +10,125 @@ use stdweb::web::set_timeout;
|
||||||
|
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
|
use Device as DeviceTrait;
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
use DevicesError;
|
use DevicesError;
|
||||||
|
use EventLoop as EventLoopTrait;
|
||||||
use Format;
|
use Format;
|
||||||
|
use Host as HostTrait;
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use SupportedFormatsError;
|
use SupportedFormatsError;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
use StreamDataResult;
|
use StreamDataResult;
|
||||||
|
use StreamId as StreamIdTrait;
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
use UnknownTypeOutputBuffer;
|
use UnknownTypeOutputBuffer;
|
||||||
|
|
||||||
|
/// The default emscripten host type.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Host;
|
||||||
|
|
||||||
|
impl Host {
|
||||||
|
pub fn new() -> Result<Self, crate::HostUnavailable> {
|
||||||
|
Ok(Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HostTrait for Host {
|
||||||
|
type Devices = Devices;
|
||||||
|
type Device = Device;
|
||||||
|
type EventLoop = EventLoop;
|
||||||
|
|
||||||
|
fn is_available() -> bool {
|
||||||
|
// Assume ALSA is always available on linux/freebsd.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn devices(&self) -> Result<Self::Devices, DevicesError> {
|
||||||
|
Devices::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_input_device(&self) -> Option<Self::Device> {
|
||||||
|
default_input_device()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_output_device(&self) -> Option<Self::Device> {
|
||||||
|
default_output_device()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_loop(&self) -> Self::EventLoop {
|
||||||
|
EventLoop::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceTrait for Device {
|
||||||
|
type SupportedInputFormats = SupportedInputFormats;
|
||||||
|
type SupportedOutputFormats = SupportedOutputFormats;
|
||||||
|
|
||||||
|
fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
|
Device::name(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||||
|
Device::supported_input_formats(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||||
|
Device::supported_output_formats(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||||
|
Device::default_input_format(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||||
|
Device::default_output_format(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventLoopTrait for EventLoop {
|
||||||
|
type Device = Device;
|
||||||
|
type StreamId = StreamId;
|
||||||
|
|
||||||
|
fn build_input_stream(
|
||||||
|
&self,
|
||||||
|
device: &Self::Device,
|
||||||
|
format: &Format,
|
||||||
|
) -> Result<Self::StreamId, BuildStreamError> {
|
||||||
|
EventLoop::build_input_stream(self, device, format)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_output_stream(
|
||||||
|
&self,
|
||||||
|
device: &Self::Device,
|
||||||
|
format: &Format,
|
||||||
|
) -> Result<Self::StreamId, BuildStreamError> {
|
||||||
|
EventLoop::build_output_stream(self, device, format)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
|
||||||
|
EventLoop::play_stream(self, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
|
||||||
|
EventLoop::pause_stream(self, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy_stream(&self, stream: Self::StreamId) {
|
||||||
|
EventLoop::destroy_stream(self, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<F>(&self, callback: F) -> !
|
||||||
|
where
|
||||||
|
F: FnMut(Self::StreamId, StreamDataResult) + Send,
|
||||||
|
{
|
||||||
|
EventLoop::run(self, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamIdTrait for StreamId {}
|
||||||
|
|
||||||
// The emscripten backend works by having a global variable named `_cpal_audio_contexts`, which
|
// The emscripten backend works by having a global variable named `_cpal_audio_contexts`, which
|
||||||
// is an array of `AudioContext` objects. A stream ID corresponds to an entry in this array.
|
// is an array of `AudioContext` objects. A stream ID corresponds to an entry in this array.
|
||||||
//
|
//
|
||||||
|
@ -44,7 +152,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run<F>(&self, callback: F) -> !
|
fn run<F>(&self, callback: F) -> !
|
||||||
where F: FnMut(StreamId, StreamDataResult),
|
where F: FnMut(StreamId, StreamDataResult),
|
||||||
{
|
{
|
||||||
// The `run` function uses `set_timeout` to invoke a Rust callback repeatidely. The job
|
// The `run` function uses `set_timeout` to invoke a Rust callback repeatidely. The job
|
||||||
|
@ -124,12 +232,12 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build_input_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
fn build_input_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build_output_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
fn build_output_stream(&self, _: &Device, _format: &Format) -> Result<StreamId, BuildStreamError> {
|
||||||
let stream = js!(return new AudioContext()).into_reference().unwrap();
|
let stream = js!(return new AudioContext()).into_reference().unwrap();
|
||||||
|
|
||||||
let mut streams = self.streams.lock().unwrap();
|
let mut streams = self.streams.lock().unwrap();
|
||||||
|
@ -146,12 +254,12 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
fn destroy_stream(&self, stream_id: StreamId) {
|
||||||
self.streams.lock().unwrap()[stream_id.0] = None;
|
self.streams.lock().unwrap()[stream_id.0] = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
||||||
let streams = self.streams.lock().unwrap();
|
let streams = self.streams.lock().unwrap();
|
||||||
let stream = streams
|
let stream = streams
|
||||||
.get(stream_id.0)
|
.get(stream_id.0)
|
||||||
|
@ -162,7 +270,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
||||||
let streams = self.streams.lock().unwrap();
|
let streams = self.streams.lock().unwrap();
|
||||||
let stream = streams
|
let stream = streams
|
||||||
.get(stream_id.0)
|
.get(stream_id.0)
|
||||||
|
@ -193,7 +301,7 @@ fn is_webaudio_available() -> bool {
|
||||||
pub struct Devices(bool);
|
pub struct Devices(bool);
|
||||||
|
|
||||||
impl Devices {
|
impl Devices {
|
||||||
pub fn new() -> Result<Self, DevicesError> {
|
fn new() -> Result<Self, DevicesError> {
|
||||||
Ok(Self::default())
|
Ok(Self::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,12 +326,12 @@ impl Iterator for Devices {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn default_input_device() -> Option<Device> {
|
fn default_input_device() -> Option<Device> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn default_output_device() -> Option<Device> {
|
fn default_output_device() -> Option<Device> {
|
||||||
if is_webaudio_available() {
|
if is_webaudio_available() {
|
||||||
Some(Device)
|
Some(Device)
|
||||||
} else {
|
} else {
|
||||||
|
@ -236,17 +344,17 @@ pub struct Device;
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn name(&self) -> Result<String, DeviceNameError> {
|
fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
Ok("Default Device".to_owned())
|
Ok("Default Device".to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||||
// TODO: right now cpal's API doesn't allow flexibility here
|
// TODO: right now cpal's API doesn't allow flexibility here
|
||||||
// "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if
|
// "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if
|
||||||
// this ever becomes more flexible, don't forget to change that
|
// this ever becomes more flexible, don't forget to change that
|
||||||
|
@ -264,11 +372,11 @@ impl Device {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||||
// TODO: because it is hard coded, see supported_output_formats.
|
// TODO: because it is hard coded, see supported_output_formats.
|
||||||
Ok(
|
Ok(
|
||||||
Format {
|
Format {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||||
pub(crate) mod alsa;
|
pub(crate) mod alsa;
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
mod coreaudio;
|
pub(crate) mod coreaudio;
|
||||||
//mod dynamic;
|
//mod dynamic;
|
||||||
#[cfg(target_os = "emscripten")]
|
#[cfg(target_os = "emscripten")]
|
||||||
mod emscripten;
|
pub(crate) mod emscripten;
|
||||||
mod null;
|
pub(crate) mod null;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub(crate) mod wasapi;
|
pub(crate) mod wasapi;
|
||||||
|
|
Loading…
Reference in New Issue