初めまして。
当方、JUCE開発、デジタルフィルタ開発、プログラミング初心者です。
今回私はJUCEでダウンサンプリングをするオーディオプラグインの開発をしようとしています。そこで、私はまずフィルタを設計することからはじめました。
作成しようとしたフィルタはFIRフィルタです。しかし、作成したオーディオプラグインを使用して見ると、出力音声が一定間隔で0になってしまうという現象が起こってしまいました。
写真はサイン波を作成したFIRFilterに通して.wavで出力した波形の一部です。
void FirFilter4AudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
ScopedNoDenormals noDenormals;
const int totalNumInputChannels = getTotalNumInputChannels();
const int totalNumOutputChannels = getTotalNumOutputChannels();
// In case we have more outputs than inputs, this code clears any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
// This is here to avoid people getting screaming feedback
// when they first compile a plugin, but obviously you don't need to keep
// this code if your algorithm always overwrites all the output channels.
for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
if(UserParams[MasterBypass] >= 1.0f)
return;
// This is the place where you'd normally do the guts of your plugin's
// audio processing...
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
// float* channelData = buffer.getWritePointer (channel);
// ..do something to the data...
///////////////////イコライザー処理の開始///////////////////////
// 関数SetParameterに渡す引数の値を算出
float _sampleRate = getSampleRate();
UserParams[SampleRate] = _sampleRate;
float _frequency;
UserParams[Frequency] = 0.5664f; // カットオフ周波数を 1000Hz に設定
_frequency = 20.0f * pow(1000.0f, UserParams[Frequency]); // 中心周波数を決定する(範囲 20~20000 Hz)
if(channel < 2)
{
// フィルタ係数を算出して保持する関数を実行
// firFilter[channel].SetParameter(_sampleRate, _frequency, _q);
///////////////////////////////// 遅延器の数を設定 /////////////////////////////////////
// j = numDelayer(_frequency / _sampleRate);
j = firFilter[channel].numDelayer(1000. / _sampleRate);
/////////////////////////////////// ハニング窓 ///////////////////////////////////
float* w = new float[j+1];
float* b = new float[j+1];
firFilter[channel].hammingWindow(w, j+1);
/////////////////////////////////// fir LPF ///////////////////////////////////
firFilter[channel].SetParameter(_sampleRate, _frequency, j, w, b);
/////////////////////////////////// 書き込み ///////////////////////////////////
firFilter[channel].DoProcess(buffer.getWritePointer(channel), buffer.getNumSamples(), j, b);
delete[] w;
delete[] b;
}
}
}
以上がprocessBlockの中身です。
#include "FIR_filter.h"
#include
// コンストラクタと変数の初期化、インスタンス生成と同時にヘッダで宣言した変数を初期化する
FIR_filter::FIR_filter()
{}
// デストラクタ
FIR_filter::~FIR_filter() {}
// LPF
void FIR_filter::SetParameter(float samplerate, float frequency, int j, float w[], float b[])
{
int offset, m;
offset = j / 2;
for(m = (-j / 2); m < (j / 2) + 1; m++)
{
b[offset + m] = 2.0 * (frequency / samplerate) * sinc(2.0 * M_PI * (frequency / samplerate) * m);
}
for(m = 0; m < j+1; m++)
{
b[m] *= w[m];
}
}
// オーティオデータにフィルタを適用する関数
// float bufferPtr ... オーディオバッファのポインタ
// int bufferSize ... オーディオバッファのサイズ おそらく197サンプル
void FIR_filter::DoProcess(float* bufferPtr, int bufferSize, int j, float b[])
{
float* tmp = new float[bufferSize];
float* win = new float[bufferSize];
// hammingWindow(win, bufferSize);
for(int n = 0; n < bufferSize; n++) // オーディオバッファのサンプル数まで回す
{
float d = 0.0;
for(int mm = 0; mm < j+1; mm++) // タップ数回す
{
if((n - mm) >= 0)
{
d += b[mm] * bufferPtr[n - mm];
}
}
tmp[n] = d;
}
for(int n = 0; n < bufferSize; n++)
{
// bufferPtr[n] = tmp[n] * win[n]; // ここに窓をかけた
bufferPtr[n] = tmp[n];
}
delete[] tmp;
delete[] win;
}
float FIR_filter::sinc(float x)
{
float y;
if(x == 0.0)
{
y = 1.0;
}
else
{
y = sin(x) / x;
}
return y;
}
void FIR_filter::hammingWindow(float w[], int N)
{
int n;
// if(N % 2 == 0) // (j+1)が偶数の時
// {
// for(n = 0; n < N; n++)
// {
// w[n] = 0.5 - 0.5 * cos(2.0 * M_PI * n / N);
// }
// }
// else
// {
// for(n = 0; n < N; n++)
// {
// w[n] = 0.5 - 0.5 * cos(2.0 * M_PI * (n + 0.5) / N);
// // w[n] = 0.5 - 0.5 * cos(2.0 * M_PI * (n) / N);
// }
// }
for(n = 0; n < N; n++)
{
w[n] = 0.54 - 0.46 * cos(2.0 * M_PI * n / N);
}
}
int FIR_filter::numDelayer(float delta)
{
int j = (int)(3.1 / delta + 0.5) - 1; // 遅延器の数 // 136.21
// int j = (int)(3.1 / delta) - 1;
if(j % 2 == 1)
{
j++; // j+1が奇数なるように調整
}
return j;
}
以上が作成したFIRFilterのコード部分です。
私の考えでは、processBlockで呼ばれる入力が197サンプル(おそらく… はっきりわかってないです)なので、この関数が呼び出されるごとにFIRFilterの係数がリセットされているからだと考えているのですが、もしこの考えがあっているとするならば解決策などはあるのでしょうか。
初歩的な質問で申し訳ないのですが、よろしくお願いいたします。