From 45e67c0632042d42be8b36f3ab5a5a70e5aac56e Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 10:37:16 -0600 Subject: [PATCH 01/16] Added support for sf2. --- neothesia-cli/src/main.rs | 61 ++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 09c491f..05ec978 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -1,4 +1,5 @@ use std::{default::Default, time::Duration}; +use synthesizer_io_core::{midi, soundfont}; use neothesia_core::{ config::Config, @@ -22,6 +23,9 @@ struct Recorder { config: Config, width: u32, height: u32, + + synth: Option, + sample_rate: u32, } fn get_layout( @@ -60,17 +64,30 @@ impl Recorder { }); let args: Vec = std::env::args().collect(); - let midi = if args.len() > 1 { - midi_file::MidiFile::new(&args[1]).unwrap_or_else(|err| { - eprintln!("Error loading MIDI file: {}", err); - std::process::exit(1); - }) - } else { - eprintln!("No MIDI file provided."); - eprintln!("Usage: neothesia-cli "); + if args.len() < 2 { + eprintln!("Usage: neothesia-cli [soundfont-file]"); + std::process::exit(1); + } + + let midi = midi_file::MidiFile::new(&args[1]).unwrap_or_else(|err| { + eprintln!("Error loading MIDI file: {}", err); std::process::exit(1); + }); + + let synth = if args.len() > 2 { + match soundfont::Synthesizer::new(&args[2]) { + Ok(synth) => Some(synth), + Err(err) => { + eprintln!("Error loading soundfont: {}", err); + None + } + } + } else { + None }; + let sample_rate = 44100; + let config = Config::new(); let width = 1920; @@ -136,11 +153,39 @@ impl Recorder { config, width, height, + + synth, + sample_rate, } } fn update(&mut self, delta: Duration) { let events = self.playback.update(delta); + + // Process audio if synth is available + if let Some(synth) = &mut self.synth { + for event in events.iter() { + use midi_file::midly::MidiMessage; + match event.message { + MidiMessage::NoteOn { key, vel } => { + synth.note_on(event.channel as u8, key.as_int(), vel.as_int()); + }, + MidiMessage::NoteOff { key, .. } => { + synth.note_off(event.channel as u8, key.as_int()); + }, + _ => {} + } + } + + // Generate audio samples for this frame + let frame_samples = (self.sample_rate as f32 / 60.0) as usize; + let mut buffer = vec![0.0; frame_samples * 2]; // Stereo + synth.render_stereo(&mut buffer); + + // TODO: Add audio samples to video encoder + // This depends on the video encoder library's audio support + } + file_midi_events(&mut self.keyboard, &self.config, &events); let time = time_without_lead_in(&self.playback); From 447fb7135a499aebab98a39ec1cd904d358ce669 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 11:15:34 -0600 Subject: [PATCH 02/16] Revert "Added support for sf2." This reverts commit 45e67c0632042d42be8b36f3ab5a5a70e5aac56e. --- neothesia-cli/src/main.rs | 61 +++++---------------------------------- 1 file changed, 8 insertions(+), 53 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 05ec978..09c491f 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -1,5 +1,4 @@ use std::{default::Default, time::Duration}; -use synthesizer_io_core::{midi, soundfont}; use neothesia_core::{ config::Config, @@ -23,9 +22,6 @@ struct Recorder { config: Config, width: u32, height: u32, - - synth: Option, - sample_rate: u32, } fn get_layout( @@ -64,30 +60,17 @@ impl Recorder { }); let args: Vec = std::env::args().collect(); - if args.len() < 2 { - eprintln!("Usage: neothesia-cli [soundfont-file]"); - std::process::exit(1); - } - - let midi = midi_file::MidiFile::new(&args[1]).unwrap_or_else(|err| { - eprintln!("Error loading MIDI file: {}", err); - std::process::exit(1); - }); - - let synth = if args.len() > 2 { - match soundfont::Synthesizer::new(&args[2]) { - Ok(synth) => Some(synth), - Err(err) => { - eprintln!("Error loading soundfont: {}", err); - None - } - } + let midi = if args.len() > 1 { + midi_file::MidiFile::new(&args[1]).unwrap_or_else(|err| { + eprintln!("Error loading MIDI file: {}", err); + std::process::exit(1); + }) } else { - None + eprintln!("No MIDI file provided."); + eprintln!("Usage: neothesia-cli "); + std::process::exit(1); }; - let sample_rate = 44100; - let config = Config::new(); let width = 1920; @@ -153,39 +136,11 @@ impl Recorder { config, width, height, - - synth, - sample_rate, } } fn update(&mut self, delta: Duration) { let events = self.playback.update(delta); - - // Process audio if synth is available - if let Some(synth) = &mut self.synth { - for event in events.iter() { - use midi_file::midly::MidiMessage; - match event.message { - MidiMessage::NoteOn { key, vel } => { - synth.note_on(event.channel as u8, key.as_int(), vel.as_int()); - }, - MidiMessage::NoteOff { key, .. } => { - synth.note_off(event.channel as u8, key.as_int()); - }, - _ => {} - } - } - - // Generate audio samples for this frame - let frame_samples = (self.sample_rate as f32 / 60.0) as usize; - let mut buffer = vec![0.0; frame_samples * 2]; // Stereo - synth.render_stereo(&mut buffer); - - // TODO: Add audio samples to video encoder - // This depends on the video encoder library's audio support - } - file_midi_events(&mut self.keyboard, &self.config, &events); let time = time_without_lead_in(&self.playback); From 2a77b48bfc2c3654906d5a5536909c542622a6a5 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 11:30:33 -0600 Subject: [PATCH 03/16] Enhance MIDI file handling to support optional soundfont file input --- neothesia-cli/Cargo.toml | 11 ++++++++++- neothesia-cli/src/main.rs | 25 +++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/neothesia-cli/Cargo.toml b/neothesia-cli/Cargo.toml index 7bf30d0..6ace996 100644 --- a/neothesia-cli/Cargo.toml +++ b/neothesia-cli/Cargo.toml @@ -6,6 +6,12 @@ edition = "2021" [features] # Download and compile ffmpeg build-ffmpeg = ["mpeg_encoder/build"] +default = ["oxi-synth", "soundfont"] +synth = [] +fluid-synth = ["synth", "cpal", "fluidlite", "oxisynth"] +oxi-synth = ["synth", "cpal", "oxisynth"] +soundfont = ["fluid-synth"] +profiling-on = ["profiling/profile-with-puffin"] [dependencies] neothesia-core.workspace = true @@ -13,5 +19,8 @@ midi-file.workspace = true wgpu-jumpstart.workspace = true env_logger.workspace = true futures.workspace = true - mpeg_encoder = { path = "../mpeg_encoder" } +oxisynth = { version = "0.0.6", optional = true } +cpal = { version = "0.15", optional = true } +fluidlite = { version = "0.3.0", optional = true } +synthesizer-io-core = "0.2" diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 09c491f..338283f 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -60,16 +60,25 @@ impl Recorder { }); let args: Vec = std::env::args().collect(); - let midi = if args.len() > 1 { - midi_file::MidiFile::new(&args[1]).unwrap_or_else(|err| { - eprintln!("Error loading MIDI file: {}", err); - std::process::exit(1); - }) - } else { + if args.len() < 2 { eprintln!("No MIDI file provided."); - eprintln!("Usage: neothesia-cli "); + eprintln!("Usage: neothesia-cli "); std::process::exit(1); - }; + } + + let midi = midi_file::MidiFile::new(&args[1]).unwrap_or_else(|err| { + eprintln!("Error loading MIDI file: {}", err); + std::process::exit(1); + }); + + // Handle optional soundfont + if args.len() > 2 { + if let Err(err) = std::fs::metadata(&args[2]) { + eprintln!("Error loading soundfont file: {}", err); + std::process::exit(1); + } + std::env::set_var("SOUNDFONT", &args[2]); + } let config = Config::new(); From d41420775741bcdc2fddf22d65902e2cc5fcfb36 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 11:32:54 -0600 Subject: [PATCH 04/16] Add profiling support with puffin feature in Cargo.toml --- neothesia-cli/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/neothesia-cli/Cargo.toml b/neothesia-cli/Cargo.toml index 6ace996..aaf85f9 100644 --- a/neothesia-cli/Cargo.toml +++ b/neothesia-cli/Cargo.toml @@ -24,3 +24,4 @@ oxisynth = { version = "0.0.6", optional = true } cpal = { version = "0.15", optional = true } fluidlite = { version = "0.3.0", optional = true } synthesizer-io-core = "0.2" +profiling = { version = "1.0", features = ["profile-with-puffin"] } From 979d435ad45eab932d4c12048330f76d011c7c02 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 11:58:46 -0600 Subject: [PATCH 05/16] Update fluidlite dependency version to 0.2.1 in Cargo.toml --- neothesia-cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neothesia-cli/Cargo.toml b/neothesia-cli/Cargo.toml index aaf85f9..0b6d28c 100644 --- a/neothesia-cli/Cargo.toml +++ b/neothesia-cli/Cargo.toml @@ -22,6 +22,6 @@ futures.workspace = true mpeg_encoder = { path = "../mpeg_encoder" } oxisynth = { version = "0.0.6", optional = true } cpal = { version = "0.15", optional = true } -fluidlite = { version = "0.3.0", optional = true } +fluidlite = { version = "0.2.1", optional = true } synthesizer-io-core = "0.2" profiling = { version = "1.0", features = ["profile-with-puffin"] } From ad4e27ba57369fedd2a13f8e084a3d4fe43ed18b Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 12:00:44 -0600 Subject: [PATCH 06/16] Downgrade oxisynth dependency version to 0.0.5 in Cargo.toml --- neothesia-cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neothesia-cli/Cargo.toml b/neothesia-cli/Cargo.toml index 0b6d28c..89bd922 100644 --- a/neothesia-cli/Cargo.toml +++ b/neothesia-cli/Cargo.toml @@ -20,7 +20,7 @@ wgpu-jumpstart.workspace = true env_logger.workspace = true futures.workspace = true mpeg_encoder = { path = "../mpeg_encoder" } -oxisynth = { version = "0.0.6", optional = true } +oxisynth = { version = "0.0.5", optional = true } cpal = { version = "0.15", optional = true } fluidlite = { version = "0.2.1", optional = true } synthesizer-io-core = "0.2" From 8f7f87521219725990aca74a0a2c640208d5e694 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 12:23:20 -0600 Subject: [PATCH 07/16] Remove synthesizer-io-core dependency from Cargo.toml --- neothesia-cli/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/neothesia-cli/Cargo.toml b/neothesia-cli/Cargo.toml index 89bd922..12e1dd3 100644 --- a/neothesia-cli/Cargo.toml +++ b/neothesia-cli/Cargo.toml @@ -23,5 +23,4 @@ mpeg_encoder = { path = "../mpeg_encoder" } oxisynth = { version = "0.0.5", optional = true } cpal = { version = "0.15", optional = true } fluidlite = { version = "0.2.1", optional = true } -synthesizer-io-core = "0.2" profiling = { version = "1.0", features = ["profile-with-puffin"] } From 6856599b913fb4d1ac1ae0e47c496ea8207ba854 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 13:22:11 -0600 Subject: [PATCH 08/16] Add fluid-synth support for MIDI note handling --- neothesia-cli/src/main.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 338283f..9e31683 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -1,4 +1,6 @@ use std::{default::Default, time::Duration}; +#[cfg(feature = "fluid-synth")] +use {fluidlite::{Settings, Synth}, std::sync::Arc}; use neothesia_core::{ config::Config, @@ -12,6 +14,8 @@ struct Recorder { transform_uniform: Uniform, playback: midi_file::PlaybackState, + #[cfg(feature = "fluid-synth")] + synth: Option>, quad_pipeline: QuadPipeline, keyboard: KeyboardRenderer, @@ -80,6 +84,20 @@ impl Recorder { std::env::set_var("SOUNDFONT", &args[2]); } + #[cfg(feature = "fluid-synth")] + let synth = if args.len() > 2 { + let settings = Settings::new().unwrap(); + let synth = Synth::new(settings).unwrap(); + synth.sfload(&args[2], 1).unwrap_or_else(|_| { + eprintln!("Failed to load soundfont"); + std::process::exit(1); + }); + synth.program_select(0, 0, 0, 0).unwrap(); + Some(Arc::new(synth)) + } else { + None + }; + let config = Config::new(); let width = 1920; @@ -135,6 +153,8 @@ impl Recorder { transform_uniform, playback, + #[cfg(feature = "fluid-synth")] + synth, quad_pipeline, keyboard, @@ -150,6 +170,21 @@ impl Recorder { fn update(&mut self, delta: Duration) { let events = self.playback.update(delta); + #[cfg(feature = "fluid-synth")] + if let Some(ref synth) = self.synth { + for e in &events { + match e.message { + midi_file::midly::MidiMessage::NoteOn { key, vel } => { + synth.noteon(e.channel as i32, key.as_int() as i32, vel.as_int() as i32).ok(); + } + midi_file::midly::MidiMessage::NoteOff { key, .. } => { + synth.noteoff(e.channel as i32, key.as_int() as i32).ok(); + } + _ => {} + } + } + } + file_midi_events(&mut self.keyboard, &self.config, &events); let time = time_without_lead_in(&self.playback); From 7908456382e679b1d45a566e408ba6a7dddac67c Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 13:24:57 -0600 Subject: [PATCH 09/16] Refactor MIDI handling methods for consistency in Synth interface --- neothesia-cli/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 9e31683..b20c0f0 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -88,7 +88,7 @@ impl Recorder { let synth = if args.len() > 2 { let settings = Settings::new().unwrap(); let synth = Synth::new(settings).unwrap(); - synth.sfload(&args[2], 1).unwrap_or_else(|_| { + synth.sfload(&args[2], true).unwrap_or_else(|_| { eprintln!("Failed to load soundfont"); std::process::exit(1); }); @@ -175,10 +175,10 @@ impl Recorder { for e in &events { match e.message { midi_file::midly::MidiMessage::NoteOn { key, vel } => { - synth.noteon(e.channel as i32, key.as_int() as i32, vel.as_int() as i32).ok(); + synth.note_on(e.channel as i32, key.as_int() as i32, vel.as_int() as i32).ok(); } midi_file::midly::MidiMessage::NoteOff { key, .. } => { - synth.noteoff(e.channel as i32, key.as_int() as i32).ok(); + synth.note_off(e.channel as i32, key.as_int() as i32).ok(); } _ => {} } From 5dd79fa4bbceecf94c04090b30c7458392d359b3 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 14:19:21 -0600 Subject: [PATCH 10/16] Refactor MIDI message handling to use fluidlite's Chan type for channel representation --- neothesia-cli/src/main.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index b20c0f0..0cf7328 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -1,6 +1,9 @@ use std::{default::Default, time::Duration}; #[cfg(feature = "fluid-synth")] -use {fluidlite::{Settings, Synth}, std::sync::Arc}; +use { + fluidlite::{Settings, Synth, midi::Chan}, + std::sync::Arc, +}; use neothesia_core::{ config::Config, @@ -175,10 +178,15 @@ impl Recorder { for e in &events { match e.message { midi_file::midly::MidiMessage::NoteOn { key, vel } => { - synth.note_on(e.channel as i32, key.as_int() as i32, vel.as_int() as i32).ok(); + let chan = Chan::try_from(e.channel as u32).unwrap(); + let key = key.as_int() as u32; + let vel = vel.as_int() as u32; + synth.note_on(chan, key, vel).ok(); } midi_file::midly::MidiMessage::NoteOff { key, .. } => { - synth.note_off(e.channel as i32, key.as_int() as i32).ok(); + let chan = Chan::try_from(e.channel as u32).unwrap(); + let key = key.as_int() as u32; + synth.note_off(chan, key).ok(); } _ => {} } From a274aaef947a7183b3cb5e8d7d77ef7ad899ab70 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 14:22:20 -0600 Subject: [PATCH 11/16] Simplify MIDI note handling by removing unnecessary channel conversion --- neothesia-cli/src/main.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 0cf7328..47a0f7d 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -1,9 +1,6 @@ use std::{default::Default, time::Duration}; #[cfg(feature = "fluid-synth")] -use { - fluidlite::{Settings, Synth, midi::Chan}, - std::sync::Arc, -}; +use {fluidlite::{Settings, Synth}, std::sync::Arc}; use neothesia_core::{ config::Config, @@ -178,15 +175,17 @@ impl Recorder { for e in &events { match e.message { midi_file::midly::MidiMessage::NoteOn { key, vel } => { - let chan = Chan::try_from(e.channel as u32).unwrap(); - let key = key.as_int() as u32; - let vel = vel.as_int() as u32; - synth.note_on(chan, key, vel).ok(); + synth.note_on( + e.channel as u32, + key.as_int() as u32, + vel.as_int() as u32 + ).ok(); } midi_file::midly::MidiMessage::NoteOff { key, .. } => { - let chan = Chan::try_from(e.channel as u32).unwrap(); - let key = key.as_int() as u32; - synth.note_off(chan, key).ok(); + synth.note_off( + e.channel as u32, + key.as_int() as u32 + ).ok(); } _ => {} } From e88a3d817ba4c9a0187267f233e216ac00d50dfc Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 14:42:47 -0600 Subject: [PATCH 12/16] Improve soundfont loading by adding preset selection and fallback mechanism --- neothesia-cli/src/main.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 47a0f7d..7ac1cb7 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -88,11 +88,31 @@ impl Recorder { let synth = if args.len() > 2 { let settings = Settings::new().unwrap(); let synth = Synth::new(settings).unwrap(); - synth.sfload(&args[2], true).unwrap_or_else(|_| { + let sfont_id = synth.sfload(&args[2], true).unwrap_or_else(|_| { eprintln!("Failed to load soundfont"); std::process::exit(1); }); - synth.program_select(0, 0, 0, 0).unwrap(); + + // Try to find a piano preset (usually bank 0, preset 0-3) + let presets = [(0, 0), (0, 1), (0, 2), (0, 3)]; + let mut success = false; + + for (bank, preset) in presets { + if synth.program_select(0, sfont_id, bank, preset).is_ok() { + success = true; + break; + } + } + + if !success { + eprintln!("Warning: Could not find piano preset, using first available preset"); + // Try to select any available preset + if let Err(e) = synth.program_select(0, sfont_id, 0, 0) { + eprintln!("Error selecting preset: {}", e); + std::process::exit(1); + } + } + Some(Arc::new(synth)) } else { None From 086c09284cff312bd7da0f0b04e3fa57af4a1aea Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 14:55:07 -0600 Subject: [PATCH 13/16] Update dependencies and enhance audio handling in Recorder with fluid-synth support --- Cargo.lock | 6 +++++- neothesia-cli/src/main.rs | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89a5750..132b0ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -2452,11 +2452,15 @@ dependencies = [ name = "neothesia-cli" version = "0.1.0" dependencies = [ + "cpal", "env_logger", + "fluidlite", "futures", "midi-file", "mpeg_encoder", "neothesia-core", + "oxisynth", + "profiling", "wgpu-jumpstart", ] diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 7ac1cb7..d170e2b 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -1,6 +1,9 @@ use std::{default::Default, time::Duration}; #[cfg(feature = "fluid-synth")] -use {fluidlite::{Settings, Synth}, std::sync::Arc}; +use { + fluidlite::{Settings, Synth}, + std::sync::Arc, +}; use neothesia_core::{ config::Config, @@ -26,6 +29,8 @@ struct Recorder { config: Config, width: u32, height: u32, + #[cfg(feature = "fluid-synth")] + audio_buffer: Vec, } fn get_layout( @@ -185,6 +190,8 @@ impl Recorder { config, width, height, + #[cfg(feature = "fluid-synth")] + audio_buffer: Vec::with_capacity(44100 * 2 * 60 * 10), // 10 minutes stereo buffer } } @@ -210,6 +217,12 @@ impl Recorder { _ => {} } } + + // Render audio for this frame (1/60th of a second) + let samples_per_frame = (44100.0 * (1.0/60.0)) as usize; + let mut frame_audio = vec![0.0f32; samples_per_frame * 2]; // Stereo + synth.write(&mut frame_audio).unwrap_or(0); + self.audio_buffer.extend_from_slice(&frame_audio); } file_midi_events(&mut self.keyboard, &self.config, &events); @@ -342,7 +355,7 @@ fn main() { "./out/video.mp4", recorder.width as usize, recorder.height as usize, - Some(0.0), + Some(44100), // Set audio sample rate Some("medium"), ); @@ -366,8 +379,22 @@ fn main() { let mapping = slice.get_mapped_range(); - let data: &[u8] = &mapping; - encoder.encode_bgra(1920, 1080, data, false); + #[cfg(feature = "fluid-synth")] + { + let samples_per_frame = (44100.0 * (1.0/60.0)) as usize; + let frame_start = (n - 1) * samples_per_frame * 2; + let frame_end = frame_start + samples_per_frame * 2; + let audio_frame = if frame_start < recorder.audio_buffer.len() { + &recorder.audio_buffer[frame_start..std::cmp::min(frame_end, recorder.audio_buffer.len())] + } else { + &[] + }; + encoder.encode_bgra_with_audio(1920, 1080, &mapping, audio_frame, false); + } + + #[cfg(not(feature = "fluid-synth"))] + encoder.encode_bgra(1920, 1080, &mapping, false); + print!( "\r Encoded {} frames ({}s, {}%) in {}s", n, From 3239efc32aa1239b4855e048583eaad6e1818905 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 14:57:58 -0600 Subject: [PATCH 14/16] Refactor audio frame handling in Recorder to improve performance and clarity --- neothesia-cli/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index d170e2b..e14d4ca 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -221,7 +221,7 @@ impl Recorder { // Render audio for this frame (1/60th of a second) let samples_per_frame = (44100.0 * (1.0/60.0)) as usize; let mut frame_audio = vec![0.0f32; samples_per_frame * 2]; // Stereo - synth.write(&mut frame_audio).unwrap_or(0); + synth.write(&mut frame_audio[..]).ok(); // Pass slice instead of Vec self.audio_buffer.extend_from_slice(&frame_audio); } @@ -355,7 +355,7 @@ fn main() { "./out/video.mp4", recorder.width as usize, recorder.height as usize, - Some(44100), // Set audio sample rate + Some(44100.0), // Use f32 for sample rate Some("medium"), ); @@ -389,11 +389,11 @@ fn main() { } else { &[] }; - encoder.encode_bgra_with_audio(1920, 1080, &mapping, audio_frame, false); + encoder.encode_frame(1920, 1080, &mapping, Some(audio_frame), false); } #[cfg(not(feature = "fluid-synth"))] - encoder.encode_bgra(1920, 1080, &mapping, false); + encoder.encode_frame(1920, 1080, &mapping, None, false); print!( "\r Encoded {} frames ({}s, {}%) in {}s", From d9bcc2f1825eb78e668891ed406c2ed8d0b5ed58 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 15:08:14 -0600 Subject: [PATCH 15/16] Update audio rendering to use 1024 samples per frame for AAC support --- neothesia-cli/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index e14d4ca..9cfb743 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -218,10 +218,10 @@ impl Recorder { } } - // Render audio for this frame (1/60th of a second) - let samples_per_frame = (44100.0 * (1.0/60.0)) as usize; + // Render audio for this frame, using 1024 samples per frame for AAC + let samples_per_frame = 1024; let mut frame_audio = vec![0.0f32; samples_per_frame * 2]; // Stereo - synth.write(&mut frame_audio[..]).ok(); // Pass slice instead of Vec + synth.write(&mut frame_audio[..]).ok(); self.audio_buffer.extend_from_slice(&frame_audio); } From 8ad129b0863d0168ddb863ae4ebfabaf43580088 Mon Sep 17 00:00:00 2001 From: Guy Chronister Date: Thu, 19 Dec 2024 15:11:46 -0600 Subject: [PATCH 16/16] Enhance video encoding by adding audio frame encoding and removing unnecessary frame encoding logic --- neothesia-cli/src/main.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 9cfb743..5c87736 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -372,13 +372,13 @@ fn main() { { let slice = output_buffer.slice(..); - slice.map_async(wgpu::MapMode::Read, move |_| {}); - recorder.gpu.device.poll(wgpu::Maintain::Wait); - let mapping = slice.get_mapped_range(); + // Encode video frame + encoder.encode_bgra(1920, 1080, &mapping, false); + #[cfg(feature = "fluid-synth")] { let samples_per_frame = (44100.0 * (1.0/60.0)) as usize; @@ -389,12 +389,11 @@ fn main() { } else { &[] }; - encoder.encode_frame(1920, 1080, &mapping, Some(audio_frame), false); + if !audio_frame.is_empty() { + encoder.encode_audio(audio_frame); + } } - #[cfg(not(feature = "fluid-synth"))] - encoder.encode_frame(1920, 1080, &mapping, None, false); - print!( "\r Encoded {} frames ({}s, {}%) in {}s", n,