【发布时间】:2020-07-11 17:30:25
【问题描述】:
当播放期间 mp3 文件中的节拍或鼓声出现时,我想触发一个动作(例如让强光闪烁)。我不知道我应该采取的理论上的程序/方法。
首先我想到了在第一步中静态分析 MP3。分析的结果将是应该在哪个时间戳触发操作。然后我启动 MP3,另一个线程在特定时间启动动作。这应该很容易,因为我可以使用rodio-crate 进行播放。但是静态分析部分还是很重的。
分析算法:
我的想法是使用 minimp3-crate 从 MP3 读取原始音频数据,并使用 rustfft-crate 进行 FFT。当我从 FFT 进行频谱分析时,我可以查看高音量的深层频率,这应该是歌曲的节拍。
我尝试将 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
}
我的问题还在于 FFT 函数没有任何可以指定 sample_rate(即 48.000kHz)的参数。我从decoder.next_frame() 得到的只是Vec<i16> 2304 项..
有什么想法可以实现这一目标以及我目前得到的数字实际上意味着什么?
【问题讨论】:
-
你解决了这个问题吗?我遇到了同样的问题(不是相同的语言,而是相同的概念),而且似乎没有很好的记录
-
是的,我做到了。我发布了这个@Maxime 的答案