From 7d9aa8027612371da321630586c4cb72ad897c53 Mon Sep 17 00:00:00 2001 From: MBMS <31241793+MyBlackMIDIScore@users.noreply.github.com> Date: Sun, 25 Aug 2024 16:20:37 +0300 Subject: [PATCH] Use crossbeam for sending events and audio samples in xsynth-render (#102) --- Cargo.lock | 57 ++++++++++++++++++++++++------------------ Cargo.toml | 8 +++--- render/Cargo.toml | 3 ++- render/src/main.rs | 20 ++++++++++++++- render/src/rendered.rs | 1 - render/src/writer.rs | 28 +++++++++++++-------- 6 files changed, 75 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bec817..82ceddb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,6 +432,19 @@ dependencies = [ "itertools", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -443,26 +456,30 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", - "scopeguard", ] [[package]] @@ -905,15 +922,6 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - [[package]] name = "midi-toolkit-rs" version = "0.1.0" @@ -2259,7 +2267,7 @@ dependencies = [ [[package]] name = "xsynth-clib" -version = "0.3.0" +version = "0.3.1" dependencies = [ "cbindgen", "pkg-version", @@ -2269,7 +2277,7 @@ dependencies = [ [[package]] name = "xsynth-core" -version = "0.3.0" +version = "0.3.1" dependencies = [ "atomic_refcell", "biquad", @@ -2292,7 +2300,7 @@ dependencies = [ [[package]] name = "xsynth-kdmapi" -version = "0.3.0" +version = "0.3.1" dependencies = [ "cfg-if", "directories", @@ -2306,7 +2314,7 @@ dependencies = [ [[package]] name = "xsynth-realtime" -version = "0.3.0" +version = "0.3.1" dependencies = [ "atomic_refcell", "bytemuck", @@ -2325,10 +2333,11 @@ dependencies = [ [[package]] name = "xsynth-render" -version = "0.3.0" +version = "0.3.1" dependencies = [ "atomic_float", "clap 4.5.16", + "crossbeam", "crossbeam-channel", "hound", "midi-toolkit-rs", @@ -2340,7 +2349,7 @@ dependencies = [ [[package]] name = "xsynth-soundfonts" -version = "0.3.0" +version = "0.3.1" dependencies = [ "encoding_rs", "encoding_rs_io", diff --git a/Cargo.toml b/Cargo.toml index 3218944..fd953cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["core", "clib", "soundfonts", "realtime", "render", "kdmapi"] [workspace.package] -version = "0.3.0" +version = "0.3.1" license = "LGPL-3.0" edition = "2021" homepage = "https://github.com/BlackMIDIDevs/xsynth" @@ -24,6 +24,6 @@ inherits = "release" debug = true [workspace.dependencies] -xsynth-core = { version = "0.3.0", path = "core" } -xsynth-realtime = { version = "0.3.0", path = "realtime" } -xsynth-soundfonts = { version = "0.3.0", path = "soundfonts" } +xsynth-core = { version = "0.3.1", path = "core" } +xsynth-realtime = { version = "0.3.1", path = "realtime" } +xsynth-soundfonts = { version = "0.3.1", path = "soundfonts" } diff --git a/render/Cargo.toml b/render/Cargo.toml index 665d399..9cee066 100644 --- a/render/Cargo.toml +++ b/render/Cargo.toml @@ -21,4 +21,5 @@ midi-toolkit-rs = "0.1.0" spin_sleep = "1.2.1" atomic_float = "1.0.0" thiserror = "1.0.63" -clap = { version = "4.5.16", features = ["cargo"] } \ No newline at end of file +clap = { version = "4.5.16", features = ["cargo"] } +crossbeam = "0.8.4" diff --git a/render/src/main.rs b/render/src/main.rs index 5af3588..3a8757a 100644 --- a/render/src/main.rs +++ b/render/src/main.rs @@ -31,6 +31,7 @@ use std::{ Arc, }, thread, + time::{Duration, Instant}, }; use atomic_float::AtomicF64; @@ -74,6 +75,14 @@ fn main() { |>unwrap_items() ); + let (snd, rcv) = crossbeam_channel::bounded(100); + + thread::spawn(move || { + for batch in merged { + snd.send(batch).unwrap(); + } + }); + let position = Arc::new(AtomicF64::new(0.0)); let voices = Arc::new(AtomicU64::new(0)); @@ -94,6 +103,9 @@ fn main() { } print!("] {progress:.3}% | "); print!("Voice Count: {}", voices.load(Ordering::Relaxed)); + for _ in 0..10 { + print!(" "); + } if progress >= 100.0 { println!(); break; @@ -101,7 +113,9 @@ fn main() { }); } - for batch in merged { + let now = Instant::now(); + + for batch in rcv { if batch.delta > 0.0 { synth.render_batch(batch.delta); position.fetch_add(batch.delta, Ordering::Relaxed); @@ -158,4 +172,8 @@ fn main() { ChannelAudioEvent::ResetControl, ))); synth.finalize(); + + let elapsed = now.elapsed(); + thread::sleep(Duration::from_millis(200)); + println!("Render time: {:?}", elapsed); } diff --git a/render/src/rendered.rs b/render/src/rendered.rs index 5368dbd..1f156ed 100644 --- a/render/src/rendered.rs +++ b/render/src/rendered.rs @@ -124,7 +124,6 @@ impl XSynthRender { self.audio_writer .write_samples(&mut self.render_elements.output_vec); } - self.audio_writer.finalize(); } /// Returns the active voice count of the MIDI synthesizer. diff --git a/render/src/writer.rs b/render/src/writer.rs index 0b397fd..9ac4115 100644 --- a/render/src/writer.rs +++ b/render/src/writer.rs @@ -1,11 +1,12 @@ use crate::config::XSynthRenderConfig; -use std::{fs::File, io::BufWriter, path::PathBuf}; +use std::{path::PathBuf, thread}; +use crossbeam_channel::Sender; use hound::{WavSpec, WavWriter}; pub struct AudioFileWriter { - writer: WavWriter>, + sender: Sender>, } impl AudioFileWriter { @@ -16,18 +17,23 @@ impl AudioFileWriter { bits_per_sample: 32, sample_format: hound::SampleFormat::Float, }; - let writer = WavWriter::create(path, spec).unwrap(); + let mut writer = WavWriter::create(path, spec).unwrap(); - Self { writer } - } + let (snd, rcv) = crossbeam_channel::unbounded::>(); - pub fn write_samples(&mut self, samples: &mut Vec) { - for s in samples.drain(0..) { - self.writer.write_sample(s).unwrap(); - } + thread::spawn(move || { + for batch in rcv { + for s in batch { + writer.write_sample(s).unwrap(); + } + } + writer.finalize().unwrap(); + }); + + Self { sender: snd } } - pub fn finalize(self) { - self.writer.finalize().unwrap(); + pub fn write_samples(&mut self, samples: &mut Vec) { + self.sender.send(std::mem::take(samples)).unwrap(); } }