Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions crates/rustmotion/src/cli/commands/sheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,7 @@ pub fn cmd_sheet(
));
}

if !scenario.fonts.is_empty() {
engine::renderer::load_custom_fonts(&scenario.fonts);
}

for view in &scenario.views {
engine::prefetch_icons(&view.scenes)?;
}
engine::preload::preload_scenario_assets(&scenario)?;

let start_time = std::time::Instant::now();
let config = &scenario.video;
Expand Down
8 changes: 1 addition & 7 deletions crates/rustmotion/src/cli/commands/still.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,7 @@ pub fn cmd_still(
format: Option<String>,
quality: u8,
) -> Result<()> {
if !scenario.fonts.is_empty() {
engine::renderer::load_custom_fonts(&scenario.fonts);
}

for view in &scenario.views {
engine::prefetch_icons(&view.scenes)?;
}
engine::preload::preload_scenario_assets(&scenario)?;

let config = &scenario.video;
let fps = config.fps;
Expand Down
19 changes: 18 additions & 1 deletion crates/rustmotion/src/engine/preload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use std::io::Read;
use std::sync::Arc;

use crate::components::{ChildComponent, Component};
use crate::schema::Scene;
use crate::encode::audio_analysis::analyze_scenario_audio;
use crate::schema::{ResolvedScenario, Scene};
use rustmotion_core::engine::renderer::{
asset_cache, fetch_icon_svg, ffmpeg_available, icon_cache_dir, icon_cache_key,
video_frame_cache,
Expand Down Expand Up @@ -34,6 +35,22 @@ fn video_frame_cache_bytes() -> u64 {
.sum()
}

pub fn preload_scenario_assets(scenario: &ResolvedScenario) -> crate::error::Result<()> {
if !scenario.fonts.is_empty() {
rustmotion_core::engine::renderer::load_custom_fonts(&scenario.fonts);
}
for view in &scenario.views {
prefetch_icons(&view.scenes)?;
}
for failure in analyze_scenario_audio(scenario) {
eprintln!(
"rustmotion: audio-reactive: {failure} — waveform/audio_spectrum will render flat \
for this track."
);
}
Ok(())
}

pub fn prefetch_icons(scenes: &[Scene]) -> crate::error::Result<()> {
use std::collections::HashSet;

Expand Down
56 changes: 54 additions & 2 deletions crates/rustmotion/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ mod audio_tests {

use rustmotion_core::engine::renderer::audio_analysis::{audio_analysis_cache, AudioAnalysis};

fn make_sine_wav(
pub(super) fn make_sine_wav(
total_samples: u32,
sine_samples: u32,
freq: f32,
Expand Down Expand Up @@ -1629,7 +1629,7 @@ mod audio_tests {
}
wav
}
fn nanos() -> u128 {
pub(super) fn nanos() -> u128 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
Expand Down Expand Up @@ -3770,3 +3770,55 @@ mod self_painting_background_animation_tests {
);
}
}

#[cfg(test)]
mod still_runs_the_audio_analysis_tests {
use super::audio_tests::{make_sine_wav, nanos};
use rustmotion_core::engine::renderer::audio_analysis_cache;

fn scenario_json(wav: &str) -> String {
serde_json::json!({
"video": { "width": 64, "height": 64, "fps": 30 },
"audio": [{ "src": wav }],
"scenes": [{
"duration": 1.0,
"children": [{
"type": "audio_spectrum", "bars": 8, "mode": "bars", "color": "#ffffff",
"style": { "position": "absolute", "left": 0, "top": 0,
"width": 64, "height": 64 }
}]
}]
})
.to_string()
}

#[test]
fn the_shared_preload_analyses_the_track_the_way_an_encode_does() {
let sample_rate = 44100u32;
let wav = make_sine_wav(sample_rate, sample_rate / 2, 440.0, sample_rate);
let wav_path = std::env::temp_dir().join(format!("rm_still_audio_{}.wav", nanos()));
std::fs::write(&wav_path, &wav).expect("write wav fixture");
let src = wav_path.to_str().unwrap().to_string();

let scenario = crate::loader::load_scenario_from_source(None, Some(&scenario_json(&src)))
.expect("load scenario");

audio_analysis_cache().remove(&src);
assert!(
audio_analysis_cache().get(&src).is_none(),
"test setup: the track must not already be analysed"
);

crate::engine::preload::preload_scenario_assets(&scenario).expect("preload");

let analysed = audio_analysis_cache().get(&src).is_some();
std::fs::remove_file(&wav_path).ok();

assert!(
analysed,
"still and sheet went through this preamble without analysing the track, so every \
audio-reactive component in an exported frame sat at its min — the flat bars \
reported in issue #349"
);
}
}
Loading