all platform traits implemented & dumb async runtime imports
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ use std::cell::RefCell;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::imp::{SpawnHandle, SpawnHandleInterface as _};
|
use crate::imp::SpawnHandle;
|
||||||
|
|
||||||
static DF_MODEL: Asset = asset!("/assets/DeepFilterNet3_ll_onnx.tar.gz");
|
static DF_MODEL: Asset = asset!("/assets/DeepFilterNet3_ll_onnx.tar.gz");
|
||||||
// TODO: make this user configurable.
|
// TODO: make this user configurable.
|
||||||
|
|||||||
+31
-105
@@ -4,20 +4,8 @@ use dioxus::hooks::UnboundedReceiver;
|
|||||||
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||||
use mumble_web2_common::{ClientConfig, ServerStatus};
|
use mumble_web2_common::{ClientConfig, ServerStatus};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::future::Future;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::{
|
|
||||||
PlatformConfig, PlatformInit, PlatformInterface, PlatformNetwork, PlatformRuntime,
|
|
||||||
SpawnHandleTrait,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use super::connect::*;
|
|
||||||
pub use super::native_audio::*;
|
|
||||||
|
|
||||||
pub use tokio::task::spawn;
|
|
||||||
pub use tokio::time::sleep;
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Platform Struct
|
// Platform Struct
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -25,84 +13,69 @@ pub use tokio::time::sleep;
|
|||||||
/// Desktop platform implementation using Tokio and native audio.
|
/// Desktop platform implementation using Tokio and native audio.
|
||||||
pub struct DesktopPlatform;
|
pub struct DesktopPlatform;
|
||||||
|
|
||||||
// ============================================================================
|
impl super::PlatformInterface for DesktopPlatform {
|
||||||
// SpawnHandle
|
type AudioSystem = super::native_audio::NativeAudioSystem;
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
pub type SpawnHandle = tokio::runtime::Handle;
|
|
||||||
|
|
||||||
impl SpawnHandleTrait for SpawnHandle {
|
|
||||||
fn spawn<F>(&self, future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + Send + 'static,
|
|
||||||
{
|
|
||||||
let _ = tokio::runtime::Handle::spawn(self, future);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current() -> Self {
|
|
||||||
tokio::runtime::Handle::current()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Trait Implementations
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
impl PlatformRuntime for DesktopPlatform {
|
|
||||||
type SpawnHandle = SpawnHandle;
|
|
||||||
|
|
||||||
fn spawn<F>(future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + Send + 'static,
|
|
||||||
{
|
|
||||||
let _ = tokio::task::spawn(future);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn sleep(duration: Duration) {
|
async fn sleep(duration: Duration) {
|
||||||
tokio::time::sleep(duration).await;
|
tokio::time::sleep(duration).await;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformConfig for DesktopPlatform {
|
|
||||||
async fn load_config() -> color_eyre::Result<ClientConfig> {
|
async fn load_config() -> color_eyre::Result<ClientConfig> {
|
||||||
load_config().await
|
Ok(ClientConfig {
|
||||||
|
proxy_url: None,
|
||||||
|
cert_hash: None,
|
||||||
|
any_server: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_username() -> Option<String> {
|
fn load_username() -> Option<String> {
|
||||||
load_username()
|
let config = load_config_map();
|
||||||
|
config.get("username").cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_server_url() -> Option<String> {
|
fn load_server_url() -> Option<String> {
|
||||||
load_server_url()
|
let config = load_config_map();
|
||||||
|
config.get("server").cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_default_username(username: &str) -> Option<()> {
|
fn set_default_username(username: &str) -> Option<()> {
|
||||||
set_default_username(username)
|
let mut config = load_config_map();
|
||||||
|
config.insert("username".to_string(), username.to_string());
|
||||||
|
save_config_map(&config).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_default_server(server: &str) -> Option<()> {
|
fn set_default_server(server: &str) -> Option<()> {
|
||||||
set_default_server(server)
|
let mut config = load_config_map();
|
||||||
|
config.insert("server".to_string(), server.to_string());
|
||||||
|
save_config_map(&config).ok()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformNetwork for DesktopPlatform {
|
|
||||||
async fn network_connect(
|
async fn network_connect(
|
||||||
address: String,
|
address: String,
|
||||||
username: String,
|
username: String,
|
||||||
event_rx: &mut UnboundedReceiver<Command>,
|
event_rx: &mut UnboundedReceiver<Command>,
|
||||||
gui_config: &ClientConfig,
|
gui_config: &ClientConfig,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
network_connect(address, username, event_rx, gui_config).await
|
super::connect::network_connect(address, username, event_rx, gui_config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_status(client: &reqwest::Client) -> color_eyre::Result<ServerStatus> {
|
async fn get_status(client: &reqwest::Client) -> color_eyre::Result<ServerStatus> {
|
||||||
get_status(client).await
|
super::connect::get_status(client).await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformInit for DesktopPlatform {
|
|
||||||
fn init_logging() {
|
fn init_logging() {
|
||||||
init_logging();
|
use tracing::level_filters::LevelFilter;
|
||||||
|
use tracing_subscriber::filter::EnvFilter;
|
||||||
|
|
||||||
|
let env_filter = EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::INFO.into())
|
||||||
|
.from_env_lossy();
|
||||||
|
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_target(true)
|
||||||
|
.with_level(true)
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_permissions() {
|
fn request_permissions() {
|
||||||
@@ -110,8 +83,6 @@ impl PlatformInit for DesktopPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformInterface for DesktopPlatform {}
|
|
||||||
|
|
||||||
fn get_config_path() -> std::path::PathBuf {
|
fn get_config_path() -> std::path::PathBuf {
|
||||||
let strategy = choose_app_strategy(AppStrategyArgs {
|
let strategy = choose_app_strategy(AppStrategyArgs {
|
||||||
top_level_domain: "com".to_string(),
|
top_level_domain: "com".to_string(),
|
||||||
@@ -139,48 +110,3 @@ fn save_config_map(config: &HashMap<String, String>) -> color_eyre::Result<()> {
|
|||||||
std::fs::write(&config_path, contents)?;
|
std::fs::write(&config_path, contents)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_default_username(username: &str) -> Option<()> {
|
|
||||||
let mut config = load_config_map();
|
|
||||||
config.insert("username".to_string(), username.to_string());
|
|
||||||
save_config_map(&config).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_default_server(server: &str) -> Option<()> {
|
|
||||||
let mut config = load_config_map();
|
|
||||||
config.insert("server".to_string(), server.to_string());
|
|
||||||
save_config_map(&config).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_username() -> Option<String> {
|
|
||||||
let config = load_config_map();
|
|
||||||
config.get("username").cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_server_url() -> Option<String> {
|
|
||||||
let config = load_config_map();
|
|
||||||
config.get("server").cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_config() -> color_eyre::Result<ClientConfig> {
|
|
||||||
Ok(ClientConfig {
|
|
||||||
proxy_url: None,
|
|
||||||
cert_hash: None,
|
|
||||||
any_server: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_logging() {
|
|
||||||
use tracing::level_filters::LevelFilter;
|
|
||||||
use tracing_subscriber::filter::EnvFilter;
|
|
||||||
|
|
||||||
let env_filter = EnvFilter::builder()
|
|
||||||
.with_default_directive(LevelFilter::INFO.into())
|
|
||||||
.from_env_lossy();
|
|
||||||
|
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.with_target(true)
|
|
||||||
.with_level(true)
|
|
||||||
.with_env_filter(env_filter)
|
|
||||||
.init();
|
|
||||||
}
|
|
||||||
|
|||||||
+33
-102
@@ -1,19 +1,11 @@
|
|||||||
use android_permissions::{PermissionManager, RECORD_AUDIO};
|
|
||||||
use crate::app::Command;
|
use crate::app::Command;
|
||||||
use color_eyre::eyre::Error;
|
use color_eyre::eyre::Error;
|
||||||
use dioxus::hooks::UnboundedReceiver;
|
use dioxus::hooks::UnboundedReceiver;
|
||||||
use jni::{objects::JObject, JavaVM};
|
|
||||||
use mumble_web2_common::{ClientConfig, ServerStatus};
|
use mumble_web2_common::{ClientConfig, ServerStatus};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::{PlatformInterface, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
|
|
||||||
|
|
||||||
pub use super::connect::*;
|
pub use super::connect::*;
|
||||||
pub use super::native_audio::*;
|
|
||||||
|
|
||||||
pub use tokio::task::spawn;
|
|
||||||
pub use tokio::time::sleep;
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Platform Struct
|
// Platform Struct
|
||||||
@@ -22,67 +14,33 @@ pub use tokio::time::sleep;
|
|||||||
/// Mobile platform implementation using Tokio, native audio, and Android permissions.
|
/// Mobile platform implementation using Tokio, native audio, and Android permissions.
|
||||||
pub struct MobilePlatform;
|
pub struct MobilePlatform;
|
||||||
|
|
||||||
// ============================================================================
|
impl super::PlatformInterface for MobilePlatform {
|
||||||
// SpawnHandle
|
type AudioSystem = super::native_audio::NativeAudioSystem;
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
pub type SpawnHandle = tokio::runtime::Handle;
|
|
||||||
|
|
||||||
impl SpawnHandleTrait for SpawnHandle {
|
|
||||||
fn spawn<F>(&self, future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + Send + 'static,
|
|
||||||
{
|
|
||||||
let _ = tokio::runtime::Handle::spawn(self, future);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current() -> Self {
|
|
||||||
tokio::runtime::Handle::current()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Trait Implementations
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
impl PlatformRuntime for MobilePlatform {
|
|
||||||
type SpawnHandle = SpawnHandle;
|
|
||||||
|
|
||||||
fn spawn<F>(future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + Send + 'static,
|
|
||||||
{
|
|
||||||
let _ = tokio::task::spawn(future);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn sleep(duration: Duration) {
|
|
||||||
tokio::time::sleep(duration).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformConfig for MobilePlatform {
|
|
||||||
async fn load_config() -> color_eyre::Result<ClientConfig> {
|
async fn load_config() -> color_eyre::Result<ClientConfig> {
|
||||||
load_config().await
|
Ok(ClientConfig {
|
||||||
|
proxy_url: None,
|
||||||
|
cert_hash: None,
|
||||||
|
any_server: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_username() -> Option<String> {
|
fn load_username() -> Option<String> {
|
||||||
load_username()
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_server_url() -> Option<String> {
|
fn load_server_url() -> Option<String> {
|
||||||
load_server_url()
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_default_username(username: &str) -> Option<()> {
|
fn set_default_username(_username: &str) -> Option<()> {
|
||||||
set_default_username(username)
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_default_server(server: &str) -> Option<()> {
|
fn set_default_server(server: &str) -> Option<()> {
|
||||||
set_default_server(server)
|
None
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformNetwork for MobilePlatform {
|
|
||||||
async fn network_connect(
|
async fn network_connect(
|
||||||
address: String,
|
address: String,
|
||||||
username: String,
|
username: String,
|
||||||
@@ -95,66 +53,39 @@ impl PlatformNetwork for MobilePlatform {
|
|||||||
async fn get_status(client: &reqwest::Client) -> color_eyre::Result<ServerStatus> {
|
async fn get_status(client: &reqwest::Client) -> color_eyre::Result<ServerStatus> {
|
||||||
get_status(client).await
|
get_status(client).await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformInit for MobilePlatform {
|
|
||||||
fn init_logging() {
|
fn init_logging() {
|
||||||
init_logging();
|
use tracing::level_filters::LevelFilter;
|
||||||
|
use tracing_subscriber::filter::EnvFilter;
|
||||||
|
|
||||||
|
let env_filter = EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::INFO.into())
|
||||||
|
.from_env_lossy();
|
||||||
|
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_target(true)
|
||||||
|
.with_level(true)
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_permissions() {
|
fn request_permissions() {
|
||||||
request_recording_permission();
|
request_recording_permission();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn sleep(duration: Duration) {
|
||||||
|
tokio::time::sleep(duration).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformInterface for MobilePlatform {}
|
#[cfg(not(target_os = "android"))]
|
||||||
|
pub fn request_recording_permission() {}
|
||||||
pub fn set_default_username(_username: &str) -> Option<()> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_default_server(server: &str) -> Option<()> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_username() -> Option<String> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_server_url() -> Option<String> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_config() -> color_eyre::Result<ClientConfig> {
|
|
||||||
Ok(ClientConfig {
|
|
||||||
proxy_url: None,
|
|
||||||
cert_hash: None,
|
|
||||||
any_server: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_logging() {
|
|
||||||
use tracing::level_filters::LevelFilter;
|
|
||||||
use tracing_subscriber::filter::EnvFilter;
|
|
||||||
|
|
||||||
let env_filter = EnvFilter::builder()
|
|
||||||
.with_default_directive(LevelFilter::INFO.into())
|
|
||||||
.from_env_lossy();
|
|
||||||
|
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.with_target(true)
|
|
||||||
.with_level(true)
|
|
||||||
.with_env_filter(env_filter)
|
|
||||||
.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "mobile")]
|
|
||||||
pub fn request_permissions() {
|
|
||||||
request_recording_permission();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub fn request_recording_permission() {
|
pub fn request_recording_permission() {
|
||||||
|
use android_permissions::{PermissionManager, RECORD_AUDIO};
|
||||||
|
use jni::{objects::JObject, JavaVM};
|
||||||
|
|
||||||
let ctx = ndk_context::android_context();
|
let ctx = ndk_context::android_context();
|
||||||
let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()).unwrap() };
|
let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()).unwrap() };
|
||||||
let activity = unsafe { JObject::from_raw(ctx.context().cast()) };
|
let activity = unsafe { JObject::from_raw(ctx.context().cast()) };
|
||||||
|
|||||||
+12
-21
@@ -14,17 +14,6 @@ use std::time::Duration;
|
|||||||
// Trait Definitions
|
// Trait Definitions
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// Trait for spawn handles that can be stored and used to spawn tasks later.
|
|
||||||
pub trait SpawnHandleInterface: Clone + 'static {
|
|
||||||
/// Spawn an async task using this handle.
|
|
||||||
fn spawn<F>(&self, future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + 'static;
|
|
||||||
|
|
||||||
/// Get a spawn handle for the current context.
|
|
||||||
fn current() -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AudioSystemInterface {
|
pub trait AudioSystemInterface {
|
||||||
type AudioPlayer: AudioPlayerInterface;
|
type AudioPlayer: AudioPlayerInterface;
|
||||||
}
|
}
|
||||||
@@ -36,7 +25,6 @@ pub trait AudioPlayerInterface {}
|
|||||||
/// verification that all platforms implement the required functionality.
|
/// verification that all platforms implement the required functionality.
|
||||||
pub trait PlatformInterface {
|
pub trait PlatformInterface {
|
||||||
type AudioSystem: AudioSystemInterface;
|
type AudioSystem: AudioSystemInterface;
|
||||||
type SpawnHandle: SpawnHandleInterface;
|
|
||||||
|
|
||||||
/// Initialize logging for the platform.
|
/// Initialize logging for the platform.
|
||||||
fn init_logging();
|
fn init_logging();
|
||||||
@@ -72,11 +60,6 @@ pub trait PlatformInterface {
|
|||||||
/// Save the default server URL.
|
/// Save the default server URL.
|
||||||
fn set_default_server(server: &str) -> Option<()>;
|
fn set_default_server(server: &str) -> Option<()>;
|
||||||
|
|
||||||
/// Spawn an async task.
|
|
||||||
fn spawn<F>(future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + 'static;
|
|
||||||
|
|
||||||
/// Async sleep for the given duration.
|
/// Async sleep for the given duration.
|
||||||
fn sleep(duration: Duration) -> impl Future<Output = ()>;
|
fn sleep(duration: Duration) -> impl Future<Output = ()>;
|
||||||
}
|
}
|
||||||
@@ -86,7 +69,7 @@ pub trait PlatformInterface {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#[cfg(feature = "web")]
|
#[cfg(feature = "web")]
|
||||||
mod web;
|
pub mod web;
|
||||||
|
|
||||||
#[cfg(any(feature = "desktop", feature = "mobile"))]
|
#[cfg(any(feature = "desktop", feature = "mobile"))]
|
||||||
mod connect;
|
mod connect;
|
||||||
@@ -94,9 +77,9 @@ mod connect;
|
|||||||
mod native_audio;
|
mod native_audio;
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
mod desktop;
|
pub mod desktop;
|
||||||
#[cfg(feature = "mobile")]
|
#[cfg(feature = "mobile")]
|
||||||
mod mobile;
|
pub mod mobile;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Platform Type Alias
|
// Platform Type Alias
|
||||||
@@ -113,7 +96,15 @@ pub type Platform = mobile::MobilePlatform;
|
|||||||
|
|
||||||
pub type AudioSystem = <Platform as PlatformInterface>::AudioSystem;
|
pub type AudioSystem = <Platform as PlatformInterface>::AudioSystem;
|
||||||
pub type AudioPlayer = <AudioSystem as AudioSystemInterface>::AudioPlayer;
|
pub type AudioPlayer = <AudioSystem as AudioSystemInterface>::AudioPlayer;
|
||||||
pub type SpawnHandle = <Platform as PlatformInterface>::SpawnHandle;
|
|
||||||
|
// ========================
|
||||||
|
// Platform Async Runtime
|
||||||
|
// ========================
|
||||||
|
// Note: these can not be part of the Platform because they differ in Send requiremets
|
||||||
|
#[cfg(all(any(feature = "desktop", feature = "mobile"), not(feature = "web")))]
|
||||||
|
pub use native_audio::{spawn, SpawnHandle};
|
||||||
|
#[cfg(feature = "web")]
|
||||||
|
pub use web::{spawn, SpawnHandle};
|
||||||
|
|
||||||
/// Compile-time assertion that CurrentPlatform implements Platform.
|
/// Compile-time assertion that CurrentPlatform implements Platform.
|
||||||
const _: () = {
|
const _: () = {
|
||||||
|
|||||||
+21
-12
@@ -1,19 +1,22 @@
|
|||||||
use crate::effects::{AudioProcessor, AudioProcessorSender, TransmitState};
|
use crate::effects::{AudioProcessor, AudioProcessorSender, TransmitState};
|
||||||
use color_eyre::eyre::{eyre, Error};
|
use color_eyre::eyre::{eyre, Error};
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait as _};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait as _};
|
||||||
use futures::io::{AsyncRead, AsyncWrite};
|
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
pub trait ImpRead: AsyncRead + Unpin + Send + 'static {}
|
// =============
|
||||||
impl<T: AsyncRead + Unpin + Send + 'static> ImpRead for T {}
|
// Async runtime
|
||||||
|
// =============
|
||||||
|
pub use tokio::spawn;
|
||||||
|
pub type SpawnHandle = tokio::runtime::Handle;
|
||||||
|
|
||||||
pub trait ImpWrite: AsyncWrite + Unpin + Send + 'static {}
|
// ============
|
||||||
impl<T: AsyncWrite + Unpin + Send + 'static> ImpWrite for T {}
|
// Audio System
|
||||||
|
// ============
|
||||||
|
|
||||||
pub struct AudioSystem {
|
pub struct NativeAudioSystem {
|
||||||
output: cpal::Device,
|
output: cpal::Device,
|
||||||
input: cpal::Device,
|
input: cpal::Device,
|
||||||
processors: AudioProcessorSender,
|
processors: AudioProcessorSender,
|
||||||
@@ -52,13 +55,13 @@ 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 AudioSystem {
|
impl NativeAudioSystem {
|
||||||
pub async fn new() -> Result<Self, Error> {
|
pub async fn new() -> Result<Self, Error> {
|
||||||
// TODO
|
// TODO
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let name = host.id();
|
let name = host.id();
|
||||||
let processors = AudioProcessorSender::default();
|
let processors = AudioProcessorSender::default();
|
||||||
Ok(AudioSystem {
|
Ok(NativeAudioSystem {
|
||||||
output: host
|
output: host
|
||||||
.default_output_device()
|
.default_output_device()
|
||||||
.ok_or(eyre!("no output devices from {name:?}"))?,
|
.ok_or(eyre!("no output devices from {name:?}"))?,
|
||||||
@@ -145,7 +148,7 @@ impl AudioSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_player(&mut self) -> Result<AudioPlayer, Error> {
|
pub 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 {:#?}",
|
||||||
@@ -183,7 +186,7 @@ impl AudioSystem {
|
|||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
Ok(AudioPlayer {
|
Ok(NativeAudioPlayer {
|
||||||
decoder,
|
decoder,
|
||||||
stream,
|
stream,
|
||||||
buffer,
|
buffer,
|
||||||
@@ -192,14 +195,18 @@ impl AudioSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AudioPlayer {
|
impl super::AudioSystemInterface for NativeAudioSystem {
|
||||||
|
type AudioPlayer = NativeAudioPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NativeAudioPlayer {
|
||||||
decoder: opus::Decoder,
|
decoder: opus::Decoder,
|
||||||
stream: cpal::Stream,
|
stream: cpal::Stream,
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
tmp: Vec<i16>,
|
tmp: Vec<i16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioPlayer {
|
impl NativeAudioPlayer {
|
||||||
pub fn play_opus(&mut self, payload: &[u8]) {
|
pub 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,
|
||||||
@@ -221,3 +228,5 @@ impl AudioPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl super::AudioPlayerInterface for NativeAudioPlayer {}
|
||||||
|
|||||||
+22
-30
@@ -38,14 +38,34 @@ use web_sys::WorkletOptions;
|
|||||||
use web_sys::{console, window};
|
use web_sys::{console, window};
|
||||||
use web_sys::{AudioContext, AudioDataCopyToOptions};
|
use web_sys::{AudioContext, AudioDataCopyToOptions};
|
||||||
|
|
||||||
pub use wasm_bindgen_futures::spawn_local as spawn;
|
|
||||||
|
|
||||||
pub trait ImpRead: AsyncRead + Unpin + 'static {}
|
pub trait ImpRead: AsyncRead + Unpin + 'static {}
|
||||||
impl<T: AsyncRead + Unpin + 'static> ImpRead for T {}
|
impl<T: AsyncRead + Unpin + 'static> ImpRead for T {}
|
||||||
|
|
||||||
pub trait ImpWrite: AsyncWrite + Unpin + 'static {}
|
pub trait ImpWrite: AsyncWrite + Unpin + 'static {}
|
||||||
impl<T: AsyncWrite + Unpin + 'static> ImpWrite for T {}
|
impl<T: AsyncWrite + Unpin + 'static> ImpWrite for T {}
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// Async runtime
|
||||||
|
// =============
|
||||||
|
|
||||||
|
pub use wasm_bindgen_futures::spawn_local as spawn;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SpawnHandle;
|
||||||
|
|
||||||
|
impl SpawnHandle {
|
||||||
|
pub fn spawn<F>(&self, future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + 'static,
|
||||||
|
{
|
||||||
|
wasm_bindgen_futures::spawn_local(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current() -> Self {
|
||||||
|
SpawnHandle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Platform Struct
|
// Platform Struct
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -53,29 +73,8 @@ impl<T: AsyncWrite + Unpin + 'static> ImpWrite for T {}
|
|||||||
/// Web platform implementation using WebTransport and Web Audio API.
|
/// Web platform implementation using WebTransport and Web Audio API.
|
||||||
pub struct WebPlatform;
|
pub struct WebPlatform;
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Trait Implementations
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct WebSpawnHandle;
|
|
||||||
|
|
||||||
impl super::SpawnHandleInterface for WebSpawnHandle {
|
|
||||||
fn spawn<F>(&self, future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + 'static,
|
|
||||||
{
|
|
||||||
wasm_bindgen_futures::spawn_local(future);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current() -> Self {
|
|
||||||
WebSpawnHandle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::PlatformInterface for WebPlatform {
|
impl super::PlatformInterface for WebPlatform {
|
||||||
type AudioSystem = WebAudioSystem;
|
type AudioSystem = WebAudioSystem;
|
||||||
type SpawnHandle = WebSpawnHandle;
|
|
||||||
|
|
||||||
fn init_logging() {
|
fn init_logging() {
|
||||||
init_logging();
|
init_logging();
|
||||||
@@ -118,13 +117,6 @@ impl super::PlatformInterface for WebPlatform {
|
|||||||
get_status(client).await
|
get_status(client).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn<F>(future: F)
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + 'static,
|
|
||||||
{
|
|
||||||
wasm_bindgen_futures::spawn_local(future);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn sleep(duration: Duration) {
|
async fn sleep(duration: Duration) {
|
||||||
TimeoutFuture::new(duration.as_millis() as u32).await;
|
TimeoutFuture::new(duration.as_millis() as u32).await;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user