move audio stuff into trait
This commit is contained in:
+26
-5
@@ -2,8 +2,9 @@
|
|||||||
//!
|
//!
|
||||||
//! This module defines traits that each platform (web, desktop, mobile) must implement.
|
//! This module defines traits that each platform (web, desktop, mobile) must implement.
|
||||||
//! The traits make the platform boundary explicit and provide compile-time verification.
|
//! The traits make the platform boundary explicit and provide compile-time verification.
|
||||||
|
#![allow(async_fn_in_trait)]
|
||||||
|
|
||||||
use crate::app::Command;
|
use crate::{app::Command, effects::AudioProcessor};
|
||||||
use color_eyre::eyre::Error;
|
use color_eyre::eyre::Error;
|
||||||
use dioxus::hooks::UnboundedReceiver;
|
use dioxus::hooks::UnboundedReceiver;
|
||||||
use mumble_web2_common::{ClientConfig, ServerStatus};
|
use mumble_web2_common::{ClientConfig, ServerStatus};
|
||||||
@@ -14,11 +15,31 @@ use std::time::Duration;
|
|||||||
// Trait Definitions
|
// Trait Definitions
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
pub trait AudioSystemInterface {
|
pub trait AudioSystemInterface: Sized {
|
||||||
type AudioPlayer: AudioPlayerInterface;
|
type AudioPlayer: AudioPlayerInterface;
|
||||||
|
|
||||||
|
/// Initialize the audio system, including relevant state
|
||||||
|
async fn new() -> Result<Self, Error>;
|
||||||
|
|
||||||
|
/// Set the processor for the microphone input, mainly noise cancellation settings.
|
||||||
|
fn set_processor(&self, processor: AudioProcessor);
|
||||||
|
|
||||||
|
/// Begin listening to microphone input, calling the `each` function with
|
||||||
|
/// encoded opus frames.
|
||||||
|
fn start_recording(
|
||||||
|
&mut self,
|
||||||
|
each: impl FnMut(Vec<u8>, bool) + Send + 'static,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Begin playback of an audio stream, returning an object that can be passed
|
||||||
|
/// with opus frames.
|
||||||
|
fn create_player(&mut self) -> Result<Self::AudioPlayer, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AudioPlayerInterface {}
|
pub trait AudioPlayerInterface {
|
||||||
|
/// Playback an opus frame.
|
||||||
|
fn play_opus(&mut self, payload: &[u8]);
|
||||||
|
}
|
||||||
|
|
||||||
/// This is the main trait that each platform must implement. It combines all
|
/// This is the main trait that each platform must implement. It combines all
|
||||||
/// platform-specific functionality into a single interface, providing compile-time
|
/// platform-specific functionality into a single interface, providing compile-time
|
||||||
@@ -72,9 +93,9 @@ pub trait PlatformInterface {
|
|||||||
pub mod web;
|
pub mod web;
|
||||||
|
|
||||||
#[cfg(any(feature = "desktop", feature = "mobile"))]
|
#[cfg(any(feature = "desktop", feature = "mobile"))]
|
||||||
mod connect;
|
pub mod connect;
|
||||||
#[cfg(any(feature = "desktop", feature = "mobile"))]
|
#[cfg(any(feature = "desktop", feature = "mobile"))]
|
||||||
mod native_audio;
|
pub mod native_audio;
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
pub mod desktop;
|
pub mod desktop;
|
||||||
|
|||||||
+29
-31
@@ -56,27 +56,6 @@ fn encode_and_send(
|
|||||||
type Buffer = Arc<Mutex<dasp_ring_buffer::Bounded<Vec<i16>>>>;
|
type Buffer = Arc<Mutex<dasp_ring_buffer::Bounded<Vec<i16>>>>;
|
||||||
|
|
||||||
impl NativeAudioSystem {
|
impl NativeAudioSystem {
|
||||||
pub async fn new() -> Result<Self, Error> {
|
|
||||||
// TODO
|
|
||||||
let host = cpal::default_host();
|
|
||||||
let name = host.id();
|
|
||||||
let processors = AudioProcessorSender::default();
|
|
||||||
Ok(NativeAudioSystem {
|
|
||||||
output: host
|
|
||||||
.default_output_device()
|
|
||||||
.ok_or(eyre!("no output devices from {name:?}"))?,
|
|
||||||
input: host
|
|
||||||
.default_input_device()
|
|
||||||
.ok_or(eyre!("no input devices from {name:?}"))?,
|
|
||||||
processors,
|
|
||||||
recording_stream: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_processor(&self, processor: AudioProcessor) {
|
|
||||||
self.processors.store(Some(processor))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn choose_config(
|
fn choose_config(
|
||||||
&self,
|
&self,
|
||||||
configs: impl Iterator<Item = cpal::SupportedStreamConfigRange>,
|
configs: impl Iterator<Item = cpal::SupportedStreamConfigRange>,
|
||||||
@@ -106,8 +85,33 @@ impl NativeAudioSystem {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.ok_or(eyre!("no supported stream configs"))
|
.ok_or(eyre!("no supported stream configs"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_recording(
|
impl super::AudioSystemInterface for NativeAudioSystem {
|
||||||
|
type AudioPlayer = NativeAudioPlayer;
|
||||||
|
|
||||||
|
async fn new() -> Result<Self, Error> {
|
||||||
|
// TODO
|
||||||
|
let host = cpal::default_host();
|
||||||
|
let name = host.id();
|
||||||
|
let processors = AudioProcessorSender::default();
|
||||||
|
Ok(NativeAudioSystem {
|
||||||
|
output: host
|
||||||
|
.default_output_device()
|
||||||
|
.ok_or(eyre!("no output devices from {name:?}"))?,
|
||||||
|
input: host
|
||||||
|
.default_input_device()
|
||||||
|
.ok_or(eyre!("no input devices from {name:?}"))?,
|
||||||
|
processors,
|
||||||
|
recording_stream: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_processor(&self, processor: AudioProcessor) {
|
||||||
|
self.processors.store(Some(processor))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_recording(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut each: impl FnMut(Vec<u8>, bool) + Send + 'static,
|
mut each: impl FnMut(Vec<u8>, bool) + Send + 'static,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@@ -148,7 +152,7 @@ impl NativeAudioSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_player(&mut self) -> Result<NativeAudioPlayer, Error> {
|
fn create_player(&mut self) -> Result<NativeAudioPlayer, Error> {
|
||||||
let config = self.choose_config(self.output.supported_output_configs()?)?;
|
let config = self.choose_config(self.output.supported_output_configs()?)?;
|
||||||
info!(
|
info!(
|
||||||
"creating player on {:?} with {:#?}",
|
"creating player on {:?} with {:#?}",
|
||||||
@@ -195,10 +199,6 @@ impl NativeAudioSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::AudioSystemInterface for NativeAudioSystem {
|
|
||||||
type AudioPlayer = NativeAudioPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NativeAudioPlayer {
|
pub struct NativeAudioPlayer {
|
||||||
decoder: opus::Decoder,
|
decoder: opus::Decoder,
|
||||||
stream: cpal::Stream,
|
stream: cpal::Stream,
|
||||||
@@ -206,8 +206,8 @@ pub struct NativeAudioPlayer {
|
|||||||
tmp: Vec<i16>,
|
tmp: Vec<i16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeAudioPlayer {
|
impl super::AudioPlayerInterface for NativeAudioPlayer {
|
||||||
pub fn play_opus(&mut self, payload: &[u8]) {
|
fn play_opus(&mut self, payload: &[u8]) {
|
||||||
let len = match self.decoder.decode(payload, &mut self.tmp, false) {
|
let len = match self.decoder.decode(payload, &mut self.tmp, false) {
|
||||||
Ok(l) => l,
|
Ok(l) => l,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -228,5 +228,3 @@ impl NativeAudioPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::AudioPlayerInterface for NativeAudioPlayer {}
|
|
||||||
|
|||||||
+6
-13
@@ -177,10 +177,8 @@ async fn attach_worklet(audio_context: &AudioContext) -> Result<(), Error> {
|
|||||||
|
|
||||||
impl super::AudioSystemInterface for WebAudioSystem {
|
impl super::AudioSystemInterface for WebAudioSystem {
|
||||||
type AudioPlayer = WebAudioPlayer;
|
type AudioPlayer = WebAudioPlayer;
|
||||||
}
|
|
||||||
|
|
||||||
impl WebAudioSystem {
|
async fn new() -> Result<Self, Error> {
|
||||||
pub async fn new() -> Result<Self, Error> {
|
|
||||||
// Create MediaStreams to playback decoded audio
|
// Create MediaStreams to playback decoded audio
|
||||||
// The audio context is used to reproduce audio.
|
// The audio context is used to reproduce audio.
|
||||||
let webctx = configure_audio_context();
|
let webctx = configure_audio_context();
|
||||||
@@ -191,14 +189,11 @@ impl WebAudioSystem {
|
|||||||
Ok(WebAudioSystem { webctx, processors })
|
Ok(WebAudioSystem { webctx, processors })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_processor(&self, processor: AudioProcessor) {
|
fn set_processor(&self, processor: AudioProcessor) {
|
||||||
self.processors.store(Some(processor))
|
self.processors.store(Some(processor))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_recording(
|
fn start_recording(&mut self, each: impl FnMut(Vec<u8>, bool) + 'static) -> Result<(), Error> {
|
||||||
&mut self,
|
|
||||||
each: impl FnMut(Vec<u8>, bool) + 'static,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let audio_context_worklet = self.webctx.clone();
|
let audio_context_worklet = self.webctx.clone();
|
||||||
let processors = self.processors.clone();
|
let processors = self.processors.clone();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
@@ -210,7 +205,7 @@ impl WebAudioSystem {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_player(&mut self) -> Result<WebAudioPlayer, Error> {
|
fn create_player(&mut self) -> Result<WebAudioPlayer, Error> {
|
||||||
let sink_node = AudioWorkletNode::new(&self.webctx, "rust_speaker_worklet").ey()?;
|
let sink_node = AudioWorkletNode::new(&self.webctx, "rust_speaker_worklet").ey()?;
|
||||||
|
|
||||||
// Connect worklet to destination
|
// Connect worklet to destination
|
||||||
@@ -269,10 +264,8 @@ impl WebAudioSystem {
|
|||||||
|
|
||||||
pub struct WebAudioPlayer(AudioDecoder);
|
pub struct WebAudioPlayer(AudioDecoder);
|
||||||
|
|
||||||
impl super::AudioPlayerInterface for WebAudioPlayer {}
|
impl super::AudioPlayerInterface for WebAudioPlayer {
|
||||||
|
fn play_opus(&mut self, payload: &[u8]) {
|
||||||
impl WebAudioPlayer {
|
|
||||||
pub fn play_opus(&mut self, payload: &[u8]) {
|
|
||||||
let js_audio_payload = Uint8Array::from(payload);
|
let js_audio_payload = Uint8Array::from(payload);
|
||||||
let _ = self.0.decode(
|
let _ = self.0.decode(
|
||||||
&EncodedAudioChunk::new(&EncodedAudioChunkInit::new(
|
&EncodedAudioChunk::new(&EncodedAudioChunkInit::new(
|
||||||
|
|||||||
+2
-1
@@ -29,7 +29,8 @@ use tracing::info;
|
|||||||
|
|
||||||
use crate::effects::AudioProcessor;
|
use crate::effects::AudioProcessor;
|
||||||
use crate::imp::{
|
use crate::imp::{
|
||||||
AudioPlayer, AudioSystem, AudioSystemInterface as _, Platform, PlatformInterface as _,
|
AudioPlayer, AudioPlayerInterface as _, AudioSystem, AudioSystemInterface as _, Platform,
|
||||||
|
PlatformInterface as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
|
|||||||
Reference in New Issue
Block a user