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
Diff: main.cpp
- 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(); + } +}