手始めにVFOを実装します。
仕様
VFOは以下のような仕様とします。
- 矩形波と三角波を生成できる
- 任意のFsを設定できる
- 任意の発振周波数を設定できる(Fs/4まで)
- デューティー・サイクルを変更できる
- block_size分の信号を一度に生成する
block_size分の信号を生成する際に、周波数スイープするような仕様は考えないものとします。
クラス宣言
クラス宣言は特に変哲の無いものです。コンストラクタ、デストラクタは自明です。run()は、信号生成のメソッドです。外部からパラメタを設定するメソッドを用意するほか、update_parameters()で内部変数をアップデートしています。このメソッドは外部からは呼びません。
// Variable Frequency Oscillator. Only square and triangle
class VFO {
public:
VFO( void );
virtual ~VFO(void);
void run(
float out_buffer[], // place to write the right output samples
unsigned int block_size // block size [sample]
);
// parameter settings
void set_frequency( int freq ); // unit is Hz.
void set_Fs( int Fs ); // unit is Hz.
void set_duty_cycle( float duty ); // 0 ... 0.5
void set_wave_form( wave_form form );
private:
// control variables.
int frequency; // VFO frequency [Hz]
int Fs; // sampling Frequency [Hz]
float duty_cycle; // VFO duty cycle. 0 ... 0.5
wave_form form; // form of the wave form.
int current_phase; // internal variable of VFO.
// internal variable.
int half_way; // change point by duty cycle. ( period * duty_cycle ).
float rising_rate;
float falling_rate;
void update_parameters(void); // call one of the parameter is changed.
};
実装
実装に当たっては、内部でhalf_wayメンバー変数を作りだし、これを元に現在の位相を計算しながら波形を計算します。矩形の場合、位相がhalf_wayに達していなければ出力は1、達していれば0です。三角波の場合、位相がhalf_wayに達していなければ上昇、達していれば下降です。
これらを簡単に行えるよう、update_parameters()メソッドでhalf_wayを計算します。ここでひとつポイントがあります。half_wayの最小値に気をつけなければなりません。矩形波のパルス幅が細くなりすぎると、位相の進み方によってはパルスの頭が出力されない場合があり得るからです。これを防ぐには、幅をfrequencyサンプル以上に制限します。
ソースコードを以下に示します。
#include "signal_processing.h"
VFO::VFO( void )
{
// initial parameter setting.
this->form = triangle;
this->Fs = 48000;
this->frequency = 440;
this->duty_cycle = 0.5;
this->update_parameters();
} // End of constructor()
VFO::~VFO( void )
{
// do nothing
}
void VFO::run(
float out_buffer[], // vfo output buffer
unsigned int block_size // block size [sample]
)
{
// place the signal processing coce here
for ( int i= 0; i< block_size; i++ )
{
// 1 : if phase < half_way; 0 : others.
if ( this->form == square )
{
if ( this->current_phase < this->half_way )
out_buffer[i] = 1.0;
else
out_buffer[i] = 0.0;
}
else // form == triangle
{
if ( this->current_phase < this->half_way )
out_buffer[i] = this->rising_rate * this->current_phase;
else
out_buffer[i] = 1 + this->falling_rate * ( this->current_phase - this->half_way );
}
// update phase
this->current_phase += this->frequency;
// limit the range of the phase.
if ( this->current_phase >= this->Fs )
this->current_phase -= this->Fs;
}
} // End of run()
void VFO::set_Fs( int Fs )
{
// regulate the Fs.
if ( Fs != 32000 && Fs != 44100 && Fs != 96000 && Fs != 48000 )
Fs = 48000;
this->Fs = Fs;
this->update_parameters();
}
void VFO::set_frequency( int freq )
{
if ( freq > this->Fs / 4 )
freq = Fs / 4;
this->frequency = freq;
this->update_parameters();
}
void VFO::set_duty_cycle( float duty )
{
if ( duty > 0.5f ) // high limit
duty = 0.5f;
if ( duty < 0.0f ) // low limit
duty = 0.0f;
this->duty_cycle = duty;
this->update_parameters();
}
void VFO::set_wave_form( wave_form form )
{
this->form = form;
}
// update the internal parameter by given parameters
void VFO::update_parameters(void)
{
// calc the half_way;
this-> half_way = this->Fs * this-> duty_cycle;
// forbid to be zero.
if ( this-> half_way < this->frequency )
half_way = this->frequency;
// for triangle wave;
this->rising_rate = 1.0 / this->half_way;
this->falling_rate = - 1.0 / ( this->Fs - this->half_way );
}
次回は
次回はVFOをプログラムに組み込みます。
ところで、今回作ったVFOはDC成分を含んでいます。これについては後日対策することとして、今は気にしないでおきます。