Skip to content

Commit

Permalink
Reimplement audio_length based off of Kapu's more thorough implemen…
Browse files Browse the repository at this point in the history
…tation from tgstation/rust-g#192
  • Loading branch information
Absolucy committed Dec 26, 2024
1 parent 1d80417 commit aeee512
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 52 deletions.
80 changes: 53 additions & 27 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/audio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ publish.workspace = true

[dependencies]
meowtonin = { workspace = true }
symphonia = { version = "0.5", default-features = false, features = ["mp3", "vorbis", "ogg", "wav"] }
symphonia = { version = "0.5", default-features = false, features = ["all-codecs"] }
77 changes: 53 additions & 24 deletions crates/audio/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,76 @@
// SPDX-License-Identifier: MPL-2.0

use meowtonin::byond_fn;
use std::{ffi::OsStr, fs::File, path::PathBuf, time::Duration};
use symphonia::core::{
formats::FormatOptions,
io::{MediaSourceStream, MediaSourceStreamOptions},
meta::MetadataOptions,
probe::Hint,
use symphonia::{
core::{
codecs::DecoderOptions,
formats::FormatOptions,
io::{MediaSourceStream, MediaSourceStreamOptions},
meta::MetadataOptions,
probe::{Hint, ProbeResult},
},
default::{get_codecs, get_probe},
};

#[byond_fn]
pub fn audio_length(path: PathBuf) -> Option<u32> {
pub fn audio_length(path: PathBuf) -> f32 {
// If the path has an extension, include it as a hint.
let mut hint = Hint::new();
if let Some(extension) = path.extension().and_then(OsStr::to_str) {
hint.with_extension(extension);
}

// Open the file
let file = File::open(path).ok()?;
let file = File::open(&path)
.map(Box::new)
.expect("failed to open audio file");

// Create a media source stream
let mss = MediaSourceStream::new(Box::new(file), MediaSourceStreamOptions::default());
let mss = MediaSourceStream::new(file, MediaSourceStreamOptions::default());

// Use default options for format and metadata
let format_opts = FormatOptions::default();
let metadata_opts = MetadataOptions::default();

// Probe the media source
let probed = symphonia::default::get_probe()
let probed = get_probe()
.format(&hint, mss, &format_opts, &metadata_opts)
.ok()?;
.expect("failed to probe audio");

// Get the default track
let track = probed.format.default_track()?;
sound_length_simple(&probed).unwrap_or_else(|| sound_length_decode(probed)) as f32
}

// Calculate the duration
fn sound_length_simple(probed: &ProbeResult) -> Option<f64> {
let track = probed.format.default_track()?;
let time_base = track.codec_params.time_base?;
let duration = track.codec_params.n_frames.map(|frames| {
let time = time_base.calc_time(frames);
Duration::from_secs(time.seconds) + Duration::from_secs_f64(time.frac)
})?;
let n_frames = track.codec_params.n_frames?;

let time = time_base.calc_time(n_frames);
let duration = Duration::from_secs(time.seconds) + Duration::from_secs_f64(time.frac);

Some(duration.as_secs_f64() * 10.0)
}

fn sound_length_decode(probed: ProbeResult) -> f64 {
let mut format = probed.format;

let track = format.default_track().expect("could not get default track");

// Grab the number of frames of the track
let samples_capacity = track.codec_params.n_frames.unwrap_or(0) as f64;

// Create a decoder using the provided codec parameters in the track.
let decoder_opts = DecoderOptions::default();
let mut decoder = get_codecs()
.make(&track.codec_params, &decoder_opts)
.expect("decoder creation error");

// Try to grab a data packet from the container
let encoded_packet = format.next_packet().expect("next_packet error");

// Try to decode the data packet
let decoded_packet = decoder
.decode(&encoded_packet)
.expect("failed to decode packet");

// Convert to deciseconds
Some((duration.as_secs_f64() * 10.0).ceil() as u32)
// Grab the sample rate from the spec of the buffer.
let sample_rate = decoded_packet.spec().rate as f64;
// Math!
samples_capacity / sample_rate * 10.0
}

0 comments on commit aeee512

Please sign in to comment.