Как анализировать MP3 на предмет временных меток ритма / ударных, действий триггеров и воспроизведения одновременно (Rust) - PullRequest
0 голосов
/ 11 июля 2020

Я хочу активировать действие (например, дать яркому свету sh), когда во время воспроизведения присутствуют бит или ударные в файле mp3. Я не знаю теоретически процедуры / подхода, который мне следует предпринять.

Сначала я подумал о статическом анализе MP3 на первом этапе. Результатом анализа будет то, в какие отметки времени должно запускаться действие. Затем я запускаю MP3, и другой поток запускает действия в заданное время c. Это должно быть легко, потому что я могу использовать rodio -crate для воспроизведения. Но stati c анализирует части по-прежнему тяжело.

Алгоритм анализа:

Моя идея заключалась в том, чтобы прочитать необработанные аудиоданные из MP3 с использованием minimp3 - crate и выполните БПФ с помощью rustfft -crate. Когда у меня есть анализ спектра с помощью БПФ, я могу смотреть, где глубокие частоты находятся на большой громкости, и это должно быть ритм песни.

Я пробовал комбинировать minimp3 и rustfft, но у меня абсолютно не знаю, что на самом деле означают данные, которые я получаю .. И я тоже не могу написать для этого тест ..

Пока что это мой подход:

use minimp3::{Decoder, Frame, Error};

use std::fs::File;
use std::sync::Arc;
use rustfft::FFTplanner;
use rustfft::num_complex::Complex;
use rustfft::num_traits::{Zero, FromPrimitive, ToPrimitive};

fn main() {
    let mut decoder = Decoder::new(File::open("08-In the end.mp3").unwrap());

    loop {
        match decoder.next_frame() {
            Ok(Frame { data, sample_rate, channels, .. }) => {
                // we only need mono data; because data is interleaved
                // data[0] is first value channel left, data[1] is first channel right, ...
                let mut mono_audio = vec![];
                for i in 0..data.len() / channels {
                    let sum = data[i] as i32 + data[i+1] as i32;
                    let avg = (sum / 2) as i16;
                    mono_audio.push(avg);
                }
                // unnormalized spectrum; now check where the beat/drums are 
                // by checking for high volume in low frequencies
                let spectrum = calc_fft(&mono_audio);
            },
            Err(Error::Eof) => break,
            Err(e) => panic!("{:?}", e),
        }
    }
}

fn calc_fft(raw_mono_audio_data: &Vec<i16>) -> Vec<i16> {
    // Perform a forward FFT of size 1234

    let len = raw_mono_audio_data.len();

    let mut input:  Vec<Complex<f32>> = vec![];
    //let mut output: Vec<Complex<f32>> = vec![Complex::zero(); 256];
    let mut spectrum: Vec<Complex<f32>> = vec![Complex::zero(); len];

    // from Vec<i16> to Vec<Complex<f32>>
    raw_mono_audio_data.iter().for_each(|val| {
        let compl = Complex::from_i16(*val).unwrap();
        input.push(compl);
    });

    let mut planner = FFTplanner::new(false);
    let fft = planner.plan_fft(len);
    fft.process(&mut input, &mut spectrum);

    // to Vec<i16>
    let mut output_i16 = vec![];
    spectrum.iter().for_each(|val| {
        if let Some(val) = val.to_i16() {
            output_i16.push(val);
        }
    });

    output_i16
}

Моя проблема также то, что функция БПФ не имеет параметра, где я могу указать sample_rate (что составляет 48,000 кГц). Все, что я получаю от decoder.next_frame(), - это Vec<i16> с 2304 элементами ..

Есть идеи, как я могу этого добиться и что на самом деле означают цифры, которые я получаю в настоящее время?

...