
Sample program of the mbed Audio Framework for the Nucleo F746ZG
Dependencies: amakusa mbed-dsp mbed shimabara ukifune unzen_nucleo_f746
Fork of Signal_nucleo_f746 by
このプログラムは『雲仙』オーディオ・フレームワークを使する、オーディオ信号処理プログラムのスケルトンです。
雲仙はオーディオ信号処理を割り込みコンテキストで実行します。割り込みコンテキストではmbedの便利な機能を使いにくいため、それらはmain()関数で実行します。管理プログラムはシリアルIO、GPIO、ファイルIOなどといったmbedの機能をそのまま使うことが出来ます。
雲仙は各種のサンプル周波数や各種のハードウェアに対応できる柔軟な設計になっています。一方で、この柔軟さは信号処理と無関係のAPIを複数呼ぶことを必要とします。結果として雲仙の柔軟性はオーディオ信号処理をする上で余計な作業を増やします。
スケルトンを使うことで、これらの問題は概ね解決します。本プロジェクトは雲仙の管理APIを可能な限りサブルーチンに押し込み、一方でサンプル周波数やブロックサイズといったコンフィギュレーション用のマクロをプログラマが変更できるようにしています。このスケルトンでは、プログラマは大きく分けて「信号処理の初期化」「信号処理アルゴリズム」「管理プログラム」の3つのコードを穴埋めしていきます。
利用方法
このページ右上の"Import to compiler"をクリックしてください。自分のmbed compilerにスケルトンがインポートされます。あとはプロジェクト名を変えて、自由に変更してください。
利用しているライブラリ
ライブラリの詳細はそれぞれのライブラリを参照してください。
- unzen_nucleo_f746 : Nucleo F746ZG用のオーディオ・フレームワーク本体です。詳細は『雲仙』オーディオ・フレームワークを参照してください。
- shimabara : 雲仙とペアで使う、オーディオCODECコントローラ。
- ukifune : Nucleo F476ZG用のUI基板コントロール・ライブラリ。回路設計情報もあります。
- amakusa : mbed-dsp 用のラッパー・クラス・ライブラリ + α
- mbed-dsp : mbed用の公式DSPライブラリ。
利用するハードウェア
- Nucleo F746ZG
- 「桐壺」基板 : Nucleo F746ZGと UMB-ADAU1361-A の接続基板。設計情報は『雲仙』オーディオ・フレームワークにあります。
- 「浮舟」基板 : Nucleo F476ZG用のUI基板。基板設計情報はukifuneにあります。
- UMB-ADAU1361-A : オーディオCODEC基板。
- TOL-POT4-A : ボリューム基板。
コンフィギュレーション
このスケルトンは、マクロを使ってサンプル周波数とブロックサイズを変更できます。
BLOCKSIZEマクロは main.cpp の冒頭で宣言されています。この値がNのとき、Nサンプル毎に信号処理がおこなわれます。FSマクロはmain.cppの冒頭で宣言されています。この値はサンプル周波数を変更します。
main.cpp
/*========================= Project Dependent Constants ======================*/ #define BLOCKSIZE 16 // number of the sample to be processed at once. #define FS shimabara::Fs_48 // Fs can be Fs_32, Fs_441, Fs_48, Fs_96
信号処理アルゴリズムの初期化と実装
このスケルトンは、信号処理アルゴリズムとその初期化をひとつのClassとして実装しています。ユーザーはアルゴリズムの内部変数を含め、クラスの実装を変更することで独自のアルゴリズムを実装します。
ヘッダー・ファイルである signal_processing.h にはクラスの宣言があります。このクラス宣言にはユーザーが変更してよい部分と変更してはならない部分があります。
次のメソッド・プロトタイプは変更してはいけません。
- SignalProcessing::SignalProcessing()
- SignalProcessing::run()
- SignalProcessing::set_volume()
- SignalProcessing::enter_critical_section()
- SignalProcessing::leave_critical_section()
次のメソッド・プロトタイプは、管理プログラムと信号処理アルゴリズムの通信用のサンプルとして実装しているものです。ユーザーが自由に変更、削除、追加してかまいません。
- SignalProcessing::set_volume()
メンバー変数は自由に変更してください。
signal_processing.h
// User Signal processing Class class SignalProcessing { public: // essential members. Do not touch SignalProcessing( unsigned int block_size ); void run( float rx_left_buffer[], // array of the left input samples float rx_right_buffer[], // array of the right input samples float tx_left_buffer[], // place to write the left output samples float tx_right_buffer[], // place to write the right output samples unsigned int block_size // block size [sample] ); // project depenedent members. void set_volume( float vol ); private: // essential members. Do not touch. void enter_critical_section(void); void leave_critical_section(void); // project dependent members. float volume_level; };
実際のコードは signal_processing.cpp に実装します。
SignalProcessing::SignalProcessing() はコンストラクタです。信号処理の初期化が必要な場合にはここに実装します。たとえば、フィルタへの係数の設定、内部変数の初期化などがそれです。
signal_processing.cpp
// Modify this constructor to initialize your audio algorithm. SignalProcessing::SignalProcessing( unsigned int block_size ) { // place the signal processing initializing code here. this->volume_level = 0.0; // sample initializaiton } // End of constructor()
SignalProcessing::run() は信号処理本体です。このメソッドはスケルトンの冒頭で宣言したBLOCKSIZEサンプル周期で呼び出されます。デフォルトでは、入力オーディオ信号を出力にコピーしながら、ボリューム値で音量を変えるサンプルになっています。
signal_processing.cpp
// Modify this method to implement your audio algorithm. void SignalProcessing::run( float rx_left_buffer[], // array of the left input samples float rx_right_buffer[], // array of the right input samples float tx_left_buffer[], // place to write the left output samples float tx_right_buffer[], // place to write the right output samples unsigned int block_size // block size [sample] ) { // place the signal processing coce here for ( int i= 0; i< block_size; i++ ) { tx_left_buffer[i] = rx_left_buffer[i] * this->volume_level; tx_right_buffer[i] = rx_right_buffer[i] * this->volume_level; } } // End of run()
管理プログラムとの間で通信を行うメソッドを実装する場合には、必要に応じて SignalProcessing::enter_critical_section() と SignalProcessing::leave_critical_section() によってメンバー変数アクセスを保護してください。
管理プログラムの実装
main.cpp の main() 関数には信号処理アルゴリズムの外側で実行すべき管理プログラムを書きます。一般にはUIの処理、外部との通信、ファイル・アクセスなどをここに書きます。ここではmbed SDKが提供するAPIを使うことが出来ます。
管理プログラムが信号処理アルゴリズムと通信する場合には、SignalProcessing * 型変数 process を介して通信を行ってください。
main.cpp
int main() { uint32_t pushing, releasing, holding; // start audio. Do not touch initialize_system(); // main loop. Signal processing is done in background. while(1) { // place your foreground program here. // get volume from UI panel, then apply it to signal processing. process->set_volume( ukifune::get_volume(0) ); // sample usage of button switch detection ukifune::get_button_state( pushing, releasing, holding); // pushing detection demo if ( pushing & (1 << ukifune::swm1 ) ) // is SWM1 switch pusshing down? ukifune::toggle_led( ukifune::led1_1 ); // then, toggle LED1_1 // releasing detection demo if ( releasing & (1 << ukifune::swm2 ) ) // is SWM2 switch releasing? ukifune::toggle_led( ukifune::led2_1 ); // then toggle LED2_1 // holding detection demo if ( holding & (1 << ukifune::swm3 ) ) // is SWM3 switch holding? ukifune::turn_led_on( ukifune::led3_1 ); // then turn LED3_1 on else ukifune::turn_led_off( ukifune::led3_1 ); // else off // you have to call tick() every 20mS-50mS if you need get_volume() wait(0.05); ukifune::tick(); } } // End of main
Revision 20:699e209fd19a, committed 2017-02-03
- Comitter:
- shorie
- Date:
- Fri Feb 03 14:41:40 2017 +0000
- Parent:
- 19:f5e785fe50b1
- Commit message:
- Rewind the faulty commit.
Changed in this revision
diff -r f5e785fe50b1 -r 699e209fd19a dcblocker.cpp --- a/dcblocker.cpp Fri Feb 03 14:35:46 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -#include "signal_processing.h" - -/* -* Related Link -* https://www.dsprelated.com/freebooks/filters/DC_Blocker.html -* https://ccrma.stanford.edu/~jos/fp/DC_Blocker.html -*/ - -DCBlocker::DCBlocker( uint32_t blockSize ) : amakusa::AbstractFilter ( blockSize ) -{ - this->x_last = 0; - this->y_last = 0; -} - -void DCBlocker::run( float32_t *pSrc, float32_t *pDst, uint32_t blockSize ) -{ - // if the parameter is non-zero, take it. If the parameter is zero, use default. - if ( blockSize == 0 ) - blockSize = this->blockSize; - - for ( int i = 0; i < blockSize; i++ ) - { - // y = x - x * z^-1 + 0.995 * y * z^-1 - pDst[ i ] = pSrc[ i ] - this->x_last + 0.995f * this->y_last; - this->x_last = pSrc[ i ]; - this->y_last = pDst[ i ]; - } - -}
diff -r f5e785fe50b1 -r 699e209fd19a main.cpp --- a/main.cpp Fri Feb 03 14:35:46 2017 +0000 +++ b/main.cpp Fri Feb 03 14:41:40 2017 +0000 @@ -42,47 +42,33 @@ int main() { uint32_t pushing, releasing, holding; - - // VFO form - wave_form form = triangle; // start audio. Do not touch initialize_system(); - - process->set_vfo_frequency( 440 ); - process->set_vfo_wave_form( form ); - ukifune::turn_led_off( ukifune::led1_1 ); - ukifune::turn_led_on( ukifune::led1_2 ); // main loop. Signal processing is done in background. while(1) { // place your foreground program here. // get volume from UI panel, then apply it to signal processing. - process->set_volume( ukifune::get_volume(0) ); - process->set_vfo_duty_cycle( ukifune::get_volume(1) ); + process->set_volume( ukifune::get_volume(0) ); // sample usage of button switch detection ukifune::get_button_state( pushing, releasing, holding); // pushing detection demo if ( pushing & (1 << ukifune::swm1 ) ) // is SWM1 switch pusshing down? - if ( form == triangle ) - { - form = square; - process->set_vfo_wave_form( form ); - ukifune::turn_led_on( ukifune::led1_1 ); - ukifune::turn_led_off( ukifune::led1_2 ); - } - else - { - form = triangle; - process->set_vfo_wave_form( form ); - ukifune::turn_led_off( ukifune::led1_1 ); - ukifune::turn_led_on( ukifune::led1_2 ); - } - + ukifune::toggle_led( ukifune::led1_1 ); // then, toggle LED1_1 + // releasing detection demo + if ( releasing & (1 << ukifune::swm2 ) ) // is SWM2 switch releasing? + ukifune::toggle_led( ukifune::led2_1 ); // then toggle LED2_1 + + // holding detection demo + if ( holding & (1 << ukifune::swm3 ) ) // is SWM3 switch holding? + ukifune::turn_led_on( ukifune::led3_1 ); // then turn LED3_1 on + else + ukifune::turn_led_off( ukifune::led3_1 ); // else off // you have to call tick() every 20mS-50mS if you need get_volume()
diff -r f5e785fe50b1 -r 699e209fd19a monophonic.cpp --- a/monophonic.cpp Fri Feb 03 14:35:46 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -#include "signal_processing.h" - - - // constructor. -Monophonic::Monophonic( unsigned int block_size ) -{ - // initializing the subm-odules. - this->vfo = new VFO(); // allocate VFO -} // End of constructor() - -Monophonic::~Monophonic( void ) -{ - // initializing the subm-odules. - delete this->vfo; -} // End of constructor() - - - - // Run all signal processing. -void Monophonic::run( - float out_buffer[], // place to write the right output samples - unsigned int block_size // block size [sample] - ) -{ - // place the signal processing coce here - - // VFO - this->vfo->run( out_buffer, block_size); - -} // End of run() - - - // Sampling Frequency -void Monophonic::set_Fs( int Fs ) -{ - this->vfo->set_Fs( Fs ); -} - - // Oscillation Frequency -void Monophonic::set_vfo_frequency( int freq ) -{ - this->vfo->set_frequency( freq ); -} - - // Duty Cycle of VFO -void Monophonic::set_vfo_duty_cycle( float duty ) -{ - this->vfo->set_duty_cycle( duty ); -} - - // VFO wave form -void Monophonic::set_vfo_wave_form( wave_form form ) -{ - this->vfo->set_wave_form( form ); -}
diff -r f5e785fe50b1 -r 699e209fd19a signal_processing.cpp --- a/signal_processing.cpp Fri Feb 03 14:35:46 2017 +0000 +++ b/signal_processing.cpp Fri Feb 03 14:41:40 2017 +0000 @@ -6,10 +6,6 @@ { // place the signal processing initializing code here. this->volume_level = 0.0; // sample initializaiton - this->note = new Monophonic(block_size); // allocate VFO - note->set_Fs( SAMPLING_FREQUENCY ); - note->set_vfo_frequency( 440 ); - note->set_vfo_wave_form( triangle ); } // End of constructor() @@ -23,52 +19,15 @@ ) { // place the signal processing coce here - - // VFO - this->note->run( tx_left_buffer, block_size); - - // apply gain and copy to right ch. for ( int i= 0; i< block_size; i++ ) { - tx_right_buffer[i] = tx_left_buffer[i] *= this->volume_level; + tx_left_buffer[i] = rx_left_buffer[i] * this->volume_level; + tx_right_buffer[i] = rx_right_buffer[i] * this->volume_level; } } // End of run() - - // Sampling Frequency -void SignalProcessing::set_Fs( int Fs ) -{ - this->enter_critical_section(); // forbidden interrrupt. - this->note->set_Fs( Fs ); - this->leave_critical_section(); // now, ok to accept interrupt. -} - - // Oscillation Frequency -void SignalProcessing::set_vfo_frequency( int freq ) -{ - this->enter_critical_section(); // forbidden interrrupt. - this->note->set_vfo_frequency( freq ); - this->leave_critical_section(); // now, ok to accept interrupt. -} - - // Duty Cycle of VFO -void SignalProcessing::set_vfo_duty_cycle( float duty ) -{ - this->enter_critical_section(); // forbidden interrrupt. - this->note->set_vfo_duty_cycle( duty ); - this->leave_critical_section(); // now, ok to accept interrupt. -} - - // VFO wave form -void SignalProcessing::set_vfo_wave_form( wave_form form ) -{ - this->enter_critical_section(); // forbidden interrrupt. - this->note->set_vfo_wave_form( form ); - this->leave_critical_section(); // now, ok to accept interrupt. -} - - // Set the volume level to the object. + // Sample method. Set the volume level to the object. void SignalProcessing::set_volume( float vol ) { this->enter_critical_section(); // forbidden interrrupt.
diff -r f5e785fe50b1 -r 699e209fd19a signal_processing.h --- a/signal_processing.h Fri Feb 03 14:35:46 2017 +0000 +++ b/signal_processing.h Fri Feb 03 14:41:40 2017 +0000 @@ -3,70 +3,6 @@ #include "amakusa.h" -#define SAMPLING_FREQUENCY 48000 - -enum wave_form { triangle, square }; - - // 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. - - // internal variable. - int current_phase; // internal variable of VFO. - 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. -}; - - // Blocking DC -class DCBlocker : public amakusa::AbstractFilter { -public: - DCBlocker( uint32_t blockSize ); - virtual void run( float32_t *pSrc, float32_t *pDst, uint32_t blockSize = 0 ); -private: - float x_last; - float y_last; -}; - - // Monophonic synthsizer class -class Monophonic { -public: - Monophonic( unsigned int block_size ); - virtual ~Monophonic(void); - void run( - float out_buffer[], // place to write the right output samples - unsigned int block_size // block size [sample] - ); - void set_Fs( int Fs ); // unit is Hz. - void set_vfo_frequency( int freq ); // unit is Hz. - void set_vfo_duty_cycle( float duty ); // 0 ... 0.5 - void set_vfo_wave_form( wave_form form ); -private: - VFO *vfo; -}; - - // User Signal processing Class class SignalProcessing { public: @@ -82,18 +18,13 @@ // project depenedent members. void set_volume( float vol ); - void set_Fs( int Fs ); // unit is Hz. - void set_vfo_frequency( int freq ); // unit is Hz. - void set_vfo_duty_cycle( float duty ); // 0 ... 0.5 - void set_vfo_wave_form( wave_form form ); private: // essential members. Do not touch. void enter_critical_section(void); void leave_critical_section(void); // project dependent members. - float volume_level; // 0 ... 1.0 - Monophonic * note; + float volume_level; }; #endif
diff -r f5e785fe50b1 -r 699e209fd19a vfo.cpp --- a/vfo.cpp Fri Feb 03 14:35:46 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -#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 ); -} -