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

Revision:
0:2eb96a7cf9b9
Child:
1:a1be09c2533a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Apr 01 05:03:20 2016 +0000
@@ -0,0 +1,188 @@
+//--------------------------------------------------------------
+//  QSPI 接続のフラッシュメモリに前もって格納されている音楽データを
+//  再生する際に IIR フィルタをかける
+//
+//  QSPI 接続のフラッシュメモリのデータ構造
+//      0x90000000 から 4 バイト:  データ数に対応する (int32_t 型)
+//      0x90000004 から 2 バイトごと: int16_t のモノラルデータが続く
+//      モノラルデータ: 標本化周波数 16 kHz のもの
+//  IIR フィルタ ---- 低域通過および高域通過フィルタ
+//
+//  2016/04/01, 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.DrawTouched(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();
+        }
+}