Hi,

I'm trying to do a simple effect in the frequency domain, nothing extraordinary.
I need to apply various gains to specific frequencies.

For this purpose, I want to process a 16bit audio input stream, block by block,
apply a hanning window, do a fft, now apply my effect, and then an inverse fft,
followed by overlap and add buffering.

For now, I'm simply trying to do all of this without applying any effect, just
to see if my time to frequency and back conversion is correct.

I managed to do this in Octave. Now, I'm trying to do it in C with fftw and the
sound I obtain is like "shivering". It's recognizable but severely altered. I've
spent hours on this, it just won't work.

Attached :
- freqfilter.m: my working octave version
- transform.h and transform.c: time -> frequency -> time routines
- transformtest.c: test program

Could anyone tell me what's wrong with this C code?

Thanks in advance

--
  Olivier
blocksize=2048;

args=argv();

if length(args) != 3
  printf('Usage: infile nframes outfile\n');
  printf('Remark: use nframes=-1 to read the full infile\n');
  exit(1)
end

[signal, samplerate, bits] = wavread(args{1});
signal = signal(:,1);

signallen = str2num(args{2});
if signallen < 0 || signallen > length(signal)
    signallen = length(signal);
end  

printf('read signal of length %d\n', length(signal));
printf('processing %d frames\n', signallen);

maxlevel=max(abs(signal))

output=[];

window=hanning(blocksize);

for start = 1:blocksize/2:signallen
    stop = start + blocksize - 1;

    block = zeros(blocksize,1);
    if stop > signallen
        stop = signallen;
    end
    block(1:stop - start + 1) = signal(start:stop);
    block = block .* window;
    freq_block = fft(block, blocksize)(1:blocksize / 2);

    % effect
    % for ex: freq_block(64:blocksize/2) = 0;

    block = real(ifft(freq_block, blocksize));
    if length(output) < start + blocksize - 1
        output(length(output) + 1: start + blocksize - 1) = 0;
    end
    output(start:start + blocksize - 1) += transpose(block);
end

output = transpose(output);

gain = maxlevel / max(abs(output));
printf('applying gain: %f\n', gain);
output = output * gain;

printf('writing output');
wavwrite(output, samplerate, bits, args{3});
#ifndef TRANSFORM_H
#define TRANSFORM_H

#include <stdint.h>
#include <fftw3.h>

typedef struct Transform Transform;
typedef void (* TransformCallback)(fftwf_complex *freqdata, void *data);

Transform *     transform_new(int blocksize, TransformCallback callback, void *data);
void            transform_destroy(Transform *self);

/* Convert blocksize input frames and calls callback with the freqdata */
void            transform_push(Transform *self, int16_t *timedata);

/* Return blocksize/2 output frames */
int16_t *       transform_pop(Transform *self);

#endif // TRANSFORM_H
#include <stdlib.h>
#include <math.h>
#include "transform.h"

struct Transform {
    int                 blocksize;
    TransformCallback   callback;
    void *              callback_data;
    fftwf_plan          fft;
    fftwf_plan          ifft;
    float *             overlap; 
    int16_t *           outframes;
    int                 windex;
    int                 rindex;
    int                 rlen;
    float   *           hanning;
    float   *           real;
    fftwf_complex *     cplx;
    int                 begun;
    float               scale;
};

static void generate_hanning(float *window, int size) {
    int i;
    float factor = 2.f * M_PI / (size - 1.f);
    for (i = 0; i < size; i++) {
        window[i] = (1.f - cos(i * factor)) / 2.f;
    }
}

Transform *transform_new(int blocksize, TransformCallback callback, void *data) {
    Transform *self     = calloc(1, sizeof(Transform));
    self->blocksize     = blocksize;
    self->callback      = callback;
    self->callback_data = data;
    self->cplx          = fftwf_malloc((blocksize / 2 + 1) *  sizeof(*self->cplx));
    self->real          = fftwf_malloc(blocksize * sizeof(*self->real));
    self->fft           = fftwf_plan_dft_r2c_1d(blocksize, self->real, self->cplx, FFTW_ESTIMATE);
    self->ifft          = fftwf_plan_dft_c2r_1d(blocksize, self->cplx, self->real, FFTW_ESTIMATE);
    self->overlap       = calloc(blocksize + blocksize / 2, sizeof(*self->overlap));
    self->outframes     = calloc(blocksize / 2, sizeof(*self->outframes));
    self->hanning       = calloc(blocksize, sizeof(*self->hanning));
    generate_hanning(self->hanning, blocksize);
    self->scale         = INT16_MAX / (float) blocksize;

    return self;
}

void transform_destroy(Transform *self) {
    fftwf_free(self->real);
    fftwf_free(self->cplx);
    free(self->hanning);
    free(self->overlap);
    fftwf_destroy_plan(self->ifft);
    fftwf_destroy_plan(self->fft);
    free(self);
}

void transform_push(Transform *self, int16_t *frames) {

    int i, ii;

    for (i = 0; i < self->blocksize; i++) {
        self->real[i] = frames[i] / (float) INT16_MAX * self->hanning[i];
    }

    fftwf_execute(self->fft);

    self->callback(self->cplx, self->callback_data);

    fftwf_execute(self->ifft);

    ii = self->blocksize / 2;
    float *overlap = self->overlap + self->windex * self->blocksize / 2;
    for (i = 0; i < ii; i++) {
        overlap[i] += self->real[i] * self->scale;
    }
    if (self->windex == 2) {
        overlap      = self->overlap;
        self->windex = 0;
    } else {
        overlap += ii;
        self->windex++;
    }

    float *real2 = self->real + ii;
    for (i = 0; i < ii; i++) {
        overlap[i] = real2[i] * self->scale;
    }

    if (!self->begun) {
        self->begun  = 1;
    } else {
        self->rindex = (self->windex + 1) % 3;
        self->rlen   = 2;
    }
}


int16_t * transform_pop(Transform *self) {
    int16_t *outframes = NULL;
    if (self->rlen) {
        float *overlap = self->overlap + self->rindex++ * self->blocksize / 2;
        self->rindex %= 3;
        self->rlen--;
        outframes = self->outframes;
        int i;
        for (i = 0; i < self->blocksize / 2; i++)
            outframes[i] = overlap[i];
    }
    return outframes;
}

/* 
 * Transform object test
 * 
 * Compile with: 
 * gcc -Wall -o transformtest transformtest.c transform.c -lsndfile -lfftw3f
 */

#include <stdio.h>
#include <sndfile.h>
#include <stdlib.h>
#include <string.h>
#include "transform.h"

#define BLOCKSIZE 2048

static void transformed(fftwf_complex *freqdata, void *data) {
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("Usage: infile outfile\n");
        return 1;
    }

    SF_INFO info;
    info.format = 0;
    SNDFILE *infile = sf_open(argv[1], SFM_READ, &info);
    SNDFILE *outfile = sf_open(argv[2], SFM_WRITE, &info);

    int16_t *buffer = calloc(BLOCKSIZE, sizeof(*buffer));
    Transform *transform = transform_new(BLOCKSIZE, transformed, NULL);

    while (1) {
        int read = sf_read_short(infile, buffer, BLOCKSIZE);
        if (read < BLOCKSIZE) {
            memset(buffer + read, 0, (BLOCKSIZE - read) * sizeof(*buffer));
        }
        transform_push(transform, buffer);
        int16_t *processed;
        while ((processed = transform_pop(transform)) != NULL) {
            sf_write_short(outfile, processed, BLOCKSIZE / 2);
        }
        if (read < BLOCKSIZE) {
            // losing the last half block
            break;
        }
    }

    transform_destroy(transform);

    sf_close(outfile);
    sf_close(infile);

    return 0;
}
_______________________________________________
Linux-audio-dev mailing list
[email protected]
http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev

Reply via email to