Output the audio signal with filtering by IIR filter in the Quad-SPI flash memory using onboard CODEC. QSPI フラッシュメモリのオーディオデータを遮断周波数可変の IIR フィルタを通してボードに搭載されているCODEC で出力するプログラム.

Dependencies:   BSP_DISCO_F746NG_patch_fixed F746_GUI LCD_DISCO_F746NG QSPI_DISCO_F746NG TS_DISCO_F746NG mbed

main.cpp

Committer:
MikamiUitOpen
Date:
2016-04-07
Revision:
1:a1be09c2533a
Parent:
0:2eb96a7cf9b9

File content as of revision 1:a1be09c2533a:

//--------------------------------------------------------------
//  QSPI 接続のフラッシュメモリに前もって格納されている音楽データを
//  再生する際に IIR フィルタをかける
//
//  QSPI 接続のフラッシュメモリのデータ構造
//      0x90000000 から 4 バイト:  データ数に対応する (int32_t 型)
//      0x90000004 から 2 バイトごと: int16_t のモノラルデータが続く
//      モノラルデータ: 標本化周波数 16 kHz のもの
//  IIR フィルタ ---- 低域通過および高域通過フィルタ
//
//  2016/04/07, Copyright (c) 2016 MIKAMI, Naoki
//--------------------------------------------------------------

#include "LCD_DISCO_F746NG.h"
#include "sai_io_o.hpp"
#include "QSPI_BinaryReader.hpp"
#include "ButtonGroup.hpp"
#include "DesignerDrawer.hpp"

using namespace Mikami;

const uint32_t N_DATA_ = 1024;
SaiIO_O mySai_(N_DATA_, I2S_AUDIOFREQ_16K);

const int ORDER_ = 6;   // 次数
Biquad::Coefs ck_[ORDER_/2];
float g0_;

// フィルタ処理の有無を決める
void FilterOnOff(ButtonGroup &onOff, bool &filterOn);

// LPF と HPF の切り替えとフィルタの設計
void SwDesign(ButtonGroup &lpHp, DesignerDrawer &drawerObj,
              Biquad hn[]);

int main()
{
    Label myLabel(80, 6, "IIR Butterworth filter", Label::LEFT, Font16);

    // ButtonGroup: "PLAY", "PAUSE", "RESUME", "STOP"
    const string FUNCTION[4] = {"PLAY", "PAUSE", "RESUME", "STOP"};
    ButtonGroup function(390, 10, 80, 38,
                         4, FUNCTION, 0, 5, 1);
    // PLAY のみアクティブ
    function.Activate(0);
    for (int n=1; n<4; n++) function.Inactivate(n);

    // ButtonGroup: "LPF", "HPF"
    const string LP_HP[2] = {"LPF", "HPF"};
    ButtonGroup lpHp(390, 187, 40, 38,
                     2, LP_HP, 0, 0, 2, 0);

    // ButtonGroup: "ON", "OFF"
    const string ON_OFF[2] = {"ON", "OFF"};
    ButtonGroup onOff(390, 230, 40, 38,
                      2, ON_OFF, 0, 0, 2, 1);

    // QSPI フラッシュメモリからデータを読み込むための準備
    QspiBinaryReader reader;
    int32_t size = reader.ReadSize();  // Data number
    int32_t frameSize = mySai_.GetLength();
    int32_t loopCount = size/frameSize;

    // フィルタの設計と周波数特性描画用
    DesignerDrawer drawerObj(
                     60,        // グラフの左端の位置
                     230,       // グラフの下端の位置
                     30,        // 10 dB 当たりのピクセル数
                     16000,     // 標本化周波数
                     ORDER_,    // 次数
                     400,       // 最初に与える遮断周波数
                     200,       // 遮断周波数の最小値
                     5000,      // 遮断周波数の最大値
                     BilinearDesign::LPF);
    // フィルタの準備
    drawerObj.GetCoefficients(ck_, g0_);
    Biquad hn[ORDER_/2];
    for (int k=0; k<ORDER_/2; k++) hn[k] = Biquad(ck_[k]);

    int16_t *sn = new int16_t[frameSize+1]; // フレームバッファ
    bool playOk = false;
    bool filterOn = false;

    while (true)
    {
        // PLAY がタッチされるまで待つ
        if (!playOk)
            while (!function.Touched(0))
            {
                SwDesign(lpHp, drawerObj, hn);  // フィルタの切り替えとフィルタの設計
                FilterOnOff(onOff, filterOn);   // フィルタ処理の有無を決める
                wait(0.02f);
            }
        function.Activate(1);   // PAUSE を使えるようにする
        function.Activate(3);   // STOP を使えるようにする
        
        playOk = false;
        bool stopOk = false;
        mySai_.InitCodecOut();  // SAI の初期化

        // IIR フィルタの内部の遅延器のクリア
        for (int k=0; k<ORDER_/2; k++) hn[k].Clear();

        for (int k=0; k<loopCount; k++)
        {
            if (function.Touched(3)) break; // STOP
            if (function.Touched(1))        // PAUSE ?
            {
                function.Draw(0);
                function.Activate(2);   // RESUME を使えるようにする
                mySai_.Pause();
                // PLAY か RESUME か STOP がタッチされるまで待つ
                while (!function.Touched(0) && !function.Touched(2)
                                            && !function.Touched(3))
                {
                    SwDesign(lpHp, drawerObj, hn);  // フィルタの切り替えとフィルタの設計
                    FilterOnOff(onOff, filterOn);   // フィルタ処理の有無を決める
                }
                if (function.Touched(0))    // 最初から PLAY
                    playOk = true;
                if (function.Touched(2))    // PAUSE したところから PLAY 再開
                {
                    mySai_.Resume();
                    wait_ms(200);
                    function.Inactivate(2);
                }
                if (function.Touched(3))    // STOP
                    stopOk = true;

                function.TouchedColor(0);
                function.Draw(1);
            }

            if (playOk || stopOk) break;

            FilterOnOff(onOff, filterOn);   // フィルタ処理の有無を決める

            // データの転送が終わるまで待つ
            while (!mySai_.IsXferred()) {}
            // 1フレーム分のデータを QSPI フラッシュメモリから読み込む
            reader.Read(sn, frameSize*k, frameSize);

            // 1フレーム分を出力する
            for (int n=0; n<frameSize; n++)
            {
                int16_t value;
                if (filterOn)       // フィルタ処理実行
                {
                    float yn = g0_*sn[n];
                    for (int k=0; k<ORDER_/2; k++) yn = hn[k].Execute(yn);
                    value = (int16_t)yn;
                }
                else
                    value = sn[n];  // フィルタ処理なし
                mySai_.Output(value, value);    // 音響信号の出力
            }
            mySai_.ResetXferred();          // 次のデータ転送に備える
            SwDesign(lpHp, drawerObj, hn);  // フィルタの切り替えとフィルタの設計
        }

        mySai_.Stop();
        if (!playOk) function.Activate(0);  // PLAY のみアクティブにする
        for (int n=1; n<4; n++) function.Inactivate(n);
    }
}

// フィルタ処理の有無を決める
void FilterOnOff(ButtonGroup &onOff, bool &filterOn)
{
    int num;
    if (!onOff.GetTouchedNumber(num)) return;
    filterOn = (num == 0) ? true : false;
}

// フィルタの切り替えとフィルタの設計
void SwDesign(ButtonGroup &lpHp, DesignerDrawer &drawerObj,
              Biquad hn[])
{ 
    static int num = 0;
    lpHp.GetTouchedNumber(num);
    BilinearDesign::Type typeLH = (BilinearDesign::Type)num;
    if (drawerObj.ReDesignAndDraw(ck_, g0_, typeLH))
        for (int k=0; k<ORDER_/2; k++)
        {
            hn[k].SetCoefficients(ck_[k]);
            hn[k].Clear();
        }
}