Smile Engineering Blog

ジェイエスピーからTipsや技術特集、プロジェクト物語を発信します

FIRフィルタのフレーム処理

信号処理ではそのアルゴリズムによってフレーム単位で処理している場合が多いです。 フーリエ変換を行う場合は、そのサイズ(点数)でフレーム化されたりします。 このようなケースを考えたとき、フィルタもそのフレームサイズに適した形が都合が良く、高速化(最適化)に関しても利点が多いです。

FIRのフレーム処理

f:id:jspnet:20200306005824p:plain:right:w500 「適応フィルタを作ってみる」と、 「FIRフィルタを作って周波数特性を検証」 で、FIRのサンプルコードを紹介しましたが、1サンプルに対してフィルタ処理を行うサンプル処理でした。

今回は、フレーム単位で処理を行うFIRフィルタを作ってみます。

サンプルコード 【C言語

#define FIR_TAP   15  //  目的に合わせて調整する (今回は15で試してみます)
// clip
static short double_saturate(double in)
{
    if (in > 32767.0) in = 32767.0;
    if (in < -32768.0) in = -32768.0;
    return (short)in;
}
// FIRフィルタフレーム処理
void fir_frame(float* coef, short *mem, int n, short *in, short *out)
{
    int flen;
    double sum;
    short *buff;
    float *p_coef;
    short *p_buff;

    // buff update
    memcpy(mem, &mem[n], sizeof(short)*(FIR_TAP - 1));
    memcpy(&mem[FIR_TAP - 1], in, sizeof(short)*n);

    // convolution
    buff = &mem[FIR_TAP - 1];
    while (n--) {
        sum = 0.;
        p_coef = coef;
        p_buff = buff++;
        flen = FIR_TAP;
        while (flen--) {
            sum += *p_coef++ * (double)*p_buff--;
        }
        *out++ = double_saturate(sum);
    }
}

検証用のコード

PCMにWAVヘッダを付けるで紹介した検証用コードをフレームサイズで処理するように変更してみました。今回は、フレームサイズを128サンプルとしています。サンプリング周波数16kHzの場合で8msです。

サンプルコード 【C言語

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fir.h"
#include "wav_header.h"

#define FRAME_SIZE     128  // 16kHz 8ms (128サンプル)
#define WAV_FORMAT     1    // 1: WAVヘッダを追加, 0:PCM

void fir_frame(float* coef, short *mem, int n, short *in, short *out);
extern float g_filter_coef[FIR_TAP]; //フィルタ係数
short mem[FRAME_SIZE + FIR_TAP - 1]; //フィルタメモリ
long get_file_size(const char *FileName);
char* get_wav_header_16kHz(long wav_data_size);

int main(int argc, char **argv)
{
    FILE *fp_in;
    FILE *fp_out;

    int pcm_bytes = get_file_size(argv[1]);              // サイズを取得

#if WAV_FORMAT ///////////// WAVヘッダ //////////////////
    int wav_bytes = pcm_bytes / (FRAME_SIZE * 2);
    wav_bytes *= FRAME_SIZE * 2;
    char* wav_header = get_wav_header_16kHz(wav_bytes);  // WAVヘッダを取得
#endif
    if ((fp_in = fopen(argv[1], "rb")) == NULL) { // PCM入力ファイル
        printf("can not open %s\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    if ((fp_out = fopen(argv[2], "wb")) == NULL) { // PCM出力ファイル
        printf("can not open %s\n", argv[2]);
        exit(EXIT_FAILURE);
    }
    for (int i = 0; i < FIR_TAP; i++) mem[i] = 0; // フィルタメモリ初期化

#if WAV_FORMAT ///////////// WAVヘッダ //////////////////
    fwrite(wav_header, WAV_HEADER_SIZE, 1, fp_out);  // WAVヘッダ部分をファイル出力
#endif
    for (int i = 0; i < pcm_bytes; i += FRAME_SIZE * 2) {
        short in[FRAME_SIZE];
        short out[FRAME_SIZE];
        fread(in, sizeof(short), FRAME_SIZE, fp_in);

        fir_frame(g_filter_coef, mem, FRAME_SIZE, in, out); // FIRフレーム処理

        fwrite(out, sizeof(short), FRAME_SIZE, fp_out);
    }
    fclose(fp_in);
    fclose(fp_out);
    return EXIT_SUCCESS;
}

WAVヘッダのコード(get_wav_header_16kHz, get_file_size)は、こちら PCMにWAVヘッダを付ける
フィルタ係数(g_filter_coef)は、こちら FIRフィルタを作って周波数特性を検証