«

VFOを雲仙に組み込む

By 酔漢 - Last updated: 土曜日, 2月 4, 2017 - Save & Share - Leave a Comment

前回はやっつけでVFOクラスを作りましたが、今回は作ったVFOを雲仙に組み込みます。もともとは「ドッグフードを食べる」ことで雲仙を鍛えるのが目的の企画ですので、どちらかというと前回より今回の方が本筋です。

Monophonicクラス

雲仙にVFOを組み込む前に、まずはMonophonicクラスを作ってここにVFOを組み込みます。Monophonicクラスは短音のシンセサイザー・クラスで、この中にEGやフィルタと言ったモジュールを組み込んで、独立したシンセサイザーとして作り上げます。最終的にCPU負荷に余裕があればポリフォニック・シンセサイザーに仕上げたいところですが、どうなるでしょうか。

クラスの宣言は以下のようになっています。実装はおもしろみがありませんので特にこの稿では掘り下げませんが、ソースコードは公開しています。ソースコードはこの後どんどん変っていきますのであしからず。

    // 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;
};

 

SignalProcessingクラスの宣言

SignalProcessingクラスは雲仙フレームワークではなく、スケルトンが提供するクラスです。このクラスはスケルトンの中でユーザー・アルゴリズムの実装場所となっています。

スケルトンが提供しているSignalProcessingクラスの宣言の内、コンストラクタ、run()メソッド、enter_critical_secion()メソッド、leave_critical_section()メソッドは、インターフェースを変えてはいけません。一方で、その他のメソッドやメンバー変数に関しては変更は自由です。また、好きなメソッド、メンバー変数を追加して結構です。

なお、publicなメソッドを増やす場合には、雲仙の管理プログラムからアクセスする場合に限ると良いでしょう。これについては後述します。

今回の実装では、Fsの変更、波形の変更、デューティサイクルの変更などを行うシンセサイザーらしいメソッドが追加されています。なお、これらのメソッドは作り込んでいくうちに変更が加わりますので気をつけてください。

    // 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 );
    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;
};

 

SignalProcessingクラスの実装

クラスへの実装のうち、目立つところとしてrun()メソッドを取り上げます。

このメソッドは雲仙のprocessコールバックから呼び出されます。引数としてはオーディオ・コーデックLSIからの受信信号、オーディオ・コーデックLSIへの送信信号、ブロックサイズがあります。送受信信号はそれぞれステレオです。値は浮動小数点型であり、-1.0から+1.0の範囲を持ちます。

メソッドの中ではVFO(を中に持つMonophonicクラス型メンバー変数変数note)のrun()メソッドを呼んでいます。なお、その出力はモノラルなので、ボリュームを反映した後にステレオになるようコピーしています。

        // 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

        // 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;
    }
}   // 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.
}

 

管理プログラムの実装

main()には信号処理の管理プログラムを実装します。

ここでは周期的にヴォリュームの値を読み、それをSignalProcessing型変数processに適用するということを繰り返しています。また、モード・スイッチを監視し、推される度に波形を三角波と矩形波の間で切り替えることもしています。

/*========================= Main program. ====================================*/
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) ); 
       
            // 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 );
            }
            


       
            // you have to call tick() every 20mS-50mS if you need get_volume()
        wait(0.05);
        ukifune::tick();
    }
}   // End of main

 

Posted in シンセサイザー • • Top Of Page

Write a comment