diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 23af4ad8ef2..dc7beeff52c 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -5,6 +5,7 @@ + @@ -13,13 +14,14 @@ - + @@ -37,6 +39,7 @@ @@ -83,6 +86,7 @@ + @@ -91,6 +95,7 @@ + @@ -123,6 +128,7 @@ + @@ -223,6 +229,7 @@ + @@ -291,42 +298,42 @@ - - - - - - @@ -334,7 +341,6 @@ @@ -438,6 +444,7 @@ @@ -506,6 +513,7 @@ @@ -612,7 +620,7 @@ - @@ -660,6 +668,7 @@ @@ -714,21 +723,21 @@ - - - @@ -750,6 +759,7 @@ + @@ -787,6 +797,7 @@ + @@ -796,6 +807,7 @@ @@ -809,49 +821,49 @@ - - - - - - - @@ -889,6 +901,7 @@ + @@ -912,28 +925,28 @@ - - - - @@ -1126,8 +1139,8 @@ - @@ -1158,22 +1171,28 @@ - + + + + + + + @@ -1187,6 +1206,7 @@ + @@ -1197,13 +1217,12 @@ - - - - - - - + + + + + + @@ -1218,12 +1237,14 @@ + + @@ -1237,49 +1258,54 @@ - + + - - - + + + + + + - - - - - - - + + + + + + + + - - - - + + + + diff --git a/Cargo.lock b/Cargo.lock index 88ffbd357ec..d6a948784d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -263,6 +263,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46016233fc1bb55c23b856fe556b7db6ccd05119a0a392e04f0b3b7c79058f16" +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "async-trait" version = "0.1.67" @@ -343,6 +354,7 @@ dependencies = [ "lazy_static", "log", "thiserror", + "wasm_thread", "wisual-logger", ] @@ -430,6 +442,7 @@ dependencies = [ "hound", "log", "rayon", + "rubato", "samplerate", "symphonia", "symphonia-bundle-mp3", @@ -1753,6 +1766,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "concurrent-queue" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -2614,6 +2636,12 @@ dependencies = [ "num-traits 0.2.15", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "expat-sys" version = "2.1.6" @@ -4731,6 +4759,9 @@ dependencies = [ "macos-bundle-resources", "ringbuf", "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm_thread", "wisual-logger", ] @@ -6385,6 +6416,15 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "realfft" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6b8e8f0c6d2234aa58048d7290c60bf92cd36fd2888cd8331c66ad4f2e1d2" +dependencies = [ + "rustfft", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -6561,6 +6601,18 @@ dependencies = [ "wisual-logger", ] +[[package]] +name = "rubato" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd70209c27d5b08f5528bdc779ea3ffb418954e28987f9f9775c6eac41003f9c" +dependencies = [ + "num-complex 0.4.3", + "num-integer", + "num-traits 0.2.15", + "realfft", +] + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -8413,6 +8465,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm_thread" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a52fc987e67957cab58160d1ea273a2886972ef6f59c7fa0c02e9fe2c8e11706" +dependencies = [ + "async-channel", + "futures", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wayland-client" version = "0.29.5" diff --git a/crates/apps/daw/src/bridge_generated.io.rs b/crates/apps/daw/src/bridge_generated.io.rs index be3540719db..3796e965361 100644 --- a/crates/apps/daw/src/bridge_generated.io.rs +++ b/crates/apps/daw/src/bridge_generated.io.rs @@ -1,3 +1,21 @@ +// = copyright ==================================================================== +// DAW: Flutter UI for a DAW application +// Copyright (C) 2022 Pedro Tacla Yamada +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// = /copyright =================================================================== + use super::*; // Section: wire functions diff --git a/crates/apps/daw/src/bridge_generated.rs b/crates/apps/daw/src/bridge_generated.rs index 3b83eef379b..6f4490087f9 100644 --- a/crates/apps/daw/src/bridge_generated.rs +++ b/crates/apps/daw/src/bridge_generated.rs @@ -1,3 +1,21 @@ +// = copyright ==================================================================== +// DAW: Flutter UI for a DAW application +// Copyright (C) 2022 Pedro Tacla Yamada +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// = /copyright =================================================================== + #![allow( non_camel_case_types, unused, diff --git a/crates/apps/demo-plugin/src/view.rs b/crates/apps/demo-plugin/src/view.rs index cfe1873f4db..a2b597bd105 100644 --- a/crates/apps/demo-plugin/src/view.rs +++ b/crates/apps/demo-plugin/src/view.rs @@ -1,3 +1,26 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + // Augmented Audio: Audio libraries and applications use augmented::gui::iced; use augmented::gui::iced_baseview; diff --git a/crates/apps/looper/looper-processor/examples/render_audio_file.rs b/crates/apps/looper/looper-processor/examples/render_audio_file.rs index ab34db0fe22..d8f8553f082 100644 --- a/crates/apps/looper/looper-processor/examples/render_audio_file.rs +++ b/crates/apps/looper/looper-processor/examples/render_audio_file.rs @@ -1,3 +1,26 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + use cacao::appkit::window::Window; use cacao::appkit::{App, AppDelegate}; use cacao::view::View; diff --git a/crates/apps/metronome/Cargo.toml b/crates/apps/metronome/Cargo.toml index d09530e4134..7f835f11f4d 100644 --- a/crates/apps/metronome/Cargo.toml +++ b/crates/apps/metronome/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/yamadapc/augmented-audio" license = "AGPL-3.0" [lib] -crate-type = ["staticlib", "lib"] +crate-type = ["staticlib", "lib", "cdylib"] [dependencies] anyhow = "1.0.52" @@ -19,12 +19,17 @@ ringbuf = "0.2.8" wisual-logger = { path = "../../augmented/ops/wisual-logger", version = "0.1.4" } audio-processor-metronome = { path = "../../augmented/audio/audio-processor-metronome" , version = "2.0.0" } -audio-processor-standalone = { path = "../../augmented/application/audio-processor-standalone" , version = "2.0.0" } +audio-processor-standalone = { path = "../../augmented/application/audio-processor-standalone" , version = "2.0.0", default-features = false } audio-garbage-collector = { path = "../../augmented/audio/audio-garbage-collector" , version = "1.1.1" } audio-processor-file = { path = "../../augmented/audio/audio-processor-file", version = "2.3.0" } audio-processor-traits = { path = "../../augmented/audio/audio-processor-traits", version = "3.2.0" } macos-bundle-resources = { path = "../../augmented/gui/macos-bundle-resources" } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2.84" +wasm-bindgen-futures = "0.4.34" +wasm_thread = { version = "0.2.0", features = ["es_modules"] } + [build-dependencies] cbindgen = "0.24.3" flutter_rust_bridge_codegen = "1.62" diff --git a/crates/apps/metronome/src/bridge_generated.io.rs b/crates/apps/metronome/src/bridge_generated.io.rs index b4743dce413..efbad0e96b4 100644 --- a/crates/apps/metronome/src/bridge_generated.io.rs +++ b/crates/apps/metronome/src/bridge_generated.io.rs @@ -1,3 +1,21 @@ +// = copyright ==================================================================== +// Simple Metronome: macOS Metronome app +// Copyright (C) 2022 Pedro Tacla Yamada +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// = /copyright =================================================================== + #![allow(clippy::not_unsafe_ptr_arg_deref)] use super::*; // Section: wire functions diff --git a/crates/apps/metronome/src/bridge_generated.rs b/crates/apps/metronome/src/bridge_generated.rs index 25b35c12716..22344fc7cb7 100644 --- a/crates/apps/metronome/src/bridge_generated.rs +++ b/crates/apps/metronome/src/bridge_generated.rs @@ -1,3 +1,21 @@ +// = copyright ==================================================================== +// Simple Metronome: macOS Metronome app +// Copyright (C) 2022 Pedro Tacla Yamada +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// = /copyright =================================================================== + #![allow( non_camel_case_types, unused, diff --git a/crates/augmented/application/audio-processor-standalone-midi/Cargo.toml b/crates/augmented/application/audio-processor-standalone-midi/Cargo.toml index 6cc7a01d1e9..a3a66a5e796 100644 --- a/crates/augmented/application/audio-processor-standalone-midi/Cargo.toml +++ b/crates/augmented/application/audio-processor-standalone-midi/Cargo.toml @@ -10,13 +10,14 @@ repository = "https://github.com/yamadapc/augmented-audio" [features] default = [] actix = ["dep:actix"] +vst = ["dep:vst"] [dependencies] basedrop = "^0.1.2" thiserror = "^1.0.26" midir = "0.8" log = "^0.4.14" -vst = { version = "0.3", path = "../../../vendor/vst" } +vst = { version = "0.3", path = "../../../vendor/vst", optional = true } # augmented atomic-queue = { version = "1.0.1", path = "../../data/atomic-queue" } diff --git a/crates/augmented/application/audio-processor-standalone-midi/src/lib.rs b/crates/augmented/application/audio-processor-standalone-midi/src/lib.rs index 3b91808f167..5d414acdf25 100644 --- a/crates/augmented/application/audio-processor-standalone-midi/src/lib.rs +++ b/crates/augmented/application/audio-processor-standalone-midi/src/lib.rs @@ -136,6 +136,8 @@ pub mod audio_thread; pub mod constants; /// Hosting of MIDI pub mod host; + +#[cfg(feature = "vst")] /// VST API conversion pub mod vst; diff --git a/crates/augmented/application/audio-processor-standalone/Cargo.toml b/crates/augmented/application/audio-processor-standalone/Cargo.toml index 7c76c93ae19..6d2009a3c5d 100644 --- a/crates/augmented/application/audio-processor-standalone/Cargo.toml +++ b/crates/augmented/application/audio-processor-standalone/Cargo.toml @@ -9,9 +9,11 @@ repository = "https://github.com/yamadapc/augmented-audio" readme = "README.md" [features] -default = [] +default = ["midi"] gui = ["audio-processor-standalone-gui"] clap = [] +vst = ["dep:vst", "audio-processor-traits/vst", "audio-processor-standalone-midi/vst"] +midi = ["audio-processor-standalone-midi", "dep:augmented-midi"] [dependencies] # Logging & options @@ -25,18 +27,18 @@ thiserror = "^1.0.25" audio-processor-file = { path = "../../audio/audio-processor-file", version = "2.3.0" } audio-garbage-collector = { path = "../../audio/audio-garbage-collector", version = "1.1.1" } audio-processor-traits = { version = "3.2.0", path = "../../audio/audio-processor-traits" } -augmented-midi = { path = "../../data/augmented-midi", version = "1.4.0" } +augmented-midi = { path = "../../data/augmented-midi", version = "1.4.0", optional = true } # Audio basedrop = "^0.1.2" cpal = { version = "0.14.0" } ringbuf = "^0.2.5" -vst = { version = "0.3", path = "../../../vendor/vst" } +vst = { version = "0.3", path = "../../../vendor/vst", optional = true } audio-processor-standalone-gui = { path = "../audio-processor-standalone-gui", optional = true , version = "0.7.0" } [target.'cfg(not(target_os = "ios"))'.dependencies] -audio-processor-standalone-midi = { version = "1.4.0", path = "../audio-processor-standalone-midi" } +audio-processor-standalone-midi = { version = "1.4.0", path = "../audio-processor-standalone-midi", optional = true } [dev-dependencies] atomic-queue = { version = "1.0.1", path = "../../data/atomic-queue" } diff --git a/crates/augmented/application/audio-processor-standalone/examples/processor_function.rs b/crates/augmented/application/audio-processor-standalone/examples/processor_function.rs index 4f076741929..0c08508c147 100644 --- a/crates/augmented/application/audio-processor-standalone/examples/processor_function.rs +++ b/crates/augmented/application/audio-processor-standalone/examples/processor_function.rs @@ -1,3 +1,26 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + use audio_processor_traits::combinators::mono_generator_function; use audio_processor_traits::BufferProcessor; use std::f32::consts::PI; diff --git a/crates/augmented/application/audio-processor-standalone/src/lib.rs b/crates/augmented/application/audio-processor-standalone/src/lib.rs index a0edff2e6c8..d1bab5b31cc 100644 --- a/crates/augmented/application/audio-processor-standalone/src/lib.rs +++ b/crates/augmented/application/audio-processor-standalone/src/lib.rs @@ -127,7 +127,7 @@ pub mod standalone_processor; pub mod offline; /// VST support (VST is not compiled for iOS) -#[cfg(not(target_os = "ios"))] +#[cfg(all(feature = "vst", not(target_os = "ios")))] pub mod standalone_vst; #[cfg(feature = "clap")] @@ -205,6 +205,7 @@ fn standalone_main(mut app: SP, handle: Option<&Handle> input_file: input_path, output_file: output_path, } => { + #[cfg(feature = "midi")] let midi_input_file = options.midi().input_file.as_ref().map(|midi_input_file| { let file_contents = std::fs::read(midi_input_file).expect("Failed to read input MIDI file"); @@ -219,6 +220,7 @@ fn standalone_main(mut app: SP, handle: Option<&Handle> handle, input_path, output_path, + #[cfg(feature = "midi")] midi_input_file, }); } diff --git a/crates/augmented/application/audio-processor-standalone/src/offline.rs b/crates/augmented/application/audio-processor-standalone/src/offline.rs index 0b0de98cbdf..7c2d316b64a 100644 --- a/crates/augmented/application/audio-processor-standalone/src/offline.rs +++ b/crates/augmented/application/audio-processor-standalone/src/offline.rs @@ -1,5 +1,3 @@ -use itertools::Itertools; - // Augmented Audio: Audio libraries and applications // Copyright (c) 2022 Pedro Tacla Yamada // @@ -22,11 +20,17 @@ use itertools::Itertools; // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. + use audio_garbage_collector::Handle; use audio_processor_traits::{ - audio_buffer::VecAudioBuffer, AudioBuffer, AudioContext, AudioProcessor, - AudioProcessorSettings, MidiEventHandler, MidiMessageLike, + audio_buffer::VecAudioBuffer, AudioBuffer, AudioContext, AudioProcessor, AudioProcessorSettings, }; +#[cfg(feature = "midi")] +use audio_processor_traits::{MidiEventHandler, MidiMessageLike}; +#[cfg(feature = "midi")] +use itertools::Itertools; + +#[cfg(feature = "midi")] use augmented_midi::{ MIDIFile, MIDIFileChunk, MIDIMessage, MIDIMessageNote, MIDITrackEvent, MIDITrackInner, }; @@ -43,6 +47,7 @@ pub struct OfflineRenderOptions<'a, Processor: StandaloneProcessor> { pub input_path: &'a str, /// Output audio file path pub output_path: &'a str, + #[cfg(feature = "midi")] /// MIDI input file path pub midi_input_file: Option>>, } @@ -57,6 +62,7 @@ where handle, input_path, output_path, + #[cfg(feature = "midi")] midi_input_file, } = options; @@ -109,6 +115,7 @@ where app.processor() .prepare(&mut context, audio_processor_settings); + #[cfg(feature = "midi")] let midi_input_blocks = midi_input_file.map(|midi_input_file| { build_midi_input_blocks(&audio_processor_settings, total_blocks, midi_input_file) }); @@ -121,6 +128,7 @@ where audio_file_processor.process(&mut context, &mut buffer); + #[cfg(feature = "midi")] if let Some(midi) = app.midi() { if let Some(midi_input_blocks) = &midi_input_blocks { let midi_block = &midi_input_blocks[block_num]; @@ -130,17 +138,21 @@ where } } } + #[cfg(not(feature = "midi"))] + std::mem::forget(block_num); // suppress unused error app.processor().process(&mut context, &mut buffer); output_file_processor.process(buffer.slice_mut()); } } +#[cfg(feature = "midi")] #[derive(Debug)] struct MIDIBytes { bytes: Vec, } +#[cfg(feature = "midi")] impl MidiMessageLike for MIDIBytes { fn is_midi(&self) -> bool { true @@ -151,6 +163,7 @@ impl MidiMessageLike for MIDIBytes { } } +#[cfg(feature = "midi")] /// Converts a MIDI stream's delta_time into absolute ticks. fn convert_to_absolute_time( mut events: Vec>>, @@ -163,6 +176,7 @@ fn convert_to_absolute_time( events } +#[cfg(feature = "midi")] /// Builds chunks containing MIDI messages over each block, aligned with their /// timing and a 120bpm tempo. fn build_midi_input_blocks( @@ -242,6 +256,7 @@ fn build_midi_input_blocks( result } +#[cfg(feature = "midi")] /// Returns the number of elapsed MIDI ticks based on the current block index fn get_delta_time_ticks( tempo: f32, @@ -285,11 +300,13 @@ mod test { handle: Some(audio_garbage_collector::handle()), input_path: &input_path, output_path: &output_path, + #[cfg(feature = "midi")] midi_input_file: None, }; run_offline_render(options); } + #[cfg(feature = "midi")] #[test] fn test_build_midi_input_blocks_with_no_blocks() { let chunks = vec![ @@ -328,6 +345,7 @@ mod test { assert_eq!(result.len(), 0); } + #[cfg(feature = "midi")] #[test] fn test_build_midi_input_blocks() { let chunks = vec![ @@ -379,6 +397,7 @@ mod test { assert_eq!(result[20].len(), 1); } + #[cfg(feature = "midi")] #[test] fn test_get_delta_time_ticks() { let delta_time_ticks = get_delta_time_ticks( diff --git a/crates/augmented/application/audio-processor-standalone/src/standalone_clap.rs b/crates/augmented/application/audio-processor-standalone/src/standalone_clap.rs index b6d70909325..bf63f98dad4 100644 --- a/crates/augmented/application/audio-processor-standalone/src/standalone_clap.rs +++ b/crates/augmented/application/audio-processor-standalone/src/standalone_clap.rs @@ -1,3 +1,26 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + use std::ffi::CStr; pub use clack_plugin; diff --git a/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/audio_thread/mod.rs b/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/audio_thread/mod.rs index a15f4d331e4..e61f8f08d01 100644 --- a/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/audio_thread/mod.rs +++ b/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/audio_thread/mod.rs @@ -31,16 +31,18 @@ use audio_processor_traits::{AudioContext, AudioProcessor, AudioProcessorSetting use crate::StandaloneProcessor; +#[cfg(feature = "midi")] +use super::midi::MidiContext; use super::{ - error::AudioThreadError, input_handling, midi::MidiContext, options, output_handling, - IOConfiguration, ResolvedStandaloneConfiguration, + error::AudioThreadError, input_handling, options, output_handling, IOConfiguration, + ResolvedStandaloneConfiguration, }; pub fn audio_thread_main( host: Host, host_name: String, mut app: SP, - midi_context: Option, + #[cfg(feature = "midi")] midi_context: Option, configuration_tx: Sender, stop_signal_rx: Receiver<()>, ) -> Result<(), AudioThreadError> { @@ -119,6 +121,7 @@ pub fn audio_thread_main( num_input_channels, }, cpal_streams, + #[cfg(feature = "midi")] midi_context, }; @@ -149,6 +152,7 @@ struct AudioThreadCPalStreams { } struct AudioThreadRunParams { + #[cfg(feature = "midi")] midi_context: Option, io_hints: AudioThreadIOHints, cpal_streams: AudioThreadCPalStreams, @@ -166,6 +170,7 @@ fn audio_thread_run_processor( ); let AudioThreadRunParams { + #[cfg(feature = "midi")] midi_context, io_hints, cpal_streams, @@ -193,6 +198,7 @@ fn audio_thread_run_processor( let audio_context = AudioContext::default(); let output_stream = output_handling::build_output_stream(BuildOutputStreamParams { app, + #[cfg(feature = "midi")] midi_context, audio_context, num_output_channels, diff --git a/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/mod.rs b/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/mod.rs index 1522d5c69ac..66d3de9723f 100644 --- a/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/mod.rs +++ b/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/mod.rs @@ -33,18 +33,22 @@ use crate::standalone_processor::{ StandaloneAudioOnlyProcessor, StandaloneProcessor, StandaloneProcessorImpl, }; -use self::midi::{initialize_midi_host, MidiReference}; pub use self::mock_cpal::virtual_host::{VirtualHost, VirtualHostDevice, VirtualHostStream}; pub use self::options::AudioIOMode; +#[cfg(feature = "midi")] +use self::midi::{initialize_midi_host, MidiReference}; + mod audio_thread; mod error; mod input_handling; -mod midi; pub mod mock_cpal; mod options; mod output_handling; +#[cfg(feature = "midi")] +mod midi; + /// Start an [`AudioProcessor`] / [`MidiEventHandler`] as a stand-alone cpal app and forward MIDI /// messages received on all inputs to it. /// @@ -140,6 +144,7 @@ pub struct StandaloneHandles { // Handles contain a join handle with the thread, this might be used in the future. handle: Option>, stop_signal_tx: Sender<()>, + #[cfg(feature = "midi")] #[allow(unused)] midi_reference: MidiReference, } @@ -193,18 +198,21 @@ pub fn standalone_start_with< SP: StandaloneProcessor, Host: cpal::traits::HostTrait + Send + 'static, >( - mut app: SP, + #[allow(unused_mut)] mut app: SP, options: StandaloneStartOptions, ) -> StandaloneHandles { let StandaloneStartOptions { - handle, host, host_name, + handle, } = options; let _ = wisual_logger::try_init_from_env(); + #[cfg(feature = "midi")] let (midi_reference, midi_context) = initialize_midi_host(&mut app, handle.as_ref()); + #[cfg(not(feature = "midi"))] + std::mem::forget(handle); // suppress unused error let (configuration_tx, configuration_rx) = channel(); let (stop_signal_tx, stop_signal_rx) = channel(); @@ -217,6 +225,7 @@ pub fn standalone_start_with< host, host_name, app, + #[cfg(feature = "midi")] midi_context, configuration_tx, stop_signal_rx, @@ -235,6 +244,7 @@ pub fn standalone_start_with< configuration, handle: Some(handle), stop_signal_tx, + #[cfg(feature = "midi")] midi_reference, } } diff --git a/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/output_handling.rs b/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/output_handling.rs index 7920673f41a..2ed83e5edb9 100644 --- a/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/output_handling.rs +++ b/crates/augmented/application/audio-processor-standalone/src/standalone_cpal/output_handling.rs @@ -34,10 +34,12 @@ use audio_processor_traits::{AudioBuffer, AudioContext, AudioProcessor, Interlea use crate::StandaloneProcessor; use super::error::AudioThreadError; +#[cfg(feature = "midi")] use super::midi::MidiContext; pub struct BuildOutputStreamParams { pub app: SP, + #[cfg(feature = "midi")] pub midi_context: Option, pub audio_context: AudioContext, pub num_output_channels: usize, @@ -53,6 +55,7 @@ pub fn build_output_stream( ) -> Result { let BuildOutputStreamParams { mut app, + #[cfg(feature = "midi")] mut midi_context, mut audio_context, num_output_channels, @@ -73,6 +76,7 @@ pub fn build_output_stream( &output_config, move |data: &mut [f32], _output_info: &cpal::OutputCallbackInfo| { output_stream_with_context(OutputStreamFrameContext { + #[cfg(feature = "midi")] midi_context: midi_context.as_mut(), audio_context: &mut audio_context, processor: &mut app, @@ -93,6 +97,7 @@ pub fn build_output_stream( /// Data borrowed to process a single output frame. struct OutputStreamFrameContext<'a, SP: StandaloneProcessor> { + #[cfg(feature = "midi")] midi_context: Option<&'a mut MidiContext>, audio_context: &'a mut AudioContext, processor: &'a mut SP, @@ -107,6 +112,7 @@ struct OutputStreamFrameContext<'a, SP: StandaloneProcessor> { /// This will be called repeatedly for every audio buffer we must produce. fn output_stream_with_context(context: OutputStreamFrameContext) { let OutputStreamFrameContext { + #[cfg(feature = "midi")] midi_context, audio_context, processor, @@ -140,6 +146,7 @@ fn output_stream_with_context(context: OutputStreamFram } // Collect MIDI + #[cfg(feature = "midi")] super::midi::flush_midi_events(midi_context, processor); processor @@ -191,6 +198,7 @@ mod test { consumer: &mut consumer, num_output_channels: 1, num_input_channels: 1, + #[cfg(feature = "midi")] midi_context: None, data: &mut data, audio_context: &mut Default::default(), diff --git a/crates/augmented/audio/audio-garbage-collector/Cargo.toml b/crates/augmented/audio/audio-garbage-collector/Cargo.toml index 6db70f0ed16..4e3a5b3cc8c 100644 --- a/crates/augmented/audio/audio-garbage-collector/Cargo.toml +++ b/crates/augmented/audio/audio-garbage-collector/Cargo.toml @@ -13,6 +13,9 @@ thiserror = "^1.0.26" basedrop = "^0.1.2" log = "^0.4.14" +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm_thread = { version = "0.2.0", features = ["es_modules"] } + [dev-dependencies] wisual-logger = { version = "^0.1", path = "../../ops/wisual-logger" } diff --git a/crates/augmented/audio/audio-garbage-collector/src/lib.rs b/crates/augmented/audio/audio-garbage-collector/src/lib.rs index 7bae901fb53..cbad3b80bff 100644 --- a/crates/augmented/audio/audio-garbage-collector/src/lib.rs +++ b/crates/augmented/audio/audio-garbage-collector/src/lib.rs @@ -33,7 +33,6 @@ //! adds some small overhead. use std::sync::{Arc, Mutex}; -use std::thread::JoinHandle; use std::time::Duration; use basedrop::Collector; @@ -41,6 +40,11 @@ pub use basedrop::{Handle, Owned, Shared, SharedCell}; use lazy_static::lazy_static; use thiserror::Error; +#[cfg(not(target_arch = "wasm32"))] +use std::thread; +#[cfg(target_arch = "wasm32")] +use wasm_thread as thread; + lazy_static! { static ref GARBAGE_COLLECTOR: GarbageCollector = GarbageCollector::default(); } @@ -90,7 +94,7 @@ struct GarbageCollectorState { pub struct GarbageCollector { collector: Arc>, state: Arc>, - thread: Option>, + thread: Option>, handle: Handle, } @@ -115,10 +119,10 @@ impl GarbageCollector { let thread = { let collector = collector.clone(); let state = state.clone(); - std::thread::Builder::new() + thread::Builder::new() .name(String::from("gc-thread")) .spawn(move || run_collector_loop(collector, state)) - .unwrap() + .expect("Failed to start GC thread") }; GarbageCollector { diff --git a/crates/augmented/audio/audio-processor-file/Cargo.toml b/crates/augmented/audio/audio-processor-file/Cargo.toml index 88d77328e9d..3a21dfa8073 100644 --- a/crates/augmented/audio/audio-processor-file/Cargo.toml +++ b/crates/augmented/audio/audio-processor-file/Cargo.toml @@ -7,6 +7,11 @@ license = "MIT" homepage = "https://github.com/yamadapc/augmented-audio" repository = "https://github.com/yamadapc/augmented-audio" +[features] +default = ["rubato"] +rubato = ["dep:rubato"] +samplerate = ["dep:samplerate", "dep:augmented-convert-sample-rate"] + [dependencies] # Error / Logging log = "^0.4.14" @@ -20,13 +25,14 @@ symphonia = { version = "0.5.1", features = ["mp3", "wav", "flac", "isomp4", "aa symphonia-bundle-mp3 = "0.5.1" symphonia-format-wav = "0.5.1" hound = "^3.4.0" -samplerate = "0.2.4" +samplerate = { version = "0.2.4", optional = true } +rubato = { version = "0.12.0", optional = true } +augmented-convert-sample-rate = { path = "../../../augmented/dsp/convert-sample-rate" , version = "1.4.0", optional = true } # Augmented audio-garbage-collector = { path = "../../../augmented/audio/audio-garbage-collector" , version = "1.1.1" } -audio-processor-traits = { version = "3.2.0", path = "../../../augmented/audio/audio-processor-traits" } +audio-processor-traits = { version = "3.2.0", path = "../../../augmented/audio/audio-processor-traits", default-features = false } augmented-audio-metrics = { path = "../../ops/augmented-metrics" , version = "1.5.0" } -augmented-convert-sample-rate = { path = "../../../augmented/dsp/convert-sample-rate" , version = "1.4.0" } cpal = "0.14.0" diff --git a/crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io.rs b/crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io/mod.rs similarity index 74% rename from crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io.rs rename to crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io/mod.rs index fa6c6e46db0..953aabe9e3b 100644 --- a/crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io.rs +++ b/crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io/mod.rs @@ -24,10 +24,9 @@ use std::fs::File; use std::path::Path; -use audio_processor_traits::VecAudioBuffer; -use samplerate::Samplerate; +use audio_processor_traits::{AudioBuffer, OwnedAudioBuffer, VecAudioBuffer}; +use symphonia::core::audio::Signal; use symphonia::core::audio::{AudioBuffer as SymphoniaAudioBuffer, AudioBufferRef}; -use symphonia::core::audio::{AudioBuffer, Signal}; use symphonia::core::codecs::Decoder; use symphonia::core::formats::FormatOptions; use symphonia::core::io::MediaSourceStream; @@ -36,8 +35,11 @@ use symphonia::core::probe::{Hint, ProbeResult}; use symphonia::default::get_probe; use thiserror::Error; +use crate::file_io::sample_rate_converter::BLOCK_SIZE; use augmented_audio_metrics as metrics; +mod sample_rate_converter; + #[derive(Error, Debug)] pub enum AudioFileError { #[error("Failed to decode input file")] @@ -148,7 +150,7 @@ pub fn read_file_contents( )) } -pub fn convert_audio_buffer_sample_type(audio_buffer: AudioBufferRef) -> AudioBuffer { +pub fn convert_audio_buffer_sample_type(audio_buffer: AudioBufferRef) -> SymphoniaAudioBuffer { let mut destination = SymphoniaAudioBuffer::new(audio_buffer.capacity() as u64, *audio_buffer.spec()); let _ = destination.fill(|_, _| Ok(())); @@ -171,25 +173,69 @@ pub fn convert_audio_buffer_sample_type(audio_buffer: AudioBufferRef) -> AudioBu destination } -pub struct ConvertedFileContentsStream<'a> { +struct FileFramesStream<'a> { audio_file_stream: FileContentsStream<'a>, + buffer: Vec>, + rate: u32, + buffer_size: usize, +} + +impl<'a> Iterator for FileFramesStream<'a> { + type Item = Vec>; + + fn next(&mut self) -> Option { + while let Some(block) = self.audio_file_stream.next() { + let channels = block.spec().channels.count(); + self.rate = block.spec().rate; + self.buffer.resize(channels, Vec::new()); + for channel in 0..channels { + self.buffer[channel].extend_from_slice(block.chan(channel)); + } + + if self.buffer[0].len() >= self.buffer_size { + let mut result = Vec::new(); + for channel in 0..self.buffer.len() { + let channel_buffer = self.buffer[channel].drain(0..self.buffer_size).collect(); + result.push(channel_buffer); + } + return Some(result); + } + } + + if self.buffer[0].len() > 0 { + let mut result = Vec::new(); + for channel in 0..self.buffer.len() { + let mut channel_buffer: Vec = self.buffer[channel].drain(..).collect(); + channel_buffer.resize(self.buffer_size, 0.0); + result.push(channel_buffer); + } + return Some(result); + } + None + } +} + +pub struct ConvertedFileContentsStream<'a> { + audio_file_stream: FileFramesStream<'a>, output_rate: f32, - decoder: Option>, + decoder: Option, } impl<'a> ConvertedFileContentsStream<'a> { - fn get_decoder(&mut self, from_rate: u32, channels: usize) -> Option<&mut Samplerate> { + fn get_decoder( + &mut self, + from_rate: u32, + channels: usize, + ) -> Option<&mut sample_rate_converter::Decoder> { if self.decoder.is_none() { - self.decoder = Some(samplerate::Samplerate::new( - samplerate::ConverterType::SincBestQuality, - from_rate, - self.output_rate as u32, - channels, - )); + self.decoder = Some( + sample_rate_converter::make_decoder(from_rate, self.output_rate as u32, channels) + .unwrap(), + ); } - if let Some(Ok(decoder)) = &mut self.decoder { + if let Some(decoder) = &mut self.decoder { Some(decoder) } else { None @@ -201,30 +247,30 @@ impl<'a> Iterator for ConvertedFileContentsStream<'a> { type Item = VecAudioBuffer; fn next(&mut self) -> Option { - let audio_buffer = self.audio_file_stream.next()?; - let num_samples = audio_buffer.frames(); - let num_channels = audio_buffer.spec().channels.count(); - let decoder = self.get_decoder(audio_buffer.spec().rate, num_channels)?; - - let mut interleaved_buffer = vec![]; - interleaved_buffer.resize(num_samples * num_channels, 0.0); - let channels: Vec<&[f32]> = (0..num_channels).map(|c| audio_buffer.chan(c)).collect(); - for sample in 0..num_samples { - #[allow(clippy::needless_range_loop)] - for channel in 0..num_channels { - let index = sample * num_channels + channel; - interleaved_buffer[index] = channels[channel][sample]; + let channels = self.audio_file_stream.next()?; + let rate = self.audio_file_stream.rate; + + let decoder = self.get_decoder(rate, channels.len()).unwrap(); + + assert_eq!(channels.len(), 2); + assert_eq!(channels[0].len(), BLOCK_SIZE); + let chunk_result = sample_rate_converter::process(decoder, &channels).unwrap(); + + let mut interleaved_result = VecAudioBuffer::new(); + interleaved_result.resize(chunk_result.len(), chunk_result[0].len(), 0.0); + for sample in 0..chunk_result[0].len() { + for channel in 0..chunk_result.len() { + interleaved_result.set(channel, sample, chunk_result[channel][sample]); } } - let result = decoder.process(&interleaved_buffer).ok()?; - Some(VecAudioBuffer::new_with(result, num_channels, num_samples)) + Some(interleaved_result) } } impl<'a> ExactSizeIterator for ConvertedFileContentsStream<'a> { fn len(&self) -> usize { - self.audio_file_stream.len() + self.audio_file_stream.audio_file_stream.len() } } @@ -233,12 +279,18 @@ pub fn convert_audio_file_stream_sample_rate( output_rate: f32, ) -> ConvertedFileContentsStream { ConvertedFileContentsStream { - audio_file_stream, + audio_file_stream: FileFramesStream { + audio_file_stream, + buffer: Vec::new(), + rate: 0, + buffer_size: BLOCK_SIZE, + }, output_rate, decoder: None, } } +#[cfg(feature = "samplerate")] pub fn convert_audio_file_sample_rate( audio_file_contents: &SymphoniaAudioBuffer, output_rate: f32, diff --git a/crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io/sample_rate_converter.rs b/crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io/sample_rate_converter.rs new file mode 100644 index 00000000000..485f319cae7 --- /dev/null +++ b/crates/augmented/audio/audio-processor-file/src/audio_file_processor/file_io/sample_rate_converter.rs @@ -0,0 +1,109 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#[cfg(feature = "samplerate")] +pub use samplerate_impl::*; + +#[cfg(all(feature = "rubato", not(feature = "samplerate")))] +pub use rubato_impl::*; + +#[cfg(feature = "samplerate")] +mod samplerate_impl { + pub type Decoder = samplerate::Samplerate; + pub type DecoderError = samplerate::Error; + + pub fn make_decoder( + input_rate: u32, + output_rate: u32, + channels: usize, + ) -> Result { + samplerate::Samplerate::new( + samplerate::ConverterType::SincBestQuality, + input_rate, + output_rate, + channels, + ) + } + + pub fn process>( + decoder: &mut Decoder, + block: &Vec, + ) -> Result>, DecoderError> { + let num_channels = block.len(); + let num_samples = block[0].as_ref().len(); + + let mut interleaved_buffer = vec![]; + interleaved_buffer.resize(num_channels * num_samples, 0.0); + for sample in 0..num_samples { + #[allow(clippy::needless_range_loop)] + for channel in 0..num_channels { + let index = sample * num_channels + channel; + interleaved_buffer[index] = block[channel].as_ref()[sample]; + } + } + + let result = decoder.process(&interleaved_buffer)?; + + let mut deinterleaved_buffer = vec![]; + deinterleaved_buffer.resize(num_channels, vec![]); + for sample in 0..result.len() { + let channel = sample % num_channels; + deinterleaved_buffer[channel].push(result[sample]); + } + Ok(deinterleaved_buffer) + } +} + +pub const BLOCK_SIZE: usize = 1024; + +#[cfg(feature = "rubato")] +#[allow(unused)] +pub mod rubato_impl { + use crate::file_io::sample_rate_converter::BLOCK_SIZE; + use rubato::Resampler; + + pub type Decoder = rubato::FftFixedIn; + pub type DecoderError = rubato::ResampleError; + pub type DecoderCreateError = rubato::ResamplerConstructionError; + + pub fn make_decoder( + input_rate: u32, + output_rate: u32, + channels: usize, + ) -> Result { + Decoder::new( + input_rate as usize, + output_rate as usize, + BLOCK_SIZE, + 2, + channels, + ) + } + + pub fn process>( + decoder: &mut Decoder, + block: &Vec, + ) -> Result>, DecoderError> { + decoder.process(block, None) + } +} diff --git a/crates/augmented/audio/audio-processor-file/src/audio_file_processor/mod.rs b/crates/augmented/audio/audio-processor-file/src/audio_file_processor/mod.rs index d5e96777376..fe9b86bbef6 100644 --- a/crates/augmented/audio/audio-processor-file/src/audio_file_processor/mod.rs +++ b/crates/augmented/audio/audio-processor-file/src/audio_file_processor/mod.rs @@ -23,14 +23,12 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::time::Instant; -use rayon::prelude::*; use symphonia::core::probe::ProbeResult; use audio_garbage_collector::{Handle, Shared}; -use audio_processor_traits::{ - AudioBuffer, AudioContext, AudioProcessor, AudioProcessorSettings, OwnedAudioBuffer, - VecAudioBuffer, -}; +#[cfg(feature = "samplerate")] +use audio_processor_traits::OwnedAudioBuffer; +use audio_processor_traits::{AudioBuffer, AudioContext, AudioProcessor, AudioProcessorSettings}; use file_io::AudioFileError; pub mod file_io; @@ -50,10 +48,13 @@ impl InMemoryAudioFile { /// Eagerly read the file onto memory, do sample rate conversion into the target /// AudioProcessorSettings and return a VecAudioBuffer containing the file's contents. + #[cfg(feature = "samplerate")] pub fn read_into_vec_audio_buffer( &mut self, settings: &AudioProcessorSettings, - ) -> Result, AudioFileError> { + ) -> Result, AudioFileError> { + use rayon::prelude::*; + let output_rate = settings.sample_rate(); let contents = file_io::read_file_contents(&mut self.audio_file)?; let converted_channels: Vec> = (0..contents.spec().channels.count()) @@ -63,7 +64,7 @@ impl InMemoryAudioFile { }) .collect(); - let mut output_buffer = VecAudioBuffer::new(); + let mut output_buffer = audio_processor_traits::VecAudioBuffer::new(); output_buffer.resize(settings.output_channels(), converted_channels[0].len(), 0.0); for (channel_index, channel) in converted_channels.iter().enumerate() { for (sample_index, sample) in channel.iter().enumerate() { diff --git a/crates/augmented/audio/audio-processor-metronome/Cargo.toml b/crates/augmented/audio/audio-processor-metronome/Cargo.toml index e651ad55520..a518f553b3a 100644 --- a/crates/augmented/audio/audio-processor-metronome/Cargo.toml +++ b/crates/augmented/audio/audio-processor-metronome/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] augmented_oscillator = { path = "../oscillator" , version = "1.2.1" } audio-processor-traits = { path = "../audio-processor-traits" , version = "3.2.0" } -audio-processor-standalone = { path = "../../application/audio-processor-standalone" , version = "2.0.0" } +audio-processor-standalone = { path = "../../application/audio-processor-standalone" , version = "2.0.0", default-features = false } audio-garbage-collector = { path = "../audio-garbage-collector" , version = "1.1.1" } augmented-playhead = { path = "../../data/augmented-playhead" , version = "0.5.0" } augmented-adsr-envelope = { path = "../adsr-envelope" , version = "0.4.0" } diff --git a/crates/augmented/audio/audio-processor-traits/Cargo.toml b/crates/augmented/audio/audio-processor-traits/Cargo.toml index 7209648ee66..c747f656d18 100644 --- a/crates/augmented/audio/audio-processor-traits/Cargo.toml +++ b/crates/augmented/audio/audio-processor-traits/Cargo.toml @@ -9,8 +9,8 @@ homepage = "https://github.com/yamadapc/augmented-audio" repository = "https://github.com/yamadapc/augmented-audio" [features] -default = ["vst_support"] -vst_support = ["vst"] +default = [] +vst = ["dep:vst"] [dependencies] num = "^0.4.0" diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer.rs deleted file mode 100644 index e5b015a2708..00000000000 --- a/crates/augmented/audio/audio-processor-traits/src/audio_buffer.rs +++ /dev/null @@ -1,420 +0,0 @@ -// Augmented Audio: Audio libraries and applications -// Copyright (c) 2022 Pedro Tacla Yamada -// -// The MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -use std::slice::{Chunks, ChunksMut}; - -use num::Float; - -/// Represents an audio buffer. This decouples audio processing code from a certain representation -/// of multi-channel sample buffers. -/// -/// This crate provides implementations of this trait for CPal style buffers, which use interleaved -/// internal representation. -/// -/// When processing samples, it'll be more efficient to use `.slice` and `.slice_mut` than `.get` / -/// `.set` methods. For the VST buffer, these methods will not work. -/// -/// It's recommended to convert the buffer into interleaved layout before processing as that will be -/// around as expensive as the overhead of `get`/`set` methods on a single loop through samples. -/// -/// (due to bounds checking and other compiler optimisations that fail with them) -pub trait AudioBuffer { - /// The type of samples within this buffer. - type SampleType; - - /// The number of channels in this buffer - fn num_channels(&self) -> usize; - - /// The number of samples in this buffer - fn num_samples(&self) -> usize; - - /// Get a slice to the internal data. Will not work with VST adapter - /// - /// This is the faster way to process - fn slice(&self) -> &[Self::SampleType]; - - /// Get a mutable slice to the internal data. Will not work with VST adapter - /// - /// This is the faster way to process - fn slice_mut(&mut self) -> &mut [Self::SampleType]; - - /// Shortcut for `.slice().chunks(num_channels)` - fn frames(&self) -> Chunks<'_, Self::SampleType> { - self.slice().chunks(self.num_channels()) - } - - /// Shortcut for `.slice_mut().chunks_mut(num_channels)` - /// - /// This is a frame representing a sample in time, for all - /// channels. - fn frames_mut(&mut self) -> ChunksMut<'_, Self::SampleType> { - let channels = self.num_channels(); - self.slice_mut().chunks_mut(channels) - } - - /// Get a ref to an INPUT sample in this buffer. - /// - /// Calling this on a loop will be ~20x slower than reading from `slice`. - fn get(&self, channel: usize, sample: usize) -> &Self::SampleType; - - /// Get a mutable ref to an OUTPUT sample in this buffer - /// - /// On some implementations this may yield a different value than `.get`. - /// - /// Calling this on a loop will be ~20x slower than reading from `slice`. - fn get_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType; - - /// Set an OUTPUT sample in this buffer - fn set(&mut self, channel: usize, sample: usize, value: Self::SampleType); - - /// Unsafe, no bounds check - Get a ref to an INPUT sample in this buffer - /// - /// Calling this on a loop will be ~10x slower than reading from `slice`. - /// - /// # Safety - /// This performs no bounds checks. Make sure indexes are in range. - unsafe fn get_unchecked(&self, channel: usize, sample: usize) -> &Self::SampleType { - self.get(channel, sample) - } - - /// Unsafe, no bounds check - Get a mutable ref to an OUTPUT sample in this buffer - /// - /// On some implementations this may yield a different value than `.get`. - /// - /// Calling this on a loop will be ~10x slower than reading from `slice`. - /// - /// # Safety - /// This performs no bounds checks. Make sure indexes are in range. - unsafe fn get_unchecked_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType { - self.get_mut(channel, sample) - } - - /// Unsafe, no bounds check - Set an OUTPUT sample in this buffer - /// - /// Calling this on a loop will be ~10x slower than reading from `slice`. - /// - /// # Safety - /// This performs no bounds checks. Make sure indexes are in range. - unsafe fn set_unchecked(&mut self, channel: usize, sample: usize, value: Self::SampleType) { - self.set(channel, sample, value) - } -} - -/// Set all samples of an AudioBuffer to a constant -pub fn set_all(buf: &mut Buffer, value: SampleType) -where - Buffer: AudioBuffer, - SampleType: Clone, -{ - for sample in buf.slice_mut() { - *sample = value.clone(); - } -} - -/// Set all samples of an AudioBuffer to Zero::zero -pub fn clear(buf: &mut Buffer) -where - Buffer: AudioBuffer, - SampleType: num::Zero, -{ - for sample in buf.slice_mut() { - *sample = SampleType::zero(); - } -} - -/// An AudioBuffer that stores samples as interleaved frames, used for [`cpal`] compatibility. -/// -/// Example layout: -/// -/// [ -/// 0, 0, // <- left_sample, right_sample, -/// ..., -/// ] -pub struct InterleavedAudioBuffer<'a, SampleType> { - num_channels: usize, - inner: &'a mut [SampleType], -} - -impl<'a, SampleType> InterleavedAudioBuffer<'a, SampleType> { - pub fn new(num_channels: usize, inner: &'a mut [SampleType]) -> Self { - Self { - num_channels, - inner, - } - } -} - -impl<'a, SampleType> AudioBuffer for InterleavedAudioBuffer<'a, SampleType> { - type SampleType = SampleType; - - #[inline] - fn num_channels(&self) -> usize { - self.num_channels - } - - #[inline] - fn num_samples(&self) -> usize { - self.inner.len() / self.num_channels - } - - #[inline] - fn slice(&self) -> &[Self::SampleType] { - self.inner - } - - #[inline] - fn slice_mut(&mut self) -> &mut [Self::SampleType] { - self.inner - } - - #[inline] - fn get(&self, channel: usize, sample: usize) -> &SampleType { - &self.inner[sample * self.num_channels + channel] - } - - #[inline] - fn get_mut(&mut self, channel: usize, sample: usize) -> &mut SampleType { - &mut self.inner[sample * self.num_channels + channel] - } - - #[inline] - fn set(&mut self, channel: usize, sample: usize, value: SampleType) { - let sample_ref = self.get_mut(channel, sample); - *sample_ref = value; - } -} - -/// A trait for buffer types that own the data they hold & can be constructed / resized. -pub trait OwnedAudioBuffer: AudioBuffer { - /// Create an empty buffer of this type - fn new() -> Self; - /// Resize the buffer to fit `num_channels` and `num_samples` - fn resize(&mut self, num_channels: usize, num_samples: usize, sample: Self::SampleType); -} - -/// An owned version of the interleaved buffer implementation. Can be converted onto an -/// `InterleavedAudioBuffer`. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct VecAudioBuffer { - buffer: Vec, - num_channels: usize, - num_samples: usize, -} - -impl VecAudioBuffer { - pub fn new_with(buffer: Vec, num_channels: usize, num_samples: usize) -> Self { - Self { - buffer, - num_samples, - num_channels, - } - } - - pub fn empty_with(num_channels: usize, num_samples: usize, value: SampleType) -> Self { - let mut result = Self::new(); - result.resize(num_channels, num_samples, value); - result - } -} - -impl From> for VecAudioBuffer { - fn from(simple_vec: Vec) -> Self { - let num_samples = simple_vec.len(); - VecAudioBuffer::new_with(simple_vec, 1, num_samples) - } -} - -impl AudioBuffer for VecAudioBuffer { - type SampleType = SampleType; - - #[inline] - fn num_channels(&self) -> usize { - self.num_channels - } - - #[inline] - fn num_samples(&self) -> usize { - self.num_samples - } - - #[inline] - fn slice(&self) -> &[Self::SampleType] { - &self.buffer - } - - #[inline] - fn slice_mut(&mut self) -> &mut [Self::SampleType] { - &mut self.buffer - } - - #[inline] - fn get(&self, channel: usize, sample: usize) -> &Self::SampleType { - &self.buffer[sample * self.num_channels + channel] - } - - #[inline] - fn get_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType { - &mut self.buffer[sample * self.num_channels + channel] - } - - #[inline] - fn set(&mut self, channel: usize, sample: usize, value: Self::SampleType) { - self.buffer[sample * self.num_channels + channel] = value; - } - - #[inline] - unsafe fn get_unchecked(&self, channel: usize, sample: usize) -> &Self::SampleType { - self.buffer - .get_unchecked(sample * self.num_channels + channel) - } - - #[inline] - unsafe fn get_unchecked_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType { - self.buffer - .get_unchecked_mut(sample * self.num_channels + channel) - } - - #[inline] - unsafe fn set_unchecked(&mut self, channel: usize, sample: usize, value: Self::SampleType) { - let sample = self - .buffer - .get_unchecked_mut(sample * self.num_channels + channel); - *sample = value; - } -} - -impl OwnedAudioBuffer for VecAudioBuffer { - #[inline] - fn new() -> Self { - VecAudioBuffer { - num_channels: 0, - num_samples: 0, - buffer: Vec::new(), - } - } - - #[inline] - fn resize(&mut self, num_channels: usize, num_samples: usize, sample: Self::SampleType) { - self.num_samples = num_samples; - self.num_channels = num_channels; - self.buffer.resize(num_channels * num_samples, sample); - } -} - -impl VecAudioBuffer { - pub fn new_with_size(num_channels: usize, num_samples: usize, sample: SampleType) -> Self { - let mut buffer = Vec::with_capacity(num_samples * num_channels); - buffer.resize(num_channels * num_samples, sample); - VecAudioBuffer { - num_channels, - num_samples, - buffer, - } - } - - /// Get an `InterleavedAudioBuffer` reference type out this `VecAudioBuffer`. - pub fn interleaved(&mut self) -> InterleavedAudioBuffer { - InterleavedAudioBuffer::new(self.num_channels, &mut self.buffer) - } -} - -pub mod vst { - use super::{AudioBuffer, Float, OwnedAudioBuffer, VecAudioBuffer}; - - pub struct VSTBufferHandler { - buffer: VecAudioBuffer, - } - - impl Default for VSTBufferHandler { - fn default() -> Self { - Self::new() - } - } - - impl VSTBufferHandler { - pub fn new() -> Self { - Self { - buffer: VecAudioBuffer::new(), - } - } - - pub fn set_block_size(&mut self, block_size: usize) { - self.buffer.resize(2, block_size, SampleType::zero()); - } - - pub fn with_buffer(&mut self, buffer: &mut vst::buffer::AudioBuffer, f: F) - where - F: FnOnce(&mut VecAudioBuffer), - { - let num_samples = buffer.samples(); - let (inputs, mut outputs) = buffer.split(); - self.buffer - .resize(2, num_samples, SampleType::zero()); - { - let buffer_slice = self.buffer.slice_mut(); - for (channel, input) in inputs.into_iter().take(2).enumerate() { - for (index, sample) in input.iter().enumerate() { - buffer_slice[index * 2 + channel] = *sample; - } - } - } - - f(&mut self.buffer); - - { - let buffer_slice = self.buffer.slice(); - for (channel, output) in outputs.into_iter().take(2).enumerate() { - for (index, sample) in output.iter_mut().enumerate() { - *sample = buffer_slice[index * 2 + channel]; - } - } - } - } - } -} - -#[cfg(test)] -mod test { - use std::sync::Arc; - use std::thread; - - use super::*; - - #[test] - fn test_vec_is_send() { - let mut vec = VecAudioBuffer::new(); - vec.resize(2, 1000, 0.0); - let handle = thread::spawn(move || println!("HELLO VEC {:?}", vec)); - handle.join().unwrap(); - } - - #[test] - fn test_vec_is_sync() { - let mut vec = VecAudioBuffer::new(); - vec.resize(2, 1000, 0.0); - let vec = Arc::new(vec); - let vec_2 = vec.clone(); - let handle = thread::spawn(move || println!("HELLO VEC {:?}", vec)); - println!("HELLO VEC {:?}", vec_2); - handle.join().unwrap(); - } -} diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer/audio_buffer_trait.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/audio_buffer_trait.rs new file mode 100644 index 00000000000..364056abea5 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/audio_buffer_trait.rs @@ -0,0 +1,119 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use std::slice::{Chunks, ChunksMut}; + +/// Represents an audio buffer. This decouples audio processing code from a certain representation +/// of multi-channel sample buffers. +/// +/// This crate provides implementations of this trait for CPal style buffers, which use interleaved +/// internal representation. +/// +/// When processing samples, it'll be more efficient to use `.slice` and `.slice_mut` than `.get` / +/// `.set` methods. For the VST buffer, these methods will not work. +/// +/// It's recommended to convert the buffer into interleaved layout before processing as that will be +/// around as expensive as the overhead of `get`/`set` methods on a single loop through samples. +/// +/// (due to bounds checking and other compiler optimisations that fail with them) +pub trait AudioBuffer { + /// The type of samples within this buffer. + type SampleType; + + /// The number of channels in this buffer + fn num_channels(&self) -> usize; + + /// The number of samples in this buffer + fn num_samples(&self) -> usize; + + /// Get a slice to the internal data. Will not work with VST adapter + /// + /// This is the faster way to process + fn slice(&self) -> &[Self::SampleType]; + + /// Get a mutable slice to the internal data. Will not work with VST adapter + /// + /// This is the faster way to process + fn slice_mut(&mut self) -> &mut [Self::SampleType]; + + /// Shortcut for `.slice().chunks(num_channels)` + fn frames(&self) -> Chunks<'_, Self::SampleType> { + self.slice().chunks(self.num_channels()) + } + + /// Shortcut for `.slice_mut().chunks_mut(num_channels)` + /// + /// This is a frame representing a sample in time, for all + /// channels. + fn frames_mut(&mut self) -> ChunksMut<'_, Self::SampleType> { + let channels = self.num_channels(); + self.slice_mut().chunks_mut(channels) + } + + /// Get a ref to an INPUT sample in this buffer. + /// + /// Calling this on a loop will be ~20x slower than reading from `slice`. + fn get(&self, channel: usize, sample: usize) -> &Self::SampleType; + + /// Get a mutable ref to an OUTPUT sample in this buffer + /// + /// On some implementations this may yield a different value than `.get`. + /// + /// Calling this on a loop will be ~20x slower than reading from `slice`. + fn get_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType; + + /// Set an OUTPUT sample in this buffer + fn set(&mut self, channel: usize, sample: usize, value: Self::SampleType); + + /// Unsafe, no bounds check - Get a ref to an INPUT sample in this buffer + /// + /// Calling this on a loop will be ~10x slower than reading from `slice`. + /// + /// # Safety + /// This performs no bounds checks. Make sure indexes are in range. + unsafe fn get_unchecked(&self, channel: usize, sample: usize) -> &Self::SampleType { + self.get(channel, sample) + } + + /// Unsafe, no bounds check - Get a mutable ref to an OUTPUT sample in this buffer + /// + /// On some implementations this may yield a different value than `.get`. + /// + /// Calling this on a loop will be ~10x slower than reading from `slice`. + /// + /// # Safety + /// This performs no bounds checks. Make sure indexes are in range. + unsafe fn get_unchecked_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType { + self.get_mut(channel, sample) + } + + /// Unsafe, no bounds check - Set an OUTPUT sample in this buffer + /// + /// Calling this on a loop will be ~10x slower than reading from `slice`. + /// + /// # Safety + /// This performs no bounds checks. Make sure indexes are in range. + unsafe fn set_unchecked(&mut self, channel: usize, sample: usize, value: Self::SampleType) { + self.set(channel, sample, value) + } +} diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer/interleaved_audio_buffer.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/interleaved_audio_buffer.rs new file mode 100644 index 00000000000..8fc30718840 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/interleaved_audio_buffer.rs @@ -0,0 +1,86 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use crate::AudioBuffer; + +/// An AudioBuffer that stores samples as interleaved frames, used for [`cpal`] compatibility. +/// +/// Example layout: +/// +/// [ +/// 0, 0, // <- left_sample, right_sample, +/// ..., +/// ] +pub struct InterleavedAudioBuffer<'a, SampleType> { + num_channels: usize, + inner: &'a mut [SampleType], +} + +impl<'a, SampleType> InterleavedAudioBuffer<'a, SampleType> { + pub fn new(num_channels: usize, inner: &'a mut [SampleType]) -> Self { + Self { + num_channels, + inner, + } + } +} + +impl<'a, SampleType> AudioBuffer for InterleavedAudioBuffer<'a, SampleType> { + type SampleType = SampleType; + + #[inline] + fn num_channels(&self) -> usize { + self.num_channels + } + + #[inline] + fn num_samples(&self) -> usize { + self.inner.len() / self.num_channels + } + + #[inline] + fn slice(&self) -> &[Self::SampleType] { + self.inner + } + + #[inline] + fn slice_mut(&mut self) -> &mut [Self::SampleType] { + self.inner + } + + #[inline] + fn get(&self, channel: usize, sample: usize) -> &SampleType { + &self.inner[sample * self.num_channels + channel] + } + + #[inline] + fn get_mut(&mut self, channel: usize, sample: usize) -> &mut SampleType { + &mut self.inner[sample * self.num_channels + channel] + } + + #[inline] + fn set(&mut self, channel: usize, sample: usize, value: SampleType) { + let sample_ref = self.get_mut(channel, sample); + *sample_ref = value; + } +} diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer/mod.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/mod.rs new file mode 100644 index 00000000000..4266eecdaa1 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/mod.rs @@ -0,0 +1,37 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +pub use audio_buffer_trait::*; +pub use interleaved_audio_buffer::*; +pub use owned_audio_buffer_trait::*; +pub use util::*; +pub use vec_audio_buffer::*; + +mod audio_buffer_trait; +mod interleaved_audio_buffer; +mod owned_audio_buffer_trait; +mod util; +mod vec_audio_buffer; + +#[cfg(feature = "vst")] +pub mod vst; diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer/owned_audio_buffer_trait.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/owned_audio_buffer_trait.rs new file mode 100644 index 00000000000..da2317c39a7 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/owned_audio_buffer_trait.rs @@ -0,0 +1,32 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use crate::AudioBuffer; + +/// A trait for buffer types that own the data they hold & can be constructed / resized. +pub trait OwnedAudioBuffer: AudioBuffer { + /// Create an empty buffer of this type + fn new() -> Self; + /// Resize the buffer to fit `num_channels` and `num_samples` + fn resize(&mut self, num_channels: usize, num_samples: usize, sample: Self::SampleType); +} diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer/util.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/util.rs new file mode 100644 index 00000000000..b81510afc42 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/util.rs @@ -0,0 +1,46 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use crate::AudioBuffer; + +/// Set all samples of an AudioBuffer to a constant +pub fn set_all(buf: &mut Buffer, value: SampleType) +where + Buffer: AudioBuffer, + SampleType: Clone, +{ + for sample in buf.slice_mut() { + *sample = value.clone(); + } +} + +/// Set all samples of an AudioBuffer to Zero::zero +pub fn clear(buf: &mut Buffer) +where + Buffer: AudioBuffer, + SampleType: num::Zero, +{ + for sample in buf.slice_mut() { + *sample = SampleType::zero(); + } +} diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer/vec_audio_buffer.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/vec_audio_buffer.rs new file mode 100644 index 00000000000..7903732db5d --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/vec_audio_buffer.rs @@ -0,0 +1,177 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use crate::{AudioBuffer, InterleavedAudioBuffer, OwnedAudioBuffer}; + +/// An owned version of the interleaved buffer implementation. Can be converted onto an +/// `InterleavedAudioBuffer`. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct VecAudioBuffer { + buffer: Vec, + num_channels: usize, + num_samples: usize, +} + +impl VecAudioBuffer { + pub fn new_with(buffer: Vec, num_channels: usize, num_samples: usize) -> Self { + Self { + buffer, + num_samples, + num_channels, + } + } + + pub fn empty_with(num_channels: usize, num_samples: usize, value: SampleType) -> Self { + let mut result = Self::new(); + result.resize(num_channels, num_samples, value); + result + } +} + +impl From> for VecAudioBuffer { + fn from(simple_vec: Vec) -> Self { + let num_samples = simple_vec.len(); + VecAudioBuffer::new_with(simple_vec, 1, num_samples) + } +} + +impl AudioBuffer for VecAudioBuffer { + type SampleType = SampleType; + + #[inline] + fn num_channels(&self) -> usize { + self.num_channels + } + + #[inline] + fn num_samples(&self) -> usize { + self.num_samples + } + + #[inline] + fn slice(&self) -> &[Self::SampleType] { + &self.buffer + } + + #[inline] + fn slice_mut(&mut self) -> &mut [Self::SampleType] { + &mut self.buffer + } + + #[inline] + fn get(&self, channel: usize, sample: usize) -> &Self::SampleType { + &self.buffer[sample * self.num_channels + channel] + } + + #[inline] + fn get_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType { + &mut self.buffer[sample * self.num_channels + channel] + } + + #[inline] + fn set(&mut self, channel: usize, sample: usize, value: Self::SampleType) { + self.buffer[sample * self.num_channels + channel] = value; + } + + #[inline] + unsafe fn get_unchecked(&self, channel: usize, sample: usize) -> &Self::SampleType { + self.buffer + .get_unchecked(sample * self.num_channels + channel) + } + + #[inline] + unsafe fn get_unchecked_mut(&mut self, channel: usize, sample: usize) -> &mut Self::SampleType { + self.buffer + .get_unchecked_mut(sample * self.num_channels + channel) + } + + #[inline] + unsafe fn set_unchecked(&mut self, channel: usize, sample: usize, value: Self::SampleType) { + let sample = self + .buffer + .get_unchecked_mut(sample * self.num_channels + channel); + *sample = value; + } +} + +impl OwnedAudioBuffer for VecAudioBuffer { + #[inline] + fn new() -> Self { + VecAudioBuffer { + num_channels: 0, + num_samples: 0, + buffer: Vec::new(), + } + } + + #[inline] + fn resize(&mut self, num_channels: usize, num_samples: usize, sample: Self::SampleType) { + self.num_samples = num_samples; + self.num_channels = num_channels; + self.buffer.resize(num_channels * num_samples, sample); + } +} + +impl VecAudioBuffer { + pub fn new_with_size(num_channels: usize, num_samples: usize, sample: SampleType) -> Self { + let mut buffer = Vec::with_capacity(num_samples * num_channels); + buffer.resize(num_channels * num_samples, sample); + VecAudioBuffer { + num_channels, + num_samples, + buffer, + } + } + + /// Get an `InterleavedAudioBuffer` reference type out this `VecAudioBuffer`. + pub fn interleaved(&mut self) -> InterleavedAudioBuffer { + InterleavedAudioBuffer::new(self.num_channels, &mut self.buffer) + } +} + +#[cfg(test)] +mod test { + use std::sync::Arc; + use std::thread; + + use super::*; + + #[test] + fn test_vec_is_send() { + let mut vec = VecAudioBuffer::new(); + vec.resize(2, 1000, 0.0); + let handle = thread::spawn(move || std::mem::forget(vec)); + handle.join().unwrap(); + } + + #[test] + fn test_vec_is_sync() { + let mut vec = VecAudioBuffer::new(); + vec.resize(2, 1000, 0.0); + let vec = Arc::new(vec); + let vec_2 = vec.clone(); + let handle = thread::spawn(move || std::mem::forget(vec)); + std::mem::forget(vec_2); + handle.join().unwrap(); + } +} diff --git a/crates/augmented/audio/audio-processor-traits/src/audio_buffer/vst.rs b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/vst.rs new file mode 100644 index 00000000000..36082030f76 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/audio_buffer/vst.rs @@ -0,0 +1,76 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use num::Float; + +use super::{AudioBuffer, OwnedAudioBuffer, VecAudioBuffer}; + +pub struct VSTBufferHandler { + buffer: VecAudioBuffer, +} + +impl Default for VSTBufferHandler { + fn default() -> Self { + Self::new() + } +} + +impl VSTBufferHandler { + pub fn new() -> Self { + Self { + buffer: VecAudioBuffer::new(), + } + } + + pub fn set_block_size(&mut self, block_size: usize) { + self.buffer.resize(2, block_size, SampleType::zero()); + } + + pub fn with_buffer(&mut self, buffer: &mut vst::buffer::AudioBuffer, f: F) + where + F: FnOnce(&mut VecAudioBuffer), + { + let num_samples = buffer.samples(); + let (inputs, mut outputs) = buffer.split(); + self.buffer.resize(2, num_samples, SampleType::zero()); + { + let buffer_slice = self.buffer.slice_mut(); + for (channel, input) in inputs.into_iter().take(2).enumerate() { + for (index, sample) in input.iter().enumerate() { + buffer_slice[index * 2 + channel] = *sample; + } + } + } + + f(&mut self.buffer); + + { + let buffer_slice = self.buffer.slice(); + for (channel, output) in outputs.into_iter().take(2).enumerate() { + for (index, sample) in output.iter_mut().enumerate() { + *sample = buffer_slice[index * 2 + channel]; + } + } + } + } +} diff --git a/crates/augmented/audio/audio-processor-traits/src/combinators/function.rs b/crates/augmented/audio/audio-processor-traits/src/combinators/function.rs new file mode 100644 index 00000000000..ca97195a0ff --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/combinators/function.rs @@ -0,0 +1,99 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use std::marker::PhantomData; + +use crate::simple_processor::MonoAudioProcessor; +use crate::{AudioContext, SimpleAudioProcessor}; + +pub struct AudioProcessorFunction { + f: F, + phantom: PhantomData, +} + +impl SimpleAudioProcessor for AudioProcessorFunction +where + F: FnMut(&mut AudioContext, &mut [SampleType]), + SampleType: Copy, +{ + type SampleType = SampleType; + + fn s_process_frame(&mut self, context: &mut AudioContext, frame: &mut [Self::SampleType]) { + (self.f)(context, frame) + } +} + +pub fn processor_function(f: F) -> impl SimpleAudioProcessor +where + F: FnMut(&mut AudioContext, &mut [SampleType]), + SampleType: Copy, +{ + AudioProcessorFunction { + f, + phantom: PhantomData, + } +} + +struct MonoAudioProcessorFunction { + f: F, + phantom: PhantomData, +} + +impl MonoAudioProcessor for MonoAudioProcessorFunction +where + F: FnMut(&mut AudioContext, SampleType) -> SampleType, + SampleType: Copy, +{ + type SampleType = SampleType; + + fn m_process( + &mut self, + context: &mut AudioContext, + sample: Self::SampleType, + ) -> Self::SampleType { + (self.f)(context, sample) + } +} + +pub fn mono_processor_function( + f: F, +) -> impl MonoAudioProcessor +where + F: FnMut(&mut AudioContext, SampleType) -> SampleType, + SampleType: Copy, +{ + MonoAudioProcessorFunction { + f, + phantom: PhantomData, + } +} + +pub fn mono_generator_function( + mut f: F, +) -> impl MonoAudioProcessor +where + F: FnMut(&mut AudioContext) -> SampleType, + SampleType: Copy, +{ + mono_processor_function(move |context, _sample| f(context)) +} diff --git a/crates/augmented/audio/audio-processor-traits/src/combinators/map.rs b/crates/augmented/audio/audio-processor-traits/src/combinators/map.rs new file mode 100644 index 00000000000..53e28af1141 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/combinators/map.rs @@ -0,0 +1,55 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use crate::{AudioBuffer, AudioContext, AudioProcessor}; + +struct MapProcessor { + processor: P, + f: F, +} + +impl AudioProcessor for MapProcessor +where + P: AudioProcessor, + F: FnMut(&mut AudioContext, &mut [P::SampleType]), +{ + type SampleType = P::SampleType; + + fn process>( + &mut self, + context: &mut AudioContext, + output: &mut BufferType, + ) { + self.processor.process(context, output); + for frame in output.frames_mut() { + (self.f)(context, frame); + } + } +} + +pub fn map_processor( + processor: P, + f: F, +) -> impl AudioProcessor { + MapProcessor { processor, f } +} diff --git a/crates/augmented/audio/audio-processor-traits/src/combinators/mix.rs b/crates/augmented/audio/audio-processor-traits/src/combinators/mix.rs new file mode 100644 index 00000000000..4ea8e046014 --- /dev/null +++ b/crates/augmented/audio/audio-processor-traits/src/combinators/mix.rs @@ -0,0 +1,46 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use std::ops::AddAssign; + +use num::{Float, Zero}; + +use crate::AudioProcessor; + +use super::map::map_processor; + +pub fn mono

(processor: P) -> impl AudioProcessor +where + P: AudioProcessor, + P::SampleType: Float + AddAssign, +{ + map_processor(processor, |_ctx, frame| { + let mut sum = P::SampleType::zero(); + for sample in frame.iter() { + sum += *sample; + } + for sample in frame.iter_mut() { + *sample = sum; + } + }) +} diff --git a/crates/augmented/audio/audio-processor-traits/src/combinators/mod.rs b/crates/augmented/audio/audio-processor-traits/src/combinators/mod.rs index 860ca973d40..0883937e5ce 100644 --- a/crates/augmented/audio/audio-processor-traits/src/combinators/mod.rs +++ b/crates/augmented/audio/audio-processor-traits/src/combinators/mod.rs @@ -1,141 +1,30 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + pub use function::*; pub use map::*; pub use mix::*; -mod function { - use crate::simple_processor::MonoAudioProcessor; - use crate::{AudioContext, SimpleAudioProcessor}; - use std::marker::PhantomData; - - pub struct AudioProcessorFunction { - f: F, - phantom: PhantomData, - } - - impl SimpleAudioProcessor for AudioProcessorFunction - where - F: FnMut(&mut AudioContext, &mut [SampleType]), - SampleType: Copy, - { - type SampleType = SampleType; - - fn s_process_frame(&mut self, context: &mut AudioContext, frame: &mut [Self::SampleType]) { - (self.f)(context, frame) - } - } - - pub fn processor_function( - f: F, - ) -> impl SimpleAudioProcessor - where - F: FnMut(&mut AudioContext, &mut [SampleType]), - SampleType: Copy, - { - AudioProcessorFunction { - f, - phantom: PhantomData, - } - } - - struct MonoAudioProcessorFunction { - f: F, - phantom: PhantomData, - } - - impl MonoAudioProcessor for MonoAudioProcessorFunction - where - F: FnMut(&mut AudioContext, SampleType) -> SampleType, - SampleType: Copy, - { - type SampleType = SampleType; - - fn m_process( - &mut self, - context: &mut AudioContext, - sample: Self::SampleType, - ) -> Self::SampleType { - (self.f)(context, sample) - } - } - - pub fn mono_processor_function( - f: F, - ) -> impl MonoAudioProcessor - where - F: FnMut(&mut AudioContext, SampleType) -> SampleType, - SampleType: Copy, - { - MonoAudioProcessorFunction { - f, - phantom: PhantomData, - } - } - - pub fn mono_generator_function( - mut f: F, - ) -> impl MonoAudioProcessor - where - F: FnMut(&mut AudioContext) -> SampleType, - SampleType: Copy, - { - mono_processor_function(move |context, _sample| f(context)) - } -} - -mod map { - use crate::{AudioBuffer, AudioContext, AudioProcessor}; - - struct MapProcessor { - processor: P, - f: F, - } - - impl AudioProcessor for MapProcessor - where - P: AudioProcessor, - F: FnMut(&mut AudioContext, &mut [P::SampleType]), - { - type SampleType = P::SampleType; - - fn process>( - &mut self, - context: &mut AudioContext, - output: &mut BufferType, - ) { - self.processor.process(context, output); - for frame in output.frames_mut() { - (self.f)(context, frame); - } - } - } - - pub fn map_processor( - processor: P, - f: F, - ) -> impl AudioProcessor { - MapProcessor { processor, f } - } -} - -mod mix { - use super::map::map_processor; - use crate::AudioProcessor; - use num::{Float, Zero}; - use std::ops::AddAssign; - - pub fn mono

(processor: P) -> impl AudioProcessor - where - P: AudioProcessor, - P::SampleType: Float + AddAssign, - { - map_processor(processor, |_ctx, frame| { - let mut sum = P::SampleType::zero(); - for sample in frame.iter() { - sum += *sample; - } - for sample in frame.iter_mut() { - *sample = sum; - } - }) - } -} +mod function; +mod map; +mod mix; diff --git a/crates/augmented/audio/audio-processor-traits/src/context.rs b/crates/augmented/audio/audio-processor-traits/src/context.rs index fb94d59eec4..3dcab72fe2f 100644 --- a/crates/augmented/audio/audio-processor-traits/src/context.rs +++ b/crates/augmented/audio/audio-processor-traits/src/context.rs @@ -1,3 +1,26 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + use crate::AudioProcessorSettings; #[derive(Default)] diff --git a/crates/augmented/development/augmented-dev-cli/src/services/content_hashes/mod.rs b/crates/augmented/development/augmented-dev-cli/src/services/content_hashes/mod.rs index a7861324de3..be927ab9244 100644 --- a/crates/augmented/development/augmented-dev-cli/src/services/content_hashes/mod.rs +++ b/crates/augmented/development/augmented-dev-cli/src/services/content_hashes/mod.rs @@ -1,3 +1,26 @@ +// Augmented Audio: Audio libraries and applications +// Copyright (c) 2022 Pedro Tacla Yamada +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + use crate::manifests::{AugmentedMetadata, CargoTomlPackageMetadata}; use crate::services::ListCratesService; use sha3::{Digest, Sha3_256}; diff --git a/crates/augmented/ops/wisual-logger/src/lib.rs b/crates/augmented/ops/wisual-logger/src/lib.rs index b8c5c47fd81..2800f08d5e9 100644 --- a/crates/augmented/ops/wisual-logger/src/lib.rs +++ b/crates/augmented/ops/wisual-logger/src/lib.rs @@ -41,10 +41,12 @@ //! ``` use std::io::Write; -use std::time::SystemTime; use std::{io, thread}; -use chrono::{DateTime, Utc}; +#[cfg(not(target_arch = "wasm32"))] +use std::time::SystemTime; + +use chrono::Utc; use env_logger::fmt::{Color, Formatter}; use log::{Record, SetLoggerError}; @@ -56,7 +58,10 @@ impl LogFormatter { pub fn format(buf: &mut Formatter, record: &Record) -> io::Result<()> { let metadata = record.metadata(); let target = metadata.target(); - let time: DateTime = SystemTime::now().into(); + #[cfg(not(target_arch = "wasm32"))] + let time = chrono::DateTime::::from(SystemTime::now()); + #[cfg(target_arch = "wasm32")] + let time = Utc::now(); let time = time.format("%+"); let current_thread = thread::current(); let thread_name = current_thread