Output the audio signal (*.bin) with filtering by IIR filter in the SD card using onboard CODEC. For *.wav file, F746_SD_WavPlayer and F746_SD_GraphicEqualiser are published on mbed. SD カードのオーディオ信号 (*.bin) を遮断周波数可変の IIR フィルタを通して,ボードに搭載されているCODEC で出力する.*.wav 形式のファイル用には,F746_SD_WavPlayer と F746_SD_GraphicEqualiser を mbed で公開している.
Dependencies: BSP_DISCO_F746NG_patch_fixed F746_GUI LCD_DISCO_F746NG SDFileSystem_Warning_Fixed TS_DISCO_F746NG mbed
Revision 0:6748e3332e85, committed 2016-04-08
- Comitter:
- MikamiUitOpen
- Date:
- Fri Apr 08 13:11:53 2016 +0000
- Child:
- 1:e891c9b4f980
- Commit message:
- 1
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BSP_DISCO_F746NG_patch_fixed.lib Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,1 @@ +https://developer.mbed.org/users/the_sz/code/BSP_DISCO_F746NG_patch_fixed/#a4e658110084
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/F746_GUI.lib Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/MikamiUitOpen/code/F746_GUI/#cbf7ed9092a3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LCD_DISCO_F746NG.lib Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/teams/ST/code/LCD_DISCO_F746NG/#d44525b1de98
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/BSP_AudioOut_Overwrite.cpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,111 @@ +//-------------------------------------------------------------- +// Overwrite functuions and define calback functions +// for functions in stm32746g_discovery_audio.cpp +//-------------------------------------------------------------- +#include "BSP_AudioOut_Overwrite.hpp" + +// These three callback functions are modyfied by Mikami +void BSP_AUDIO_OUT_HalfTransfer_CallBack() +{ + Mikami::SaiIO_O::FillBuffer1st(); +} + +void BSP_AUDIO_OUT_TransferComplete_CallBack() +{ + Mikami::SaiIO_O::FillBuffer2nd(); +} + +void BSP_AUDIO_OUT_Error_CallBack() +{ + Mikami::SaiIO_O::ErrorTrap(); +} + +//-------------------------------------------------------------- +// Followings are original by Nanase +//-------------------------------------------------------------- + +DMA_HandleTypeDef hdma_sai_tx; + +void AUDIO_OUT_SAIx_DMAx_IRQHandler() +{ + HAL_DMA_IRQHandler(&hdma_sai_tx); +} + +void BSP_AUDIO_OUT_MspInit(SAI_HandleTypeDef *hsai, void *Params) +{ + //static DMA_HandleTypeDef hdma_sai_tx; + GPIO_InitTypeDef gpio_init_structure; + + /* Enable SAI clock */ + AUDIO_OUT_SAIx_CLK_ENABLE(); + + /* Enable GPIO clock */ + AUDIO_OUT_SAIx_MCLK_ENABLE(); + AUDIO_OUT_SAIx_SCK_SD_ENABLE(); + AUDIO_OUT_SAIx_FS_ENABLE(); + + /* CODEC_SAI pins configuration: FS, SCK, MCK and SD pins ------------------*/ + gpio_init_structure.Pin = AUDIO_OUT_SAIx_FS_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_HIGH; + gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF; + HAL_GPIO_Init(AUDIO_OUT_SAIx_FS_GPIO_PORT, &gpio_init_structure); + + gpio_init_structure.Pin = AUDIO_OUT_SAIx_SCK_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_HIGH; + gpio_init_structure.Alternate = AUDIO_OUT_SAIx_SCK_AF; + HAL_GPIO_Init(AUDIO_OUT_SAIx_SCK_SD_GPIO_PORT, &gpio_init_structure); + + gpio_init_structure.Pin = AUDIO_OUT_SAIx_SD_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_HIGH; + gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF; + HAL_GPIO_Init(AUDIO_OUT_SAIx_SCK_SD_GPIO_PORT, &gpio_init_structure); + + gpio_init_structure.Pin = AUDIO_OUT_SAIx_MCLK_PIN; + gpio_init_structure.Mode = GPIO_MODE_AF_PP; + gpio_init_structure.Pull = GPIO_NOPULL; + gpio_init_structure.Speed = GPIO_SPEED_HIGH; + gpio_init_structure.Alternate = AUDIO_OUT_SAIx_FS_SD_MCLK_AF; + HAL_GPIO_Init(AUDIO_OUT_SAIx_MCLK_GPIO_PORT, &gpio_init_structure); + + /* Enable the DMA clock */ + AUDIO_OUT_SAIx_DMAx_CLK_ENABLE(); + + if(hsai->Instance == AUDIO_OUT_SAIx) + { + /* Configure the hdma_saiTx handle parameters */ + hdma_sai_tx.Init.Channel = AUDIO_OUT_SAIx_DMAx_CHANNEL; + hdma_sai_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_sai_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_sai_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_sai_tx.Init.PeriphDataAlignment = AUDIO_OUT_SAIx_DMAx_PERIPH_DATA_SIZE; + hdma_sai_tx.Init.MemDataAlignment = AUDIO_OUT_SAIx_DMAx_MEM_DATA_SIZE; + hdma_sai_tx.Init.Mode = DMA_CIRCULAR; + hdma_sai_tx.Init.Priority = DMA_PRIORITY_HIGH; + hdma_sai_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_sai_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sai_tx.Init.MemBurst = DMA_MBURST_SINGLE; + hdma_sai_tx.Init.PeriphBurst = DMA_PBURST_SINGLE; + + hdma_sai_tx.Instance = AUDIO_OUT_SAIx_DMAx_STREAM; + + /* Associate the DMA handle */ + __HAL_LINKDMA(hsai, hdmatx, hdma_sai_tx); + + /* Deinitialize the Stream for new transfer */ + HAL_DMA_DeInit(&hdma_sai_tx); + + /* Configure the DMA Stream */ + HAL_DMA_Init(&hdma_sai_tx); + } + + /* SAI DMA IRQ Channel configuration */ + HAL_NVIC_SetPriority(AUDIO_OUT_SAIx_DMAx_IRQ, AUDIO_OUT_IRQ_PREPRIO, 0); + HAL_NVIC_EnableIRQ(AUDIO_OUT_SAIx_DMAx_IRQ); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/BSP_AudioOut_Overwrite.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,14 @@ +//-------------------------------------------------------------- +// Overwrite functuions and define calback function (Header) +// for functions in stm32746g_discovery_audio.cpp +//-------------------------------------------------------------- + +#ifndef F746_AUDIO_OUT_OVERWRITE_HPP +#define F746_AUDIO_OUT_OVERWRITE_HPP + +#include "stm32746g_discovery_audio.h" +#include "sai_io_o.hpp" + +void AUDIO_OUT_SAIx_DMAx_IRQHandler(); + +#endif // F746_AUDIO_OUT_OVERWRITE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/BilinearDesignLH.cpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// Design of Butterworth LPF and HPF using bilinear transform +// +// 2016/03/31, Copyright (c) 2016 MIKAMI, Naoki +//------------------------------------------------------------------------------ + +#include "BilinearDesignLH.hpp" + +namespace Mikami +{ + // Execute design + // input + // fc: Cutoff frequency + // pb: Passband (LPF or HPF) + // output + // c : Coefficients for cascade structure + // g : Gain factor for cascade structure + void BilinearDesign::Execute(float fc, Type pb, Coefs c[], float& g) + { + Butterworth(); + Bilinear(fc); + ToCascade(pb); + GetGain(pb); + GetCoefs(c, g); + } + + // Get poles for Butterworth characteristics + void BilinearDesign::Butterworth() + { + float pi_2order = PI_/(2.0f*ORDER_); + for (int j=0; j<ORDER_/2; j++) // Pole with imaginary part >= 0 + { + float theta = (2.0f*j + 1.0f)*pi_2order; + sP_[j] = Complex(-cosf(theta), sinf(theta)); + } + } + + // Bilinear transform + // fc: Cutoff frequency + void BilinearDesign::Bilinear(float fc) + { + float wc = tanf(fc*PI_FS_); + for (int k=0; k<ORDER_/2; k++) + zP_[k] = (1.0f + wc*sP_[k])/(1.0f - wc*sP_[k]); + } + + // Convert to coefficients for cascade structure + void BilinearDesign::ToCascade(Type pb) + { + for (int j=0; j<ORDER_/2; j++) + { + ck_[j].a1 = 2.0f*real(zP_[j]); // a1m + ck_[j].a2 = -norm(zP_[j]); // a2m + ck_[j].b1 = (pb == LPF) ? 2.0f : -2.0f; // b1m + ck_[j].b2 = 1.0f; // b2m + } + } + + // Calculate gain factor + void BilinearDesign::GetGain(Type pb) + { + float u = (pb == LPF) ? 1.0f : -1.0f; + float g0 = 1.0f; + for (int k=0; k<ORDER_/2; k++) + g0 = g0*(1.0f - (ck_[k].a1 + ck_[k].a2*u)*u)/ + (1.0f + (ck_[k].b1 + ck_[k].b2*u)*u); + gain_ = g0; + } + + // Get coefficients + void BilinearDesign::GetCoefs(Coefs c[], float& gain) + { + for (int k=0; k<ORDER_/2; k++) c[k] = ck_[k]; + gain = gain_; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/BilinearDesignLH.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// Design of Butterworth LPF and HPF using bilinear transform -- Header +// +// 2016/03/31, Copyright (c) 2016 MIKAMI, Naoki +//------------------------------------------------------------------------------ + +#ifndef BILINEAR_BUTTERWORTH_HPP +#define BILINEAR_BUTTERWORTH_HPP + +#include "mbed.h" +#include <complex> // requisite + +namespace Mikami +{ + typedef complex<float> Complex; // define "Complex" + + class BilinearDesign + { + public: + struct Coefs { float a1, a2, b1, b2; }; + enum Type { LPF, HPF }; + + // Constructor + BilinearDesign(int order, float fs) + : PI_FS_(PI_/fs), ORDER_(order) + { + sP_ = new Complex[order/2]; + zP_ = new Complex[order/2]; + ck_ = new Coefs[order/2]; + } + + // Destractor + ~BilinearDesign() + { + delete[] sP_; + delete[] zP_; + delete[] ck_; + } + + // Execution of design + void Execute(float fc, Type pb, Coefs c[], float& g); + + private: + static const float PI_ = 3.1415926536f; + const float PI_FS_; + const int ORDER_; + + Complex* sP_; // Poles on s-plane + Complex* zP_; // Poles on z-plane + Coefs* ck_; // Coefficients of transfer function for cascade form + float gain_; // Gain factor for cascade form + + void Butterworth(); + void Bilinear(float fc); + void ToCascade(Type pb); + void GetGain(Type pb); + void GetCoefs(Coefs c[], float& gain); + }; +} +#endif // BILINEAR_BUTTERWORTH_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/Biquad.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,65 @@ +//-------------------------------------------------------------- +// 縦続形 IIR フィルタで使う 1D タイプの 2 次のフィルタ +// Biquad filter of 1D type for IIR filter of cascade structure +// このクラスでは,係数は実行中に書き換えられることを想定している +// +// u[n] = x[n] + a1*u[n-1] + a2*u[n-2] +// y[n] = u[n] + b1*u[n-1] + b2*u[n-2] +// x[n] : input signal +// y[n] : output signal +// b0 = 1 +// +// 2016/03/25, Copyright (c) 2016 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef IIR_BIQUAD_HPP +#define IIR_BIQUAD_HPP + +#include "mbed.h" + +// 2nd order IIR filter +namespace Mikami +{ + class Biquad + { + public: + struct Coefs { float a1, a2, b1, b2; }; + + Biquad() {} // Default constructore + + Biquad(const Coefs ck) + { + SetCoefficients(ck); + Clear(); + } + + void SetCoefficients(const Coefs ck) + { + a1_ = ck.a1; + a2_ = ck.a2; + b1_ = ck.b1; + b2_ = ck.b2; + } + + float Execute(float xn) + { + float un = xn + a1_*un1_ + a2_*un2_; + float yn = un + b1_*un1_ + b2_*un2_; + + un2_ = un1_; + un1_ = un; + + return yn; + } + + void Clear() { un1_ = un2_ = 0; } + + private: + float a1_, a2_, b1_, b2_; + float un1_, un2_; + + // disallow copy constructor + Biquad(const Biquad&); + }; +} +#endif // IIR_BIQUAD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/BiquadFrqRespDrawer.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,47 @@ +//-------------------------------------------------------------- +// BiquadFrqRespDrawer class (Derived class of FrqRespDrawer) +// 縦続形 IIR フィルタの周波数特性を描画するためのクラス +// このクラスは,FrqRespDrawer の派生クラス +// +// 2016/03/31, Copyright (c) 2016 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef F746_BILINEAR_FRQ_RESP_DRAWER_HPP +#define F746_BILINEAR_FRQ_RESP_DRAWER_HPP + +#include "Biquad.hpp" +#include "FrquencyResponseDrawer.hpp" + +namespace Mikami +{ + class BiquadFrqRespDrawer : public FrqRespDrawer + { + public: + BiquadFrqRespDrawer(FrqRespDrawer::Params p) + : FrqRespDrawer(p) {} + + // 次数とフィルタの係数設定 + void SetParams(int order, float g0, Biquad::Coefs ck[]) + { + order_ = order; + g0_ = g0; + ck_ = ck; + } + + // 周波数応答の絶対値を返す関数, 引数: z^(-1) + virtual float AbsH_z(Complex u) + { + Complex h = g0_; + for (int k=0; k<order_/2; k++) + h = h*(1.0f + (ck_[k].b1 + ck_[k].b2*u)*u) + /((1.0f - (ck_[k].a1 + ck_[k].a2*u)*u)); + return abs(h); + } + + private: + int order_; + float g0_; + Biquad::Coefs *ck_; + }; +} +#endif // F746_BILINEAR_FRQ_RESP_DRAWER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/DesignerDrawer.cpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +// IIR フィルタを双一次 z 変換で設計し,その周波数特性を描画するためのクラス +// +// 2016/04/08, Copyright (c) 2016 MIKAMI, Naoki +//------------------------------------------------------------------------------ + +#include "DesignerDrawer.hpp" + +namespace Mikami +{ + // Constructor + DesignerDrawer::DesignerDrawer(uint16_t x0, uint16_t y0, + uint16_t db10, int fs, int order, + float fc, uint16_t fL, uint16_t fH, + BilinearDesign::Type lpHp) + : lcd_(GuiBase::GetLcdPtr()), ts_(GuiBase::GetTsPtr()), + X0_(x0), Y0_(y0), DB10_(db10), ORDER_(order), + CURSOR_Y0_(y0+1-db10*6), CURSOR_LENGTH_(db10*6-1), + LOWER_F_(fL), HIGHER_F_(fH), + CURSOR_COLOR_(0xFFE000D0), CURSOR_TOUCHED_COLOR_(0xFFFF80FF) + { + drawerObj_ = new BiquadFrqRespDrawer( + FrqRespDrawer::Params(x0, 100.0f, 8000.0f, 150, + y0, -60, 0, db10, fs)); + + // 双一次 z 変換による IIR フィルタの設計 + designObj_ = new BilinearDesign(order, fs); + coefs_ = new BilinearDesign::Coefs[ORDER_/2]; + ck_ = (Biquad::Coefs *)coefs_; + + fC_ = fc; // 最初に与える遮断周波数 + designObj_->Execute(fC_, lpHp, coefs_, g0_); + drawerObj_->SetParams(ORDER_, g0_, ck_); + lblFrq_ = new NumericLabel<int>(110, 30); + + // 周波数特性の描画 + DrawResponse(); + + tp_ = new TouchPanelDetectorX( + drawerObj_->X(LOWER_F_), drawerObj_->X(HIGHER_F_), + CURSOR_Y0_, y0); + lp_ = lpHp; + cursorRedraw_ = false; + } + + DesignerDrawer::~DesignerDrawer() + { + delete tp_; + delete lblFrq_; + delete coefs_; + delete designObj_; + delete drawerObj_; + } + + // フィルタの再設計と周波数特性の再描画 + bool DesignerDrawer::ReDesignAndDraw(Biquad::Coefs *ck, float &g0, + BilinearDesign::Type lpHp) + { + bool changed = (lpHp != lp_) ? true : false; + bool tch = tp_->IsTouched(cursorX_, cursorX_); + if (tch || changed) + { + int newFc = Frq10(drawerObj_->PosToFrq(cursorX_)); + newFc = (newFc > HIGHER_F_) ? HIGHER_F_ : newFc; + if ((abs(newFc - fC_) >= 10) || changed) + { + fC_ = newFc; + lblFrq_->Draw("Cutoff frequency = %4d Hz", newFc); + designObj_->Execute(newFc, lpHp, coefs_, g0_); + GetCoefficients(ck, g0); + + drawerObj_->SetParams(ORDER_, g0_, ck_); + drawerObj_->Erase(); + drawerObj_->DrawAxis(); // 目盛線の描画 + drawerObj_->DrawGraph(); // 周波数特性のグラフのカーブを描画する + + if (tch) // カーソルの移動 + { + lcd_->SetTextColor(CURSOR_TOUCHED_COLOR_); + lcd_->DrawVLine(cursorX_, CURSOR_Y0_, CURSOR_LENGTH_); + } + cursorRedraw_ = true; + oldCursorX_ = cursorX_; + lp_ = lpHp; + return true; + } + } + + if (!tch && cursorRedraw_) // カーソルを元の色に戻す + { + lcd_->SetTextColor(CURSOR_COLOR_); + lcd_->DrawVLine(oldCursorX_, CURSOR_Y0_, CURSOR_LENGTH_); + cursorRedraw_ = false; + } + return false; + } + + // 周波数特性の描画 + void DesignerDrawer::DrawResponse() + { + lblFrq_->Draw("Cutoff frequency = %4d Hz", fC_); + DrawAxisNum(); // 目盛値の描画 + drawerObj_->DrawAxis(); // 目盛線の描画 + drawerObj_->DrawGraph(); // 周波数特性のカーブの描画 + + cursorX_ = drawerObj_->X(fC_); + lcd_->SetTextColor(CURSOR_COLOR_); + lcd_->DrawVLine(cursorX_, CURSOR_Y0_, CURSOR_LENGTH_); + } + + // フィルタ係数の取得 + void DesignerDrawer::GetCoefficients(Biquad::Coefs *c, float &g0) + { + for (int k=0; k<ORDER_/2; k++) c[k] = ck_[k]; + g0 = g0_; + } + + // 周波数を 10, 20, 50, 100 Hz の倍数にする + int DesignerDrawer::Frq10(float f) + { + if (f < 1000) + return ((int)(f/10.0f + 0.5f))*10; + if (f < 2000) + return ((int)(f/20.0f + 0.5f))*20; + if (f < 3000) + return ((int)(f/50.0f + 0.5f))*50; + else + return ((int)(f/100.0f + 0.5f))*100; + } + + // 周波数特性の目盛線の描画 + void DesignerDrawer::DrawAxisNum() + { + const int16_t OFS = 6; + drawerObj_->DrawCharX(100, OFS, "0.1"); + drawerObj_->DrawCharX(300, OFS, "0.3"); + drawerObj_->DrawCharX(1000, OFS, "1.0"); + drawerObj_->DrawCharX(3000, OFS, "3.0"); + drawerObj_->DrawCharX(8000, OFS, "8.0"); + + Label l_frq(drawerObj_->X(900), Y0_+20, "Frequency [kHz]", + Label::CENTER); + drawerObj_->DrawNumericY(-24, -6, 4, 20, "%3d"); // 縦軸の目盛は 20 dB 間隔 + Label l_dB(X0_-24, Y0_-DB10_*6-20, "[dB]"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/DesignerDrawer.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// IIR フィルタを双一次 z 変換で設計し,その周波数特性を描画するためのクラス -- Header +// +// 2016/04/06, Copyright (c) 2016 MIKAMI, Naoki +//------------------------------------------------------------------------------ + +#ifndef F746_DISIGNER_AND_DRAWER_HPP +#define F746_DISIGNER_AND_DRAWER_HPP + +#include "NumericLabel.hpp" +#include "TouchPanelDetectorX.hpp" +#include "BiquadFrqRespDrawer.hpp" +#include "BilinearDesignLH.hpp" + +namespace Mikami +{ + class DesignerDrawer + { + public: + // Constructor + DesignerDrawer(uint16_t x0, uint16_t y0, + uint16_t db10, int fs, int order, + float fc, uint16_t fL, uint16_t fH, + BilinearDesign::Type lpHp); + + ~DesignerDrawer(); + + // フィルタの再設計と周波数特性の再描画 + bool ReDesignAndDraw(Biquad::Coefs *ck, float &g0, + BilinearDesign::Type lpHp); + + // 周波数特性の描画 + void DrawResponse(); + + void GetCoefficients(Biquad::Coefs *ck, float &g0); + + uint16_t GetOrder() { return ORDER_; } + + private: + LCD_DISCO_F746NG *lcd_; + TS_DISCO_F746NG *ts_; + + const uint16_t X0_, Y0_; + const uint16_t DB10_; + const uint16_t ORDER_; + const uint16_t CURSOR_Y0_, CURSOR_LENGTH_; + const uint16_t LOWER_F_, HIGHER_F_; + const uint32_t CURSOR_COLOR_, CURSOR_TOUCHED_COLOR_; + + BilinearDesign::Coefs *coefs_; // 設計された係数 + Biquad::Coefs *ck_; + float g0_; // 設計された係数(利得定数) + + int fC_; // 遮断周波数 + + uint16_t cursorX_, oldCursorX_; + bool cursorRedraw_; + BilinearDesign::Type lp_; + + BiquadFrqRespDrawer *drawerObj_; + BilinearDesign *designObj_; + TouchPanelDetectorX *tp_; + NumericLabel<int> *lblFrq_; + + // 周波数を 10, 20, 50, 100 Hz の倍数にする + int Frq10(float f); + + // 周波数特性の目盛線と目盛値の描画 + void DrawAxisNum(); + }; +} +#endif // F746_DISIGNER_AND_DRAWER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/FileSelector.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,149 @@ +//-------------------------------------------------------------- +// FileSelector class +// SD カード内のファイル名の一覧を表示し,ファイルを選択する +// +// 2016/04/05, Copyright (c) 2016 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef FILE_SELECTOR_HPP +#define FILE_SELECTOR_HPP + +#include "mbed.h" +#include "Label.hpp" +#include "ButtonGroup.hpp" +#include "SD_BinaryReader.hpp" +#include "SDFileSystem.h" + +namespace Mikami +{ + class FileSelector + { + public: + FileSelector(uint8_t x0, uint8_t y0, int maxFiles, + int maxNameLength, SD_BinaryReader &reader) + : X_(x0), Y_(y0), W_H_(24), V_L_(36), + MAX_FILES_(maxFiles), MAX_NAME_LENGTH_(maxNameLength), + BASE_COLOR_(0xFF80FFA0), TOUCHED_COLOR_(0xFF80FFFF), + fileNames_(new string[maxFiles]), + lcd_(GuiBase::GetLcdPtr()), + sdReader_(reader), prev_(-1) {} + + bool CreateTable() + { + DIR* dp = opendir("/sd"); + fileCount_ = 0; + if (dp != NULL) + { + dirent* entry; + for (int n=0; n<256; n++) + { + entry = readdir(dp); + if (entry == NULL) break; + + string strName = entry->d_name; + if ( (strName.find(".bin") != string::npos) || + (strName.find(".BIN") != string::npos) ) + { + sdReader_.Open(strName); // ファイルオープン + // 小さすぎるファイルは除外 + if ((sdReader_.ReadSize()) > 4096) + { + fileNames_[fileCount_] = strName; + fileCount_++; + } + sdReader_.Close(); + } + if (fileCount_ >= MAX_FILES_) break; + } + closedir(dp); + } + else + return false; + + if (fileCount_ == 0) return false; + + nonString_ = new string[fileCount_]; + for (int n=0; n<fileCount_; n++) nonString_[n] = ""; + rect_ = new ButtonGroup(X_, Y_, W_H_, W_H_, fileCount_, + nonString_, 0, V_L_-W_H_, 1, + -1, Font12, 0, GuiBase::ENUM_BACK, + BASE_COLOR_, TOUCHED_COLOR_); + for (int n=0; n<fileCount_; n++) rect_->Erase(n); + CreateLabels(); + + return true; + } + + // ファイルを選択する + bool Select(string &fileName) + { + int n; + if (rect_->GetTouchedNumber(n)) + { + fileNameLabels_[n]->Draw(DeleteBin(n), TOUCHED_COLOR_); + if ((prev_ >= 0) && (prev_ != n)) + fileNameLabels_[prev_]->Draw(DeleteBin(prev_)); + prev_ = n; + fileName = fileNames_[n]; + return true; + } + else + return false; + } + + // ファイルの一覧の表示 + void DisplayFileList() + { + Erase(MAX_NAME_LENGTH_*((sFONT *)(&Font16))->Width, 270-Y_); + rect_->DrawAll(); + for (int n=0; n<fileCount_; n++) + fileNameLabels_[n]->Draw(DeleteBin(n)); + } + + void Erase(uint16_t width, uint16_t height, + uint32_t color = GuiBase::ENUM_BACK) + { + lcd_->SetTextColor(color); + lcd_->FillRect(X_, Y_, width, height); + } + + private: + const uint8_t X_, Y_, W_H_, V_L_; + const int MAX_FILES_; + const int MAX_NAME_LENGTH_; + const uint32_t BASE_COLOR_; + const uint32_t TOUCHED_COLOR_; + + string *fileNames_; + string *nonString_; + ButtonGroup *rect_; + Label **fileNameLabels_; + LCD_DISCO_F746NG *lcd_; + SD_BinaryReader &sdReader_; + int fileCount_; + int prev_; + + // Label を生成 + void CreateLabels() + { + fileNameLabels_ = new Label *[fileCount_+1]; + for (int n=0; n<fileCount_; n++) + fileNameLabels_[n] = new Label(X_+30, Y_+5+V_L_*n, "", + Label::LEFT, Font16, + BASE_COLOR_); + } + + // 拡張子を削除 + string DeleteBin(int n) + { + string name = fileNames_[n]; + name.erase(name.find(".")); + return name.substr(0, MAX_NAME_LENGTH_); + } + + // disallow copy constructor and assignment operator + FileSelector(const FileSelector&); + FileSelector& operator=(const FileSelector&); + }; +} +#endif // FILE_SELECTOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/FrquencyResponseDrawer.cpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,89 @@ +//----------------------------------------------------------- +// FrqRespDrawer class (Abstract base class) +// +// 2016/03/31, Copyright (c) 2016 MIKAMI, Naoki +//----------------------------------------------------------- + +#include "FrquencyResponseDrawer.hpp" +#include "NumericLabel.hpp" + +namespace Mikami +{ + // 目盛線の描画 + void FrqRespDrawer::DrawAxis() + { + uint16_t height = DB1_*(P_.MAX_DB - P_.MIN_DB); + int logMin = (int)floorf(log10f(P_.MIN)); + int loop = (int)floorf(log10f(P_.MAX)) - logMin; + + lcd_->SetTextColor(P_.AXIS_COLOR); + uint16_t y0 = P_.ORGY- height; + lcd_->DrawVLine(X(P_.MIN), y0, height); // 最小値に対応する線 + + float du = powf(10.0, logMin); // 座標値の増分 + float u1 = (floorf(P_.MIN/du) + 1.0f)*du; // 最小値に対応する線の次の座標値 + + for (int n=0; n<=loop; n++) + { + float uMax = (10.0f*du < P_.MAX) ? 10.0f*du : P_.MAX; + for (float u=u1; u<uMax*0.99f; u+=du) + lcd_->DrawVLine(X(u), y0, height); + + du = uMax; // 値の増分を 10 倍する + u1 = du; // 次の for ループ の最初の値 + } + + lcd_->DrawVLine(X(P_.MAX), y0, height); // 最大値に対応する線 + + uint16_t width = X(P_.MAX) - X(P_.MIN); + for (int n=0; n<= (P_.MAX_DB - P_.MIN_DB)/10; n++) + lcd_->DrawHLine(X(P_.MIN), P_.ORGY-P_.DB10*n, width); + } + + // 縦軸の数値の表示 + void FrqRespDrawer::DrawNumericY(int offsetX, int offsetY, int count, + uint16_t d_dB, const char fmt[], sFONT &fonts, + uint32_t textColor) + { + uint16_t x0 = P_.ORG + offsetX; + uint16_t y0 = P_.ORGY + offsetY; + for (int n=0; n<count; n++) + new NumericLabel<int>(x0, y0-n*d_dB*DB1_, + fmt, (int)(P_.MIN_DB+d_dB*n)); + } + + // 周波数特性のグラフの描画 + void FrqRespDrawer::DrawGraph() + { + lcd_->SetTextColor(P_.LINE_COLOR); + uint16_t width = X(P_.MAX) - X(P_.MIN); + uint16_t x1 = 0; + uint16_t y1 = 0; + float pi2FsM = -6.283185f/P_.FS; // -2*PI*Ts + for (int n=0; n<=width; n++) + { + float frq = PosToFrq(n+P_.ORG); + uint16_t x2 = X(frq); + + float absHz = AbsH_z(exp(Complex(0, pi2FsM*frq))); + float dB = (absHz > 0.001f) ? 20.0f*log10f(absHz) : P_.MIN_DB; + uint16_t y2 = P_.ORGY - Round((dB - P_.MIN_DB)*DB1_); + if (y2 > P_.ORGY) y2 = P_.ORGY; + + if (n != 0) lcd_->DrawLine(x1, y1, x2, y2); + + x1 = x2; + y1 = y2; + } + lcd_->SetTextColor(P_.AXIS_COLOR); + lcd_->DrawHLine(X(P_.MIN), P_.ORGY, width); + } + + // 消去 + void FrqRespDrawer::Erase() + { + lcd_->SetTextColor(P_.BACK_COLOR); + uint16_t height = DB1_*(P_.MAX_DB - P_.MIN_DB); + lcd_->FillRect(P_.ORG, P_.ORGY- height, X(P_.MAX)-X(P_.MIN)+1, height+1); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/FrquencyResponseDrawer.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,101 @@ +//----------------------------------------------------------- +// FrqRespDrawer class (Abstract base class) -- Header +// +// 2016/03/30, Copyright (c) 2016 MIKAMI, Naoki +//----------------------------------------------------------- + +#ifndef F746_FRQ_RESP_DRAWER_HPP +#define F746_FRQ_RESP_DRAWER_HPP + +#include <complex> // requisite for complex +#include "Label.hpp" + +namespace Mikami +{ + typedef complex<float> Complex; // define "Complex" + + class FrqRespDrawer + { + public: + struct Params + { + const uint16_t ORG; // 横軸の目盛の最小値に対応する位置 + const float MIN; // 横軸の目盛の最小値 + const float MAX; // 横軸の目盛の最大値 + const uint16_t DEC; // 周波数の 10 倍に対応する長さ (pixels) + const uint16_t ORGY; // 縦軸の目盛の最小値に対応する位置 + const float MIN_DB; // 縦軸の目盛の最小値 [dB] + const float MAX_DB; // 縦軸の目盛の最大値 [dB] + const uint16_t DB10; // 10 dB 対応する長さ (pixels) + const float FS; // 標本化周波数 + const uint32_t LINE_COLOR; + const uint32_t AXIS_COLOR; + const uint32_t BACK_COLOR; + + Params(uint16_t org, float min, float max, uint16_t dec, + uint16_t orgY, float minDb, float maxDb, uint16_t db10, + float fs, + uint32_t lineColor = 0xFF00B0FF, + uint32_t axisColor = LCD_COLOR_LIGHTGRAY, + uint32_t backColor = GuiBase::ENUM_BACK) + : ORG(org), MIN(min), MAX(max), DEC(dec), + ORGY(orgY), MIN_DB(minDb), MAX_DB(maxDb), DB10(db10), + FS(fs), + LINE_COLOR(lineColor), + AXIS_COLOR(axisColor), + BACK_COLOR(backColor) {} + }; + + // Constructor + FrqRespDrawer(Params p) + : lcd_(GuiBase::GetLcdPtr()), P_(p), DB1_(p.DB10*0.1f) {} + + // 周波数に対応する x 座標値の取得 + int X(float frq) + { return Round(P_.ORG + P_.DEC*log10f(frq/P_.MIN)); } + + // x 座標値を周波数に変換 + float PosToFrq(uint16_t x) + { return P_.MIN*powf(10.0f, (x - P_.ORG)/(float)P_.DEC); } + + // 目盛線の描画 + void DrawAxis(); + + // 横軸の目盛値の表示用 + void DrawCharX(uint32_t frq, int offsetY, + const char str[], sFONT &fonts = Font12, + uint32_t textColor = LCD_COLOR_WHITE) + { Label frequency(X(frq), P_.ORGY+offsetY, str, Label::CENTER); } + + // 縦軸の目盛値の表示 + void DrawNumericY(int offsetX, int offsetY, int count, + uint16_t d_dB, const char fmt[], sFONT &fonts = Font12, + uint32_t textColor = LCD_COLOR_WHITE); + + // 周波数特性のグラフの描画 + void DrawGraph(); + + // 周波数応答の絶対値を返す関数, 引数: z^(-1) + virtual float AbsH_z(Complex u) = 0; + + // 消去 + void Erase(); + + private: + LCD_DISCO_F746NG *lcd_; + const Params P_; + const float DB1_; + + // 丸め + int Round(float x) { return x + 0.5f - (x < 0); } + + // 10 のべき乗かどうかの検査 + bool PowersOf10(float x) + { return fabsf(log10f(x) - Round(log10f(x))) < 0.01f; } + + // disallow copy constructor and assignment operator + FrqRespDrawer(const FrqRespDrawer&); + FrqRespDrawer& operator=(const FrqRespDrawer&); + }; +} +#endif // F746_FRQ_RESP_DRAWER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/MyFunctions.cpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,81 @@ +//-------------------------------------------------------------- +// フィルタ処理付き SD オーディオプレーヤーで使う大域関数(ヘッダ) +// +// 2016/04/07, Copyright (c) 2016 MIKAMI, Naoki +//-------------------------------------------------------------- + +#include "MyFunctions.hpp" + +// 1フレーム分の信号処理 (IIR フィルタ) の実行 +void ProcessSignal(SD_BinaryReader &sdReader, SaiIO_O &mySai, + int16_t sn[], float g0, Biquad hn[], + int order, bool filterOn) +{ + sdReader.Read(sn, mySai.GetLength()); // 1フレーム分のデータを SD から読み込む + + while (!mySai.IsXferred()) {} // データの転送が終わるまで待つ + //-------------------------------------------------------------- + // 1フレーム分の信号処理を行い,その結果を出力する + for (int n=0; n<mySai.GetLength(); n++) + { + int16_t value; + if (filterOn) // フィルタ処理実行 + { + // 縦続形の IIR フィルタ + 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(); // 次のデータ転送に備える +} + +// SD カードのファイルのオープン +int32_t SD_Open(SD_BinaryReader &sdReader, + string fileName, int32_t frameSize) +{ + sdReader.Open(fileName); + return sdReader.ReadSize()/frameSize; +} + +// ファイルの選択 +// selectedName: 選択されたファイル名 +void SelectFile(ButtonGroup &menu, FileSelector &selector, + Label &msg, string &selectedName) +{ + msg.Draw("Select file"); + selector.DisplayFileList(); + do + { + if (selector.Select(selectedName)) + menu.Activate(1); // PLAY 有効 + wait_ms(200); + } while (!menu.Touched(1)); // PLAY がタッチされるまで繰り返す +} + +// フィルタの変更 +void ModifyFilter(DesignerDrawer &drawerObj, + ButtonGroup &lpHp, ButtonGroup &onOff, + Biquad hn[], Biquad::Coefs ck[], + float &g0, bool &filterOn) +{ + // フィルタ処理の有効/無効切り替え + int sw = 0; + if (onOff.GetTouchedNumber(sw)) + filterOn = (sw == 0) ? true : false; + + // フィルタの周波数特性の変更 + static int num = 0; + lpHp.GetTouchedNumber(num); + BilinearDesign::Type typeLH = (BilinearDesign::Type)num; + if (drawerObj.ReDesignAndDraw(ck, g0, typeLH)) + for (int k=0; k<drawerObj.GetOrder()/2; k++) + { + hn[k].SetCoefficients(ck[k]); + hn[k].Clear(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/MyFunctions.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,37 @@ +//-------------------------------------------------------------- +// フィルタ処理付き SD オーディオプレーヤーで使う大域関数(ヘッダ) +// +// 2016/04/07, Copyright (c) 2016 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef F746_MY_FUNCTIONS_HPP +#define F746_MY_FUNCTIONS_HPP + +#include "sai_io_o.hpp" +#include "ButtonGroup.hpp" +#include "FileSelector.hpp" +#include "DesignerDrawer.hpp" +#include "SD_BinaryReader.hpp" + +using namespace Mikami; + +// 1フレーム分の信号処理 (IIR フィルタ) の実行 +void ProcessSignal(SD_BinaryReader &sdReader, SaiIO_O &mySai, + int16_t sn[], float g0, Biquad hn[], + int order, bool filterOn); + +// SD カードのファイルのオープン +int32_t SD_Open(SD_BinaryReader &sdReader, + string fileName, int32_t frameSize); + +// ファイルの選択 +void SelectFile(ButtonGroup &menu, FileSelector &selector, + Label &msg, string &selectedName); + +// フィルタの変更 +void ModifyFilter(DesignerDrawer &drawerObj, + ButtonGroup &lpHp, ButtonGroup &onOff, + Biquad hn[], Biquad::Coefs ck[], + float &g0, bool &filterOn); + +#endif // F746_MY_FUNCTIONS_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/SD_BinaryReader.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,86 @@ +//-------------------------------------------------------------- +// SD_BinaryReader class +// SD カードの内容を読み出す +// 最初の4バイト:データサイズ +// それ以降: int16_t のデータ +// +// 2016/04/07, Copyright (c) 2016 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef SD_BINARY_READER_HPP +#define SD_BINARY_READER_HPP + +#include "SDFileSystem.h" +#include "BlinkLabel.hpp" +#include <string> + +namespace Mikami +{ + class SD_BinaryReader + { + public: + SD_BinaryReader() : STR_("sd"), ok_(false) + { + sd_ = new SDFileSystem(STR_.c_str()); + sd_->mount(); + } + + ~SD_BinaryReader() + { + sd_->unmount(); + delete sd_; + } + + void Open(const string fileName) + { + string name = (string)"/" + STR_ + "/" + fileName; + fp_ = fopen(name.c_str(), "rb"); + if (fp_ == NULL) ErrorMsg("open error!!"); + } + + void Close() { fclose(fp_); } + + // ファイルからデータサイズの読み出し + // 戻り値: int16_t 型のデータサイズ + int32_t ReadSize() + { + fread(&size_, sizeof(int), 1, fp_); + ok_ = true; + return size_; + } + + // ファイルからデータの取得 + void Read(int16_t data[], uint32_t size) + { + if (!ok_) ErrorMsg("Get data FAILED"); + fread(data, sizeof(int16_t), size, fp_); + } + + // データサイズの取得 + // 戻り値: int16_t 型のデータサイズ + int32_t GetSize() + { + if (!ok_) ErrorMsg("Get data size FAILED"); + return size_; + } + + private: + const string STR_; + + SDFileSystem *sd_; + FILE *fp_; + + bool ok_; + int32_t size_; // word count, word = int16_t + + void ErrorMsg(char msg[]) + { + BlinkLabel errLabel(240, 100, msg, Label::CENTER); + } + + // disallow copy constructor and assignment operator + SD_BinaryReader(const SD_BinaryReader&); + SD_BinaryReader& operator=(const SD_BinaryReader&); + }; +} +#endif // SD_BINARY_READER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/TouchPanelDetectorX.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,46 @@ +//------------------------------------------------------ +// Touch panel detector for x axis class +// +// 2016/03/31, Copyright (c) 2016 MIKAMI, Naoki +//------------------------------------------------------ + +#ifndef F746_TOUCH_PANELDETECTORX_HPP +#define F746_TOUCH_PANELDETECTORX_HPP + +#include "GuiBase.hpp" + +namespace Mikami +{ + class TouchPanelDetectorX : public GuiBase + { + public: + // Constructor + TouchPanelDetectorX(uint16_t x1, uint16_t x2, + uint16_t y1, uint16_t y2) + : X1_(x1), X2_(x2), Y1_(y1), Y2_(y2) {} + + bool IsTouched(uint16_t xIn, uint16_t &xOut) + { + GetTsState(); + + if (!state_.touchDetected) return false; + + uint16_t x = state_.touchX[0]; + uint16_t y = state_.touchY[0]; + + if ( (x < X1_) || (x > X2_) || (y < Y1_) || (y > Y2_) ) + return false; + + const int WD = 8; + if ( (x < xIn-WD) || (x > xIn+WD) ) return false; + + xOut = (x >= X1_) ? x : X1_; + xOut = (x <= X2_) ? x : X2_; + return true; + } + + private: + const uint16_t X1_, X2_, Y1_, Y2_; + }; +} +#endif // F746_TOUCH_PANELDETECTORX_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/sai_io_o.cpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,77 @@ +//----------------------------------------------------------- +// SiaIO class for output +// 2016/02/16, Copyright (c) 2016 MIKAMI, Naoki +//----------------------------------------------------------- + +#include "sai_io_o.hpp" + +namespace Mikami +{ + SaiIO_O::SaiIO_O(int size, int fs) : FS_(fs), tmpIndex_(0) + { + nData_ = size; + bufferSize_ = (size*2)*2; + outBuffer_ = new int16_t[(size*2)*2]; + tmp_ = new int16_t[size*2]; + xferred_ = false; + } + + void SaiIO_O::InitCodecOut() + { + if (BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE, VOLUME_OUT_, FS_) == AUDIO_ERROR) + ErrorTrap(); + for (int n=0; n<bufferSize_; n++) outBuffer_[n] = 0; + for (int n=0; n<nData_*2; n++) tmp_[n] = 0; + + NVIC_SetVector(AUDIO_OUT_SAIx_DMAx_IRQ, (uint32_t)AUDIO_OUT_SAIx_DMAx_IRQHandler); + BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02); + + if (BSP_AUDIO_OUT_Play((uint16_t *)outBuffer_, + bufferSize_*AUDIODATA_SIZE) == AUDIO_ERROR) + ErrorTrap(); + } + + bool SaiIO_O::IsXferred() + { + if (xferred_) + { + tmpIndex_ = 0; + return true; + } + else + return false; + } + + void SaiIO_O::Output(int16_t xL, int16_t xR) + { + tmp_[tmpIndex_++] = xL; // Left + tmp_[tmpIndex_++] = xR; // Right + } + + void SaiIO_O::ErrorTrap() + { + DigitalOut led1(LED1); + fprintf(stderr, "\r\n### ERROR\r\n"); + while(true) + { + led1 = !led1; + wait_ms(250); + } + } + + void SaiIO_O::FillBuffer(uint32_t offset) + { + int k = offset; + for (int n=0; n<nData_*2; n++) + outBuffer_[k++] = tmp_[n]; + xferred_ = true; + } + + // Instances for static variables + int32_t SaiIO_O::nData_; + int32_t SaiIO_O::bufferSize_; + int16_t* SaiIO_O::outBuffer_; + int16_t* SaiIO_O::tmp_; + __IO bool SaiIO_O::xferred_; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyClasses_Functions/sai_io_o.hpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,59 @@ +//----------------------------------------------------------- +// SiaIO class for output (Header) +// 2016/02/16, Copyright (c) 2016 MIKAMI, Naoki +//----------------------------------------------------------- + +#ifndef F746_SAI_IO_HPP +#define F746_SAI_IO_HPP + +#include "mbed.h" +#include "stm32746g_discovery_audio.h" +#include "BSP_AudioOut_Overwrite.hpp" + +namespace Mikami +{ + class SaiIO_O + { + public: + SaiIO_O(int size, int fs); + + void InitCodecOut(); + + bool IsXferred(); + void Output(int16_t xL, int16_t xR); + + void ResetXferred() { xferred_ = false; } + int32_t GetLength() { return nData_; } + void Stop() { BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW); } + void Pause() { BSP_AUDIO_OUT_Pause(); } + void Resume() { BSP_AUDIO_OUT_Resume(); } + + + // These three member functions are called from + // callback functions in "BSP_AudioOut_Overwrite.cpp" + + // Called form BSP_AUDIO_OUT_HalfTransfer_CallBack() + static void FillBuffer1st() { FillBuffer(0); } + // Called form BSP_AUDIO_OUT_TransferComplete_CallBack() + static void FillBuffer2nd() { FillBuffer(bufferSize_/2); } + // Also called form BSP_AUDIO_OUT_Error_CallBack() + static void ErrorTrap(); + + private: + const int FS_; + static const uint8_t VOLUME_OUT_ = 90; + + static int32_t nData_; + static int32_t bufferSize_; + + static int16_t* outBuffer_; + static int16_t* tmp_; + + static __IO bool xferred_; + + __IO int32_t tmpIndex_; + + static void FillBuffer(uint32_t offset); + }; +} +#endif // F746_SAI_IO_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDFileSystem.lib Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/DieterGraef/code/SDFileSystem/#c03ef1abef0e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TS_DISCO_F746NG.lib Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,1 @@ +https://developer.mbed.org/teams/ST/code/TS_DISCO_F746NG/#fe0cf5e2960f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,169 @@ +//-------------------------------------------------------------- +// フィルタ処理付き SD オーディオプレーヤー +// SD のファイル +// 先頭から 4 バイト: データ数に対応する (int32_t 型) +// それ以降 2 バイトごと: int16_t のモノラルデータが続く +// データ: 標本化周波数 16 kHz のもの +// 拡張子: "*.bin", "*.BIN" +// IIR フィルタ ---- 低域通過および高域通過フィルタ +// +// 2016/04/08, Copyright (c) 2016 MIKAMI, Naoki +//-------------------------------------------------------------- + +#include "MyFunctions.hpp" +#include "BlinkLabel.hpp" + +using namespace Mikami; + +int main() +{ + Label myLabel(80, 4, "SD Card Audio Player", Label::LEFT, Font16); + + const int FS = I2S_AUDIOFREQ_16K; // 標本化周波数: 16 kHz + SaiIO_O mySai(1024, FS); + + SD_BinaryReader sdReader; // SD カード読み込み用オブジェクト + const int MAX_FILES = 7; + FileSelector selector(4, 28, MAX_FILES, 32, sdReader); + if (!selector.CreateTable()) + BlinkLabel errLabel(240, 100, "SD CARD ERROR", Label::CENTER); + + // ボタン用の定数 + const uint16_t BG_LEFT = 390; + const uint16_t BG_WIDTH = 80; + const uint16_t BG_HEIGHT = 36; + + // ButtonGroup: "OPEN", "PLAY", "PAUSE", "RESUME", "STOP" + const string MENU[5] = {"OPEN", "PLAY", "PAUSE", "RESUME", "STOP"}; + ButtonGroup menu(BG_LEFT, 2, BG_WIDTH, BG_HEIGHT, + 5, MENU, 0, 2, 1); + // OPEN のみアクティブ + menu.Activate(0); + for (int n=1; n<5; n++) menu.Inactivate(n); + + // ButtonGroup: "LPF", "HPF" + const string LP_HP[2] = {"LPF", "HPF"}; + ButtonGroup lpHp(BG_LEFT, 197, BG_WIDTH/2, BG_HEIGHT, + 2, LP_HP, 0, 0, 2, 0); + + // ButtonGroup: "ON", "OFF" + const string ON_OFF[2] = {"ON", "OFF"}; + ButtonGroup onOff(BG_LEFT, 235, BG_WIDTH/2, BG_HEIGHT, + 2, ON_OFF, 0, 0, 2, 1); + + // フィルタの設計と周波数特性描画用 + const int ORDER = 6; // フィルタの次数 + DesignerDrawer drawerObj( + 60, // グラフの左端の位置 + 230, // グラフの下端の位置 + 30, // 10 dB 当たりのピクセル数 + FS, // 標本化周波数 + ORDER, // フィルタの次数 + 400, // 最初に与える遮断周波数 + 200, // 遮断周波数の最小値 + 5000, // 遮断周波数の最大値 + BilinearDesign::LPF); // 低域通過フィルタ + + // フィルタの準備 + Biquad::Coefs ck[ORDER/2]; + float g0; + drawerObj.GetCoefficients(ck, g0); + Biquad hn[ORDER/2]; + for (int k=0; k<ORDER/2; k++) hn[k] = Biquad(ck[k]); + + int32_t frameSize = mySai.GetLength(); + int16_t *sn = new int16_t[frameSize+1]; // フレームバッファ + bool playOk = false; + bool filterOn = false; + bool whileFirst = true; + string fileName; + int32_t loopCount; + + while (true) + { + if (!playOk) + { + if (whileFirst) + { + whileFirst = false; + while (!menu.Touched(0)) // OPEN がタッチされるまで待つ + ModifyFilter(drawerObj, lpHp, onOff, + hn, ck, g0, filterOn); + SelectFile(menu, selector, myLabel, fileName); + } + else + { + menu.Activate(1); // PLAY 有効 + int touch10; + while (!menu.GetTouchedNumber(touch10)) + ModifyFilter(drawerObj, lpHp, onOff, + hn, ck, g0, filterOn); + if (touch10 == 0) + SelectFile(menu, selector, myLabel, fileName); + } + + loopCount = SD_Open(sdReader, fileName, frameSize); + while (!menu.Touched(1)) // PLAY がタッチされるまで待つ + ModifyFilter(drawerObj, lpHp, onOff, + hn, ck, g0, filterOn); + } + else + loopCount = SD_Open(sdReader, fileName, frameSize); + + selector.Erase(BG_LEFT-4, 244); + myLabel.Draw("IIR Butterworth filter"); + drawerObj.DrawResponse(); + menu.Inactivate(0); // OPEN 無効 + menu.Activate(2); // PAUSE 有効 + menu.Activate(4); // 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++) + { + int touch42 = -1; + menu.GetTouchedNumber(touch42); + if (touch42 == 4) break; // STOP + if (touch42 == 2) // PAUSE + { + menu.Inactivate(2); // PAUSE 無効 + menu.Activate(3); // RESUME 有効 + mySai.Pause(); + + // PLAY か RESUME か STOP がタッチされるまで待つ + int touch134 = -1; + while (!menu.GetTouchedNumber(touch134)) + ModifyFilter(drawerObj, lpHp, onOff, + hn, ck, g0, filterOn); + switch (touch134) + { + case 1: playOk = true; // 最初から PLAY + break; + case 3: mySai.Resume(); // PAUSE したところから PLAY 再開 + menu.Activate(2); + menu.Inactivate(3); + menu.TouchedColor(1); + break; + case 4: stopOk = true; // STOP + break; + } + } + if (playOk || stopOk) break; + + ModifyFilter(drawerObj, lpHp, onOff, hn, ck, g0, filterOn); + // 1フレーム分の信号処理 (IIR フィルタ) の実行 + ProcessSignal(sdReader, mySai, sn, g0, hn, ORDER, filterOn); + } + mySai.Stop(); + menu.Activate(0); // OPEN 有効 + if (!playOk) menu.Activate(1); // PLAY 有効 + for (int n=2; n<5; n++) // その他は無効 + menu.Inactivate(n); + sdReader.Close(); // SD のファイルのクローズ + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Apr 08 13:11:53 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/99a22ba036c9 \ No newline at end of file