-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reimplement
audio_length
based off of Kapu's more thorough implemen…
…tation from tgstation/rust-g#192
- Loading branch information
Showing
3 changed files
with
107 additions
and
52 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |