Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symphonia MP3 decoding has been very slow. #308

Open
GameBakerStudio opened this issue Aug 14, 2024 · 4 comments
Open

Symphonia MP3 decoding has been very slow. #308

GameBakerStudio opened this issue Aug 14, 2024 · 4 comments

Comments

@GameBakerStudio
Copy link

While I don't believe this is an issue related directly to the software of Symphonia, I think this issue will still apply here. (I also can't post on StackOverflow so please).

I attempted to use Symphonia to decode MP3 files in my application. I got it to work "following" the example documentation, but it's taking a very long time. 17 seconds is the minimum amount of time I can find to decode files of even very small duration (1-2 minutes). I tried using Rayon; got it down to 3 seconds, but apparently MP3 must be sequentially processed? I guess I am asking if anyone can help me get this time down? I don't expect it to be instantaneous, but I didn't expect it to take 15-17 seconds.

Here's my code. (All parameters and external software are working correctly/valid)

use std::time::Duration;

use symphonia::core::io::{MediaSourceStream, MediaSourceStreamOptions};
use symphonia::core::probe::Hint;
use symphonia::core::formats::FormatOptions;
use symphonia::core::codecs::DecoderOptions;
use symphonia::core::errors::Error;
use symphonia::default::get_probe;
use symphonia_core::meta::MetadataOptions;
use symphonia_core::audio::{AudioBufferRef, SampleBuffer};
use std::fs::File;
use std::path::Path;
use std::io::BufReader;
use std::thread;

use std::sync::{Arc, Mutex};

use symphonia_core::audio::Signal;


pub(crate) fn decode_mp3_file(file_path: &str) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
    // Path to the input audio file
    let audio_path = Path::new(file_path);

    // Open the input audio file
    let file = File::open(audio_path).expect("Failed to open input audio file");

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

    // Create a hint to help the format registry guess the format
    let mut hint = Hint::new();

    // Use the file extension to assist in guessing
    hint.with_extension("mp3");

    // Probe the media source
    let probed = get_probe()
        .format(&hint, mss, &FormatOptions::default(), &MetadataOptions::default())
        .expect("Failed to probe media source");

    // Get the instantiated format reader
    let mut format = probed.format;

    // Get the default track
    let track = format.default_track().expect("No tracks found");

    // Create a decoder for the track
    let mut decoder = symphonia::default::get_codecs().make(&track.codec_params, &DecoderOptions::default()).expect("Failed to create decoder");

    println!("Decoding Each Packet.");

    let start_time = std::time::Instant::now();


    // Store the track identifier, we'll use it to filter packets.
    let track_id = track.id;

    let mut sample_count = 0;

    let mut output_pcm: Vec<f32> = Vec::new();

    loop {
        
        let packet = match format.next_packet() {
            Ok(packet) => packet,
            Err(symphonia::core::errors::Error::IoError(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
                break;
            }
            Err(err) => return Err(Box::new(err)),
        };

        match decoder.decode(&packet) {

            Ok(audio_buf) => {

                // Get the audio buffer specification.
                let spec = *audio_buf.spec();

                // Get the capacity of the decoded buffer. Note: This is capacity, not length!
                let duration = audio_buf.capacity() as u64;

                // Create the f32 sample buffer.
                let mut sample_buf = Some(SampleBuffer::<f32>::new(duration, spec));



                if let Some(buf) = &mut sample_buf {
                    buf.copy_interleaved_ref(audio_buf);

                    let pcm_data = buf.samples().to_vec();
                    output_pcm.extend(pcm_data);

                }

            },
            Err(Error::DecodeError(_)) => (),
            Err(_) => break,

        }

    }
    println!("Time to decode: {} seconds", std::time::Instant::now().duration_since(start_time).as_secs());



    println!("Done Decoding mp3");
    Ok(output_pcm)
}

Sorry code isn't documented :( I'm new to this.

@a1phyr
Copy link
Contributor

a1phyr commented Sep 3, 2024

Running the code makes it go from 15s to 300ms for me (which is arguably still a lot), this is probably what you are missing here. Even opt-level = 1 is enough to make to duration acceptable in debug mode.

@GameBakerStudio
Copy link
Author

Running the code makes it go from 15s to 300ms for me (which is arguably still a lot), this is probably what you are missing here. Even opt-level = 1 is enough to make to duration acceptable in debug mode.

@a1phyr It seems you found a solution for this? I don't know what you mean by "running the code", as thats.. what I've been doing? The 15 (13-17) seconds is the time it takes in debug mode (my current only option). What is opt-level ?

@a1phyr
Copy link
Contributor

a1phyr commented Sep 7, 2024

Whoops sorry I forgot some words, I meant "Running the code in release mode".

If you don't want to use release mode but still acceptable performance, you can change the optimization level by adding this to your ̀Cargo.toml`

[profile.dev]
opt-level = 1

@hasezoey
Copy link

i dont know how much it would affect it for your use-case, but you could:

(at least those are the things we do in our implementation for symphonia decoding)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants