Symphonia icon indicating copy to clipboard operation
Symphonia copied to clipboard

How to load Metadate from the Files?

Open DRUMNICORN opened this issue 1 year ago • 6 comments

I want to load ID3 other Audio Metadata from a File.

title, artists, bpm, etc...

DRUMNICORN avatar Aug 11 '22 10:08 DRUMNICORN

use symphonia::core::codecs::{CODEC_TYPE_NULL, DecoderOptions};
use symphonia::core::errors::Error;
use symphonia::core::formats::FormatOptions;
use symphonia::core::meta::MetadataOptions;
use symphonia::core::io::MediaSourceStream;
use symphonia::core::probe::Hint;

pub fn main() {
    let path: String = "D:\\Downloads\\twenor-libary\\public\\audio_2.wav".to_string();

    // Open the media source.
    let src = std::fs::File::open(&path).expect("failed to open media");

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

    // Create a probe hint using the file's extension. [Optional]
    let mut hint = Hint::new();
    hint.with_extension("mp3");

    // Use the default options for metadata and format readers.
    let meta_opts: MetadataOptions = Default::default();
    let fmt_opts: FormatOptions = Default::default();

    // Probe the media source.
    let probed = symphonia::default::get_probe().format(&hint, mss, &fmt_opts, &meta_opts)
                                                .expect("unsupported format");

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

    // Find the first audio track with a known (decodeable) codec.
    let track = format.tracks()
                    .iter()
                    .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
                    .expect("no supported audio tracks");

    // Use the default options for the decoder.
    let dec_opts: DecoderOptions = Default::default();

    // Create a decoder for the track.
    let mut decoder = symphonia::default::get_codecs().make(&track.codec_params, &dec_opts)
                                                    .expect("unsupported codec");

    // Store the track identifier, it will be used to filter packets.
    let track_id = track.id;

    // The decode loop.
    loop {
        // Get the next packet from the media format.
        let packet = match format.next_packet() {
            Ok(packet) => packet,
            Err(Error::ResetRequired) => {
                // The track list has been changed. Re-examine it and create a new set of decoders,
                // then restart the decode loop. This is an advanced feature and it is not
                // unreasonable to consider this "the end." As of v0.5.0, the only usage of this is
                // for chained OGG physical streams.
                unimplemented!();
            }
            Err(err) => {
                // A unrecoverable error occured, halt decoding.
                panic!("{}", err);
            }
        };

        // Consume any new metadata that has been read since the last packet.
        while !format.metadata().is_latest() {
            // Pop the old head of the metadata queue.
            format.metadata().pop();

            // Consume the new metadata at the head of the metadata queue.
        }

        // If the packet does not belong to the selected track, skip over it.
        if packet.track_id() != track_id {
            continue;
        }

        // Decode the packet into audio samples.
        match decoder.decode(&packet) {
            Ok(_decoded) => {
                // Consume the decoded audio samples (see below).
            }
            Err(Error::IoError(_)) => {
                // The packet failed to decode due to an IO error, skip the packet.
                continue;
            }
            Err(Error::DecodeError(_)) => {
                // The packet failed to decode due to invalid data, skip the packet.
                continue;
            }
            Err(err) => {
                // An unrecoverable error occured, halt decoding.
                panic!("{}", err);
            }
        }
    }
}
thread` 'main' panicked at 'end of stream', src\test_read.rs:62:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

DRUMNICORN avatar Aug 11 '22 11:08 DRUMNICORN

Repository has symphonia-play, there is an example of what you are looking for (probe-only) link

SuisChan avatar Aug 11 '22 12:08 SuisChan

I had tryed to get into the Code but in my Tests it wont work. If someone could pase a Example Code for following case:

  1. Load a Audio File by defined Path variable
  2. Print out the Title of the Audio File in Console
  3. Set Album of Audio File to the String "Test"

If anyone could wrap smt around this, you could also add it to the Documentation^^.

DRUMNICORN avatar Aug 11 '22 16:08 DRUMNICORN

grafik I want to edit these Metadata + if possible ID3 and EXIFF later on

DRUMNICORN avatar Aug 11 '22 16:08 DRUMNICORN

OK that code is working in my env for reading metadata!!

use symphonia::core::formats::FormatOptions;
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::{MetadataOptions, MetadataRevision};
use symphonia::core::probe::Hint;

pub fn main() {
    let path: String = "D:\\Downloads\\twenor-libary\\public\\rick.mp3".to_string();
    let src = std::fs::File::open(&path).expect("failed to open media");
    let mss = MediaSourceStream::new(Box::new(src), Default::default());
    let mut hint = Hint::new();
    hint.with_extension("mp3");
    let meta_opts: MetadataOptions = Default::default();
    let fmt_opts: FormatOptions = Default::default();
    let mut probed = symphonia::default::get_probe()
        .format(&hint, mss, &fmt_opts, &meta_opts)
        .expect("unsupported format");

    if let Some(metadata_rev) = probed.format.metadata().current() {
        print_tags(&metadata_rev);
        if probed.metadata.get().as_ref().is_some() {
            println!("tags that are part of the container format are preferentially printed.");
            println!("not printing additional tags that were found while probing.");
        }
    } else if let Some(metadata_rev) = probed.metadata.get().as_ref().and_then(|m| m.current()) {
        print_tags(&metadata_rev);
    }
}

fn print_tags(metadata_rev: &MetadataRevision) {
    let tags = metadata_rev.tags();
    for (index, tag) in tags.iter().enumerate() {
        if tag.to_string().len() > 400 {
            continue;
        }
        println!("{}: {:?}", index, tag);
    }
}

So how now to Edit Metadata

How to rewrite A Tag for Example this one?

5: Tag { std_key: Some(Genre), key: "TCON", value: String("Rap") }

DRUMNICORN avatar Aug 11 '22 16:08 DRUMNICORN

How to rewrite

Symphonia doesn't support writing metadata (?), but alternatively you can look at this lofty-rs

SuisChan avatar Aug 11 '22 16:08 SuisChan

Thank you @SuisChan for answering @cherob's question!

I'm going to close this, but feel free to re-open if there's any further problems.

pdeljanov avatar Oct 12 '22 02:10 pdeljanov

Hi @pdeljanov ! Isn't it worth to create an example out of this to put in here?

daniellga avatar Feb 21 '23 19:02 daniellga