diff --git a/Cargo.toml b/Cargo.toml index 5b47c0f..cc43ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,16 +26,22 @@ keywords = ["audio", "realtime", "cross-platform", "record", "play"] readme = "README.md" edition = "2021" +# [dependencies] +# alsa = "0.9.1" + # For all platforms +[dependencies.event_iterator] +version = "0.2.2" + [dependencies.fon] -version = "0.5" +version = "0.6.0" [dependencies.pasts] -version = "0.12" +version = "0.14.3" # For Linux and Android [target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "linux", target_os = "android")))'.dependencies] -smelling_salts = "0.2" +smelling_salts = "0.12" dl_api = "0.4" # For Web Assembly @@ -59,7 +65,3 @@ features = [ ] [target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen] version = "0.2" - -# Examples -[dev-dependencies] -twang = "0.7" diff --git a/src/consts.rs b/old_src/consts.rs similarity index 100% rename from src/consts.rs rename to old_src/consts.rs diff --git a/src/ffi/android/ffi.rs b/old_src/ffi/android/ffi.rs similarity index 100% rename from src/ffi/android/ffi.rs rename to old_src/ffi/android/ffi.rs diff --git a/src/ffi/bsd/ffi.rs b/old_src/ffi/bsd/ffi.rs similarity index 100% rename from src/ffi/bsd/ffi.rs rename to old_src/ffi/bsd/ffi.rs diff --git a/src/ffi/dummy/device_list.rs b/old_src/ffi/dummy/device_list.rs similarity index 100% rename from src/ffi/dummy/device_list.rs rename to old_src/ffi/dummy/device_list.rs diff --git a/src/ffi/dummy/ffi.rs b/old_src/ffi/dummy/ffi.rs similarity index 100% rename from src/ffi/dummy/ffi.rs rename to old_src/ffi/dummy/ffi.rs diff --git a/src/ffi/dummy/microphone.rs b/old_src/ffi/dummy/microphone.rs similarity index 100% rename from src/ffi/dummy/microphone.rs rename to old_src/ffi/dummy/microphone.rs diff --git a/src/ffi/dummy/speakers.rs b/old_src/ffi/dummy/speakers.rs similarity index 100% rename from src/ffi/dummy/speakers.rs rename to old_src/ffi/dummy/speakers.rs diff --git a/src/ffi/fuchsia/ffi.rs b/old_src/ffi/fuchsia/ffi.rs similarity index 100% rename from src/ffi/fuchsia/ffi.rs rename to old_src/ffi/fuchsia/ffi.rs diff --git a/src/ffi/ios/ffi.rs b/old_src/ffi/ios/ffi.rs similarity index 100% rename from src/ffi/ios/ffi.rs rename to old_src/ffi/ios/ffi.rs diff --git a/src/ffi/linux/asound.rs b/old_src/ffi/linux/asound.rs similarity index 100% rename from src/ffi/linux/asound.rs rename to old_src/ffi/linux/asound.rs diff --git a/src/ffi/linux/device_list.rs b/old_src/ffi/linux/device_list.rs similarity index 100% rename from src/ffi/linux/device_list.rs rename to old_src/ffi/linux/device_list.rs diff --git a/src/ffi/linux/ffi.rs b/old_src/ffi/linux/ffi.rs similarity index 100% rename from src/ffi/linux/ffi.rs rename to old_src/ffi/linux/ffi.rs diff --git a/src/ffi/linux/microphone.rs b/old_src/ffi/linux/microphone.rs similarity index 100% rename from src/ffi/linux/microphone.rs rename to old_src/ffi/linux/microphone.rs diff --git a/src/ffi/linux/pcm.rs b/old_src/ffi/linux/pcm.rs similarity index 100% rename from src/ffi/linux/pcm.rs rename to old_src/ffi/linux/pcm.rs diff --git a/src/ffi/linux/speakers.rs b/old_src/ffi/linux/speakers.rs similarity index 100% rename from src/ffi/linux/speakers.rs rename to old_src/ffi/linux/speakers.rs diff --git a/src/ffi/macos/audio_queue.rs b/old_src/ffi/macos/audio_queue.rs similarity index 100% rename from src/ffi/macos/audio_queue.rs rename to old_src/ffi/macos/audio_queue.rs diff --git a/src/ffi/macos/device_list.rs b/old_src/ffi/macos/device_list.rs similarity index 100% rename from src/ffi/macos/device_list.rs rename to old_src/ffi/macos/device_list.rs diff --git a/src/ffi/macos/ffi-old.rs b/old_src/ffi/macos/ffi-old.rs similarity index 100% rename from src/ffi/macos/ffi-old.rs rename to old_src/ffi/macos/ffi-old.rs diff --git a/src/ffi/macos/ffi.rs b/old_src/ffi/macos/ffi.rs similarity index 100% rename from src/ffi/macos/ffi.rs rename to old_src/ffi/macos/ffi.rs diff --git a/src/ffi/macos/microphone.rs b/old_src/ffi/macos/microphone.rs similarity index 100% rename from src/ffi/macos/microphone.rs rename to old_src/ffi/macos/microphone.rs diff --git a/src/ffi/macos/speakers.rs b/old_src/ffi/macos/speakers.rs similarity index 100% rename from src/ffi/macos/speakers.rs rename to old_src/ffi/macos/speakers.rs diff --git a/src/ffi/mod.rs b/old_src/ffi/mod.rs similarity index 100% rename from src/ffi/mod.rs rename to old_src/ffi/mod.rs diff --git a/src/ffi/none/ffi.rs b/old_src/ffi/none/ffi.rs similarity index 100% rename from src/ffi/none/ffi.rs rename to old_src/ffi/none/ffi.rs diff --git a/src/ffi/redox/ffi.rs b/old_src/ffi/redox/ffi.rs similarity index 100% rename from src/ffi/redox/ffi.rs rename to old_src/ffi/redox/ffi.rs diff --git a/src/ffi/wasm/device_list.rs b/old_src/ffi/wasm/device_list.rs similarity index 100% rename from src/ffi/wasm/device_list.rs rename to old_src/ffi/wasm/device_list.rs diff --git a/src/ffi/wasm/ffi.rs b/old_src/ffi/wasm/ffi.rs similarity index 100% rename from src/ffi/wasm/ffi.rs rename to old_src/ffi/wasm/ffi.rs diff --git a/src/ffi/wasm/microphone.rs b/old_src/ffi/wasm/microphone.rs similarity index 100% rename from src/ffi/wasm/microphone.rs rename to old_src/ffi/wasm/microphone.rs diff --git a/src/ffi/wasm/speakers.rs b/old_src/ffi/wasm/speakers.rs similarity index 100% rename from src/ffi/wasm/speakers.rs rename to old_src/ffi/wasm/speakers.rs diff --git a/src/ffi/windows/ffi.rs b/old_src/ffi/windows/ffi.rs similarity index 100% rename from src/ffi/windows/ffi.rs rename to old_src/ffi/windows/ffi.rs diff --git a/old_src/lib.rs b/old_src/lib.rs new file mode 100644 index 0000000..3f9df37 --- /dev/null +++ b/old_src/lib.rs @@ -0,0 +1,124 @@ +// Copyright © 2019-2022 The Wavy Contributors. +// +// Licensed under any of: +// - Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) +// - Boost Software License, Version 1.0 (https://www.boost.org/LICENSE_1_0.txt) +// - MIT License (https://mit-license.org/) +// At your choosing (See accompanying files LICENSE_APACHE_2_0.txt, +// LICENSE_MIT.txt and LICENSE_BOOST_1_0.txt). +// +//! Asynchronous cross-platform real-time audio recording & playback. +//! +//! # Getting Started +//! Add the following to your *Cargo.toml*: +//! +//! ```toml +//! [dependencies] +//! pasts = "0.12" +//! wavy = "0.10" +//! fon = "0.5" +//! ``` +//! +//! This example records audio and plays it back in real time as it's being +//! recorded. (Make sure to wear headphones to avoid feedback): +//! +//! ```rust +//! use fon::{mono::Mono32, Audio, Sink}; +//! use pasts::{prelude::*, Join}; +//! use wavy::{Microphone, MicrophoneStream, Speakers, SpeakersSink}; +//! +//! /// Shared state between tasks on the thread. +//! struct App { +//! /// Handle to speakers +//! speakers: Speakers<1>, +//! /// Handle to the microphone +//! microphone: Microphone<1>, +//! /// Temporary buffer for holding real-time audio samples. +//! buffer: Audio, +//! } +//! +//! impl App { +//! /// Speaker is ready to play more audio. +//! fn play(&mut self, mut sink: SpeakersSink) -> Poll<()> { +//! sink.stream(self.buffer.drain()); +//! Pending +//! } +//! +//! /// Microphone has recorded some audio. +//! fn record(&mut self, stream: MicrophoneStream) -> Poll<()> { +//! self.buffer.extend(stream); +//! Pending +//! } +//! +//! /// Program start. +//! async fn main(_executor: Executor) { +//! let speakers = Speakers::default(); +//! let microphone = Microphone::default(); +//! let buffer = Audio::with_silence(48_000, 0); +//! let mut app = App { +//! speakers, +//! microphone, +//! buffer, +//! }; +//! +//! Join::new(&mut app) +//! .on(|s| &mut s.speakers, App::play) +//! .on(|s| &mut s.microphone, App::record) +//! .await +//! } +//! } +//! ``` + +#![doc( + html_logo_url = "https://ardaku.github.io/mm/logo.svg", + html_favicon_url = "https://ardaku.github.io/mm/icon.svg", + html_root_url = "https://docs.rs/wavy" +)] +#![deny(unsafe_code)] +#![warn( + anonymous_parameters, + missing_copy_implementations, + missing_debug_implementations, + missing_docs, + nonstandard_style, + rust_2018_idioms, + single_use_lifetimes, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unused_extern_crates, + unused_qualifications, + variant_size_differences +)] + +#[cfg_attr(target_arch = "wasm32", path = "ffi/wasm/ffi.rs")] +#[cfg_attr( + not(target_arch = "wasm32"), + cfg_attr(target_os = "linux", path = "ffi/linux/ffi.rs"), + cfg_attr(target_os = "android", path = "ffi/android/ffi.rs"), + cfg_attr(target_os = "macos", path = "ffi/macos/ffi.rs"), + cfg_attr(target_os = "ios", path = "ffi/ios/ffi.rs"), + cfg_attr(target_os = "windows", path = "ffi/windows/ffi.rs"), + cfg_attr( + any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "bitrig", + target_os = "openbsd", + target_os = "netbsd" + ), + path = "ffi/bsd/ffi.rs" + ), + cfg_attr(target_os = "fuchsia", path = "ffi/fuchsia/ffi.rs"), + cfg_attr(target_os = "redox", path = "ffi/redox/ffi.rs"), + cfg_attr(target_os = "none", path = "ffi/none/ffi.rs"), + cfg_attr(target_os = "dummy", path = "ffi/dummy/ffi.rs") +)] +mod ffi; + +mod consts; +mod microphone; +mod speakers; + +pub use microphone::{Microphone, MicrophoneStream}; +pub use speakers::{Speakers, SpeakersSink}; diff --git a/src/microphone.rs b/old_src/microphone.rs similarity index 100% rename from src/microphone.rs rename to old_src/microphone.rs diff --git a/src/speakers.rs b/old_src/speakers.rs similarity index 100% rename from src/speakers.rs rename to old_src/speakers.rs diff --git a/src/lib.rs b/src/lib.rs index 3f9df37..faac59e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,124 +1,71 @@ -// Copyright © 2019-2022 The Wavy Contributors. -// -// Licensed under any of: -// - Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) -// - Boost Software License, Version 1.0 (https://www.boost.org/LICENSE_1_0.txt) -// - MIT License (https://mit-license.org/) -// At your choosing (See accompanying files LICENSE_APACHE_2_0.txt, -// LICENSE_MIT.txt and LICENSE_BOOST_1_0.txt). -// -//! Asynchronous cross-platform real-time audio recording & playback. +//! The sound waves are _so_ wavy! //! -//! # Getting Started -//! Add the following to your *Cargo.toml*: -//! -//! ```toml -//! [dependencies] -//! pasts = "0.12" -//! wavy = "0.10" -//! fon = "0.5" -//! ``` -//! -//! This example records audio and plays it back in real time as it's being -//! recorded. (Make sure to wear headphones to avoid feedback): -//! -//! ```rust -//! use fon::{mono::Mono32, Audio, Sink}; -//! use pasts::{prelude::*, Join}; -//! use wavy::{Microphone, MicrophoneStream, Speakers, SpeakersSink}; +//! # About //! -//! /// Shared state between tasks on the thread. -//! struct App { -//! /// Handle to speakers -//! speakers: Speakers<1>, -//! /// Handle to the microphone -//! microphone: Microphone<1>, -//! /// Temporary buffer for holding real-time audio samples. -//! buffer: Audio, -//! } +//! Wavy is a library for asynchronous cross-platform real-time audio recording +//! & playback. This library is great for if you need low-latency sound effects +//! in video games, if you're making a multi-media player, Digital Audio +//! Workstation, or building a synthesizer; anything that needs access to +//! speakers or microphones. //! -//! impl App { -//! /// Speaker is ready to play more audio. -//! fn play(&mut self, mut sink: SpeakersSink) -> Poll<()> { -//! sink.stream(self.buffer.drain()); -//! Pending -//! } +//! ## How it works //! -//! /// Microphone has recorded some audio. -//! fn record(&mut self, stream: MicrophoneStream) -> Poll<()> { -//! self.buffer.extend(stream); -//! Pending -//! } +//! Wavy starts up an dedicated single-threaded async executor for audio, where +//! you can run futures dealing directly with recording or playing audio. +//! Depending on the platform, it may run on a separate thread. When dealing +//! with real-time audio, it is important to make your code real-time safe +//! (avoid unbounded-time operations, such as syscalls). Communicating between +//! threads is often not real-time safe, but can be using [`DualQueue`]. //! -//! /// Program start. -//! async fn main(_executor: Executor) { -//! let speakers = Speakers::default(); -//! let microphone = Microphone::default(); -//! let buffer = Audio::with_silence(48_000, 0); -//! let mut app = App { -//! speakers, -//! microphone, -//! buffer, -//! }; -//! -//! Join::new(&mut app) -//! .on(|s| &mut s.speakers, App::play) -//! .on(|s| &mut s.microphone, App::record) -//! .await -//! } -//! } -//! ``` +//! # Getting Started + +use event_iterator::EventIterator; +use fon::{Audio, Frame}; + +/// Default preferred sample rate for audio devices +pub const DEFAULT_SAMPLE_RATE: u32 = 48_000; +/// Default preferred number of chunks in the ring buffer +pub const DEFAULT_CHUNKS: usize = 8; +/// Default preferred number of frames in a chunk +pub const DEFAULT_FRAMES: usize = 32; + +/// Default preferred audio device configuration +pub type DefaultAudioConfig = AudioConfig< + DEFAULT_SAMPLE_RATE, + DEFAULT_CHUNKS, + DEFAULT_FRAMES, +>; + +/// Configuration for an audio device +pub struct AudioConfig< + const SAMPLE_RATE: u32, + const CHUNKS: usize, + const FRAMES: usize, +>; + +/// [`EventIterator`] of [`MicrophoneStream`] +pub struct Microphone {} + +/// [`EventIterator`] of [`SpeakersSink`] +pub struct Speakers {} + +/// Chunked stream of recorded audio +pub struct MicrophoneStream {} -#![doc( - html_logo_url = "https://ardaku.github.io/mm/logo.svg", - html_favicon_url = "https://ardaku.github.io/mm/icon.svg", - html_root_url = "https://docs.rs/wavy" -)] -#![deny(unsafe_code)] -#![warn( - anonymous_parameters, - missing_copy_implementations, - missing_debug_implementations, - missing_docs, - nonstandard_style, - rust_2018_idioms, - single_use_lifetimes, - trivial_casts, - trivial_numeric_casts, - unreachable_pub, - unused_extern_crates, - unused_qualifications, - variant_size_differences -)] +/// Chunked sink for audio playback +pub struct SpeakersSink {} -#[cfg_attr(target_arch = "wasm32", path = "ffi/wasm/ffi.rs")] -#[cfg_attr( - not(target_arch = "wasm32"), - cfg_attr(target_os = "linux", path = "ffi/linux/ffi.rs"), - cfg_attr(target_os = "android", path = "ffi/android/ffi.rs"), - cfg_attr(target_os = "macos", path = "ffi/macos/ffi.rs"), - cfg_attr(target_os = "ios", path = "ffi/ios/ffi.rs"), - cfg_attr(target_os = "windows", path = "ffi/windows/ffi.rs"), - cfg_attr( - any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "openbsd", - target_os = "netbsd" - ), - path = "ffi/bsd/ffi.rs" - ), - cfg_attr(target_os = "fuchsia", path = "ffi/fuchsia/ffi.rs"), - cfg_attr(target_os = "redox", path = "ffi/redox/ffi.rs"), - cfg_attr(target_os = "none", path = "ffi/none/ffi.rs"), - cfg_attr(target_os = "dummy", path = "ffi/dummy/ffi.rs") -)] -mod ffi; +/// [`EventIterator`] of [`Microphone`] +pub struct MicrophoneSearcher { + audio_config: T, +} -mod consts; -mod microphone; -mod speakers; +/// [`EventIterator`] of [`Speakers`] +pub struct SpeakersSearcher { + audio_config: T, +} -pub use microphone::{Microphone, MicrophoneStream}; -pub use speakers::{Speakers, SpeakersSink}; +/// [`EventIterator`] to real-time share data between async executors +pub struct DualQueue { + t: T +}