Skip to content

Commit

Permalink
feat(music-brainz): Enhance audio fingerprinting to return duration a…
Browse files Browse the repository at this point in the history
…nd add CLI for AcoustID identification
  • Loading branch information
Losses committed Jan 5, 2025
1 parent f1726a7 commit d79ec1f
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
48 changes: 48 additions & 0 deletions tag-editor/examples/music_brainz.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use anyhow::Result;
use clap::{Arg, Command};
use rusty_chromaprint::Configuration;
use tag_editor::music_brainz::{api::identify, fingerprint::calc_fingerprint};

#[tokio::main]
async fn main() -> Result<()> {
// Set up CLI arguments
let matches = Command::new("MusicBrainz Audio Identifier")
.version("1.0")
.author("Rune Developers")
.about("Identifies audio files using AcoustID")
.arg(
Arg::new("API_KEY")
.help("Sets the AcoustID API key")
.required(true)
.index(1),
)
.arg(
Arg::new("FILE_PATH")
.help("Sets the input audio file path")
.required(true)
.index(2),
)
.get_matches();

// Get the API key and file path from arguments
let api_key = matches.get_one::<String>("API_KEY").unwrap();
let file_path = matches.get_one::<String>("FILE_PATH").unwrap();

// Create Chromaprint configuration
let config = Configuration::default();

// Calculate fingerprint
let (fingerprint, duration) = calc_fingerprint(file_path, &config)?;

// Call the identify function
match identify(api_key, fingerprint, duration.as_secs().try_into()?).await {
Ok(response) => {
println!("Identification successful: {:?}", response);
}
Err(e) => {
eprintln!("Identification failed: {:?}", e);
}
}

Ok(())
}
20 changes: 13 additions & 7 deletions tag-editor/src/music_brainz/fingerprint.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
/**
* Source: https://github.com/darksv/rusty-chromaprint/blob/main/chromaprint/Cargo.toml
* Original License: MIT
*/
use std::path::Path;
use std::time::Duration;

use anyhow::Context;
use symphonia::core::audio::SampleBuffer;
Expand All @@ -18,7 +15,7 @@ use rusty_chromaprint::{Configuration, Fingerprinter};
pub fn calc_fingerprint(
path: impl AsRef<Path>,
config: &Configuration,
) -> anyhow::Result<Vec<u32>> {
) -> anyhow::Result<(Vec<u32>, Duration)> {
let path = path.as_ref();
let src = std::fs::File::open(path).context("failed to open file")?;
let mss = MediaSourceStream::new(Box::new(src), Default::default());
Expand Down Expand Up @@ -66,15 +63,17 @@ pub fn calc_fingerprint(
.context("initializing fingerprinter")?;

let mut sample_buf = None;
let mut total_frames = 0u64;

while let Ok(packet) = format.next_packet() {

if packet.track_id() != track_id {
continue;
}

match decoder.decode(&packet) {
Ok(audio_buf) => {
total_frames += audio_buf.frames() as u64;

if sample_buf.is_none() {
let spec = *audio_buf.spec();
let duration = audio_buf.capacity() as u64;
Expand All @@ -92,5 +91,12 @@ pub fn calc_fingerprint(
}

printer.finish();
Ok(printer.fingerprint().to_vec())

// Calculate the total duration in seconds.
// Calculate the total duration as a Duration object.
let total_duration_secs = total_frames / sample_rate as u64;
let total_duration_nanos = (total_frames % sample_rate as u64) * 1_000_000_000 / sample_rate as u64;
let total_duration = Duration::new(total_duration_secs, total_duration_nanos as u32);

Ok((printer.fingerprint().to_vec(), total_duration))
}

0 comments on commit d79ec1f

Please sign in to comment.