【发布时间】:2021-09-23 22:23:22
【问题描述】:
我正在用 C++ 编写一些代码,我想在其中播放 .wav 文件并在它出现时对其执行 FFT(使用 fftw)(并最终使用 ncurses 在屏幕上显示该 FFT)。这主要是作为一个“为了咯咯笑/看看我能不能”的项目,所以我对我可以或不能使用的东西没有任何限制,除了想尽量保持结果相当轻量级和跨平台(我'我暂时在 Linux 上这样做)。我也在努力做到这一点“正确”,而不仅仅是一起破解。
我正在使用 SDL2_audio 来实现播放,效果很好。回调在某个时间间隔被调用,请求 N 个字节(似乎是desiredSamples*nChannels)。我的想法是,在将内存从输入缓冲区复制到 SDL 的同时,我还不如将其复制到 fftw3 的输入数组中以在其上运行 FFT。然后我可以将ncurses 设置为以任何我想要的频率刷新,而不是音频回调频率,它只会从输出数组中提取最新数据。
问题是输入文件的格式是通道打包在一起的。即“(LR)(LR)(LR)......”。因此,虽然 SDL 期望这一点,但我需要一种方法来让一个通道发送到 FFTW。
来自 SDL 的音频回调格式如下所示:
void myAudioCallback(void* userdata, Uint8* stream, int len) {
SDL_memset(stream, 0, sizeof(stream));
SDL_memcpy(stream, audio_pos, len);
audio_pos += len;
}
其中userdata(当前)未使用,stream 是 SDL 想要填充的数组,len 是 stream 的长度(即 SDL 正在寻找的字节数)。
据我所知,没有办法让memcpy 复制所有其他样本(阅读:复制 N 个字节、跳过 M、复制 N 等)。我目前最好的想法是蛮力 for 循环...
// pseudocode
for (int i=0; i<len/2; i++) {
fftw_in[i] = audio_pos + 2*i*sizeof(sample)
}
甚至通过第二次读取文件并且只读取每隔一个字节或其他东西来获得更大的暴力。
还有其他方法可以实现这一点,还是其中一个是我最好的选择?从一个不错的单行 memcpy 将数据发送到 SDL 到某种奇怪的循环以将其发送到 fftw 感觉有点笨拙。
【问题讨论】:
-
(我和 OP 是同一个人。显然点击“使用 GitHub 登录”链接并没有指向与我的常规帐户相同的位置,即使我认为它们是相同的 :P)
-
那么,您的问题基本上是如何从字节数组中读取每个其他字节?
-
我认为循环一点也不奇怪。它可以满足您的要求 - 存储音频中的所有其他字节。 Memcpy 速度很快,因为它能够存储大块的顺序数据。如果你想跳过字节,你显然不能这样做。这是否会导致严重的性能问题,或者您只是想知道代码是否看起来不错?
-
如果您想要对数据做的只是计算 fft,那么甚至不必复制您的数据。事实上,fftw 支持在多个打包通道上执行多个 ffts。见here。