I am writing bit of code in C where I want to play a .wav
file and perform an FFT (with fftw
) on it as it comes (and eventually display that FFT on screen with ncurses
). This is mainly just as a "for giggles/to see if I can" project, so I have no restrictions on what I can or can't use aside from wanting to try to keep the result fairly lightweight and cross-platform (I'm doing this on Linux for the moment). I'm also trying to do this "right" and not just hack it together.
I'm using SDL2_audio to achieve the playback, which is working fine. The callback is called at some interval requesting N bytes (seems to be desiredSamples*nChannels). My idea is that at the same time I'm copying the memory from my input buffer to SDL I might as well also copy it in to fftw3's input array to run an FFT on it. Then I can just set ncurses
to refresh at whatever rate I'd like separate from the audio callback frequency and it'll just pull the most recent data from the output array.
The catch is that the input file is formatted where the channels are packed together. I.E "(LR) (LR) (LR) ...". So while SDL expects this, I need a way to just get one channel to send to FFTW.
The audio callback format from SDL looks like so:
void myAudioCallback(void* userdata, Uint8* stream, int len) {
SDL_memset(stream, 0, sizeof(stream));
SDL_memcpy(stream, audio_pos, len);
audio_pos = len;
}
where userdata
is (currently) unused, stream
is the array that SDL wants filled, and len
is the length of stream
(I.E the number of bytes SDL is looking for).
As far as I know there's no way to get memcpy
to just copy every other sample (read: Copy N bytes, skip M, copy N, etc). My current best idea is a brute-force for
loop a la...
// pseudocode
for (int i=0; i<len/2; i ) {
fftw_in[i] = audio_pos 2*i*sizeof(sample)
}
or even more brute force by just reading the file a second time and only taking every other byte or something.
Is there another way to go about accomplishing this, or is one of these my best option? It feels kind of kludgey to go from a nice one line memcpy to send to the data to SDL to some sort of weird loop to send it to fftw.
CodePudding user response:
Very hard OP's solution can be simplified (for copying bytes):
// pseudocode
const char* s = audio_pos;
for (int d = 0; s < audio_pos len; d , s = 2*sizeof(sample)) {
fftw_in[d] = *s;
}
If I new what fftw_in
is, I would memcpy blocks sizeof(*fftw_in)
.
CodePudding user response:
Please check assembly generated by @S.M.'s solution.
If the code is not vectorized, I would use intrinsics (depending on your hardware support) like _mm_mask_blend_epi8