Frequency shifter for DISCO-F746. Input: MEMS mic, Output: CN10 OUT. DISCO-F746 による周波数シフタ.入力:MEMS マイク,出力:CN10 OUT
Dependencies: F746_GUI F746_SAI_IO mbed
Revision 0:67554e1407a7, committed 2018-03-19
- Comitter:
- MikamiUitOpen
- Date:
- Mon Mar 19 03:19:35 2018 +0000
- Child:
- 1:af3546588b73
- Commit message:
- 1
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/F746_GUI.lib Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/MikamiUitOpen/code/F746_GUI/#50b8f7654c36
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/F746_SAI_IO.lib Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/MikamiUitOpen/code/F746_SAI_IO/#61e2c3cc79a3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/Biquad.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,57 @@ +//-------------------------------------------------------------- +// 縦続形 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 +// +// 2017/03/26, Copyright (c) 2017 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(const Coefs ck = (Coefs){0, 0, 0, 0}) + { + SetCoefficients(ck); + Clear(); + } + + void SetCoefficients(const Coefs cf) { cf_ = cf; } + + float Execute(float xn) + { + float un = xn + cf_.a1*un1_ + cf_.a2*un2_; + float yn = un + cf_.b1*un1_ + cf_.b2*un2_; + + un2_ = un1_; + un1_ = un; + + return yn; + } + + void Clear() { un1_ = un2_ = 0; } + + private: + Coefs cf_; + float un1_, un2_; + + // disallow copy constructor + Biquad(const Biquad&); + }; +} +#endif // IIR_BIQUAD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/DC_Cut_Coefficients.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,18 @@ +//-------------------------------------------------------------- +// 直流分除去フィルタの係数,次数:2 +//-------------------------------------------------------------- +#include "Biquad.hpp" + +// 高域通過フィルタ +// バタワース特性 +// 次数 : 2 次 +// 標本化周波数: 16.00 kHz +// 遮断周波数 : 0.05 kHz + +using namespace Mikami; + +const Biquad::Coefs dcCut_ = + { 1.972234E+00f, -9.726140E-01f, -2.0f, 1.0f}; +const float dcCutG0_ = 9.862119E-01f; + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/FrequencyShifter.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,60 @@ +//-------------------------------------------------------------- +// ヒルベルト変換フィルタを使う周波数シフタ +// +// 2018/03/19, Copyright (c) 2018 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef HILBERT_FREQUENCY_CONVERTER_HPP +#define HILBERT_FREQUENCY_CONVERTER_HPP + +#include "ProcessingBase.hpp" +#include "Biquad.hpp" // 直流分除去フィルタ用 +#include "DC_Cut_Coefficients.hpp" // 直流分除去フィルタの係数 +#include "HilbertTransform.hpp" // ヒルベルト変換用フィルタ +#include "coefsHilbert162.hpp" // ヒルベルト変換用フィルタの係数 + +namespace Mikami +{ + class FrqShifterHilbert : public ProcessingBase + { + public: + FrqShifterHilbert(float fS, float fShift = 0) + : dcCutFilter_(dcCut_), hilbertFIR_(ORDER_HILBERT_, hmHilbert_), + FS_(fS), PI2_(3.1415926536f*2), + phi_(0), dPhi_(fShift*PI2_/FS_) + { SetFrequency(fShift); } + + virtual float Execute(float xn) + { + float un = dcCutFilter_.Execute(xn*dcCutG0_); // 直流分除去フィルタ + float yI, yQ; + hilbertFIR_.Execute(un, yI, yQ); // ヒルベルト変換用フィルタ + + float yn = yI*cosf(phi_) - yQ*sinf(phi_); + + phi_ = phi_ + dPhi_; + if (phi_ > PI2_) phi_ = phi_ - PI2_; + + return yn; + } + + // 周波数のシフト量を設定 + void SetFrequency(float fShift) + { dPhi_ = fShift*PI2_/FS_; } + + private: + // + Biquad dcCutFilter_; // 直流分除去フィルタ + Hilbert hilbertFIR_; // ヒルベルト変換用フィルタ + + const float FS_; + const float PI2_; + + float phi_, dPhi_; + + // disallow copy constructor and assignment operator + FrqShifterHilbert(const FrqShifterHilbert& ); + FrqShifterHilbert& operator=(const FrqShifterHilbert& ); + }; +} +#endif // HILBERT_FREQUENCY_CONVERTER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/GuiChanger.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,24 @@ +//-------------------------------------------------------------- +// 信号処理の種類に応じた GUI 部品の状態を変更する +// +// 2018/03/18, Copyright (c) 2018 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef GUI_CHANGER_HPP +#define GUI_CHANGER_HPP + +// Through の場合 +void SetThrough(SeekBar *barFqCh, NumericLabel<int> *frqLabel) +{ + barFqCh->Inactivate(); + frqLabel->Redraw(GuiBase::ENUM_INACTIVE_TEXT); +} + +// 周波数シフトの場合 +void SetFrqShifter(SeekBar *barFqCh, NumericLabel<int> *frqLabel) +{ + barFqCh->Activate(); + frqLabel->Redraw(GuiBase::ENUM_TEXT); +} + +#endif // GUI_CHANGER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/HilbertTransform.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,52 @@ +//-------------------------------------------------------------- +// ヒルベルト変換用フィルタ +// +// Copyright (c) 2018 MIKAMI, Naoki, 2018/03/19 +//-------------------------------------------------------------- + +#ifndef HILBERT_TRANSFORM_HPP +#define HILBERT_TRANSFORM_HPP + +#include "mbed.h" + +namespace Mikami +{ + class Hilbert + { + public: + // order: 4K+2 とすること, K: 整数 + Hilbert(int order, const float hk[]) + : ORDER_(order), HN_(order+1, hk), xn_(order+1, 0.0f) + { + if ( ((order-2) % 4) != 0) + fprintf(stderr, "order must be 4*K+2, K: integer\r\n"); + } + + // yI: 同相信号 + // yQ: 直角位相信号 + void Execute(float xin, float& yI, float& yQ) + { + yQ = 0.0; + xn_[0] = xin; + + for (int k=0; k<=ORDER_/4; k++) + yQ = yQ + HN_[k]*(xn_[2*k] - xn_[ORDER_-2*k]); + yI = xn_[ORDER_/2]; // 同相信号 + + for (int k=ORDER_; k>0; k--) + xn_[k] = xn_[k-1]; // 遅延器の入力信号の移動 + } + + private: + const int ORDER_; // 次数 + const Array<float> HN_; // 係数 + Array<float> xn_; // 入力信号用バッファ + + // disallow copy constructor and assignment operator + Hilbert(const Hilbert&); + Hilbert& operator=(const Hilbert&); + }; +} +#endif // HILBERT_TRANSFORM_HPP + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/InitializeGUI.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,60 @@ +//-------------------------------------------------------------- +// Hilbert 変換フィルタを利用する MEMS マイクの入力に対して周波数シフタ +// で使う GUI 等の初期化 +// +// 2018/03/18, Copyright (c) 2018 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef EFFECTOR_INIT_GUI_HPP +#define EFFECTOR_INIT_GUI_HPP + +#include "F746_GUI.hpp" +#include "WaveformDisplay.hpp" +using namespace Mikami; + +void InitializeGUI( + ButtonGroup *(&onOff), ButtonGroup *(&menu), + SeekBar *(&barFqCh), NumericLabel<int> *(&frqLabel), + WaveformDisplay *(&dispIn), + WaveformDisplay *(&dispOut)) +{ + Label myLabel(240, 8, "Frequency shifter", Label::CENTER, Font16); + + // ButtonGroup 用の定数 + const uint16_t BG_LEFT = 360; + const uint16_t BG_WIDTH = 110; + const uint16_t BG_HEIGHT = 45; + + // ButtonGroup: "ON", "OFF" + onOff = new ButtonGroup(BG_LEFT, 40, BG_WIDTH/2, BG_HEIGHT, + 2, (string[]){"ON", "OFF"}, 0, 0, 2, 1); + + // ButtonGroup: "THROUGH", "F_SHIFTER" + menu = new ButtonGroup(BG_LEFT, 110, BG_WIDTH, BG_HEIGHT, + 2, (string[]){"THROUGH", "VOICE CHANGER"}, + 0, 10, 1, 0); + + // SeekBar 用の定数 + const uint16_t SB_LEFT = BG_LEFT - 300; + const uint16_t SB_WIDTH = 256; + const uint16_t Y0_E = 195; + const uint16_t Y0_F = Y0_E + BG_HEIGHT + 5; + + // 周波数シフタ用 + barFqCh = new SeekBar(SB_LEFT, Y0_F, SB_WIDTH, + 0, 200, 100, "0", "", "200"); + barFqCh->Inactivate(); + frqLabel = new NumericLabel<int>( + SB_LEFT+SB_WIDTH/2, Y0_F-28, "+%d Hz", + barFqCh->GetIntValue(), Label::CENTER); + frqLabel->Redraw(GuiBase::ENUM_INACTIVE_TEXT); + + // 波形表示用 + Label inLabel(SB_LEFT-40, 55, "IN"); + dispIn = new WaveformDisplay( + GuiBase::GetLcd(), SB_LEFT, 60, 256, 9); + Label outLabel(SB_LEFT-40, 125, "OUT"); + dispOut = new WaveformDisplay( + GuiBase::GetLcd(), SB_LEFT, 130, 256, 9); +} +#endif // EFFECTOR_INIT_GUI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/ProcessingBase.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,22 @@ +//-------------------------------------------------------------- +// 信号処理のための基底クラス +// +// 2017/04/06, Copyright (c) 2017 MIKAMI, Naoki +//-------------------------------------------------------------- + +#ifndef PROCESSING_BASE_HPP +#define PROCESSING_BASE_HPP + +namespace Mikami +{ + class ProcessingBase + { + public: + ProcessingBase() {} + virtual ~ProcessingBase() {} + + virtual float Execute(float xn) + { return xn; } + }; +} +#endif // PROCESSING_BASE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/WaveformDisplay.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,74 @@ +//----------------------------------------------------------- +// Class for waveform display +// +// 2017/04/06, Copyright (c) 2016 MIKAMI, Naoki +//----------------------------------------------------------- + +#ifndef F746_WAVEFORM_DISPLAY_HPP +#define F746_WAVEFORM_DISPLAY_HPP + +#include "Array.hpp" + +namespace Mikami +{ + class WaveformDisplay + { + public: + WaveformDisplay(LCD_DISCO_F746NG &lcd, + uint16_t x0, uint16_t y0, int nData, + uint16_t rShift) + : X0_(x0), Y0_(y0), R_SHIFT_(rShift), + H1_(Y0_+LIMIT_+1), H2_(Y0_-LIMIT_-1), + lcd_(lcd) { Axis(nData); } + + void Execute(const Array<int16_t> &xn) + { + lcd_.SetTextColor(GuiBase::ENUM_BACK); + lcd_.FillRect(X0_, Y0_-LIMIT_-1, + xn.Length(), (LIMIT_+1)*2+1); + Axis(xn.Length()); + lcd_.SetTextColor(LINE_COLOR_); + uint16_t x1 = X0_; + uint16_t y1 = Clip(xn[0]); + for (int n=1; n<xn.Length(); n++) + { + uint16_t x2 = X0_ + n; + uint16_t y2 = Clip(xn[n]); + lcd_.DrawLine(x1, y1, x2, y2); + if ((y1 == H1_) || (y1 == H2_)) + lcd_.DrawPixel(x1, y1, LCD_COLOR_RED); + x1 = x2; + y1 = y2; + } + } + + private: + static const uint16_t LIMIT_ = 32; + static const uint32_t LINE_COLOR_ = LCD_COLOR_CYAN; + const uint16_t X0_, Y0_; + const uint16_t R_SHIFT_; + const uint16_t H1_, H2_; + + LCD_DISCO_F746NG& lcd_; + + // Clipping + uint16_t Clip(int16_t xn) + { + int16_t x = xn >> R_SHIFT_; + if (x > LIMIT_ ) x = LIMIT_ + 1; + if (x < -LIMIT_ ) x = -(LIMIT_ + 1) ; + return Y0_ - x; + } + + void Axis(int nData) + { + lcd_.SetTextColor(LCD_COLOR_WHITE); + lcd_.DrawLine(X0_-5, Y0_, X0_+nData+5, Y0_); + } + + // disallow copy constructor and assignment operator + WaveformDisplay(const WaveformDisplay& ); + WaveformDisplay& operator=(const WaveformDisplay& ); + }; +} +#endif // F746_WAVEFORM_DISPLAY_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyAcousticEffector_MIC/coefsHilbert162.hpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,28 @@ +//-------------------------------------------------------------- +// ヒルベルト変換用 FIR フィルタの係数 +// 次数:162 +//-------------------------------------------------------------- + +// 標本化周波数 (kHz) 16.000000 +// 次数 162 +// 帯域 1 +// 下側帯域端周波数 (kHz) 0.100000 +// 上側帯域端周波数 (kHz) 7.900000 +// 利得 1.000000 +// 重み 1.000000 +// 偏差 0.016871 +// 偏差 [dB] 0.145317 + +const int ORDER_HILBERT_ = 162; +const float hmHilbert_[(ORDER_HILBERT_-2)/4+1] = { + -9.452293E-03f, -2.143468E-03f, -2.386095E-03f, -2.645189E-03f, + -2.926359E-03f, -3.222066E-03f, -3.543330E-03f, -3.886371E-03f, + -4.251500E-03f, -4.642029E-03f, -5.061504E-03f, -5.510610E-03f, + -5.990406E-03f, -6.504444E-03f, -7.055964E-03f, -7.649082E-03f, + -8.287499E-03f, -8.976063E-03f, -9.719625E-03f, -1.052535E-02f, + -1.140120E-02f, -1.235730E-02f, -1.340540E-02f, -1.456034E-02f, + -1.583971E-02f, -1.726622E-02f, -1.886917E-02f, -2.068668E-02f, + -2.276868E-02f, -2.518285E-02f, -2.802285E-02f, -3.142147E-02f, + -3.557408E-02f, -4.078067E-02f, -4.752620E-02f, -5.664814E-02f, + -6.972917E-02f, -9.016101E-02f, -1.267624E-01f, -2.118692E-01f, + -6.365072E-01f};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,97 @@ +//-------------------------------------------------------------- +// MEMS マイクの入力に対して周波数シフトを行う +// Hilbert 変換フィルタを利用する方法 +// +// 使用しているライブラリのリビジョン: +// F746_GUI Rev.33 +// F746_SAI_IO Rev.12 +// mbed Rev.161 +// +// 2018/03/19, Copyright (c) 2018 MIKAMI, Naoki +//-------------------------------------------------------------- + +#include "InitializeGUI.hpp" +#include "SAI_InOut.hpp" +#include "FrequencyShifter.hpp" +#include "GuiChanger.hpp" +using namespace Mikami; + +int main() +{ + const int FS = AUDIO_FREQUENCY_16K; // 標本化周波数: 16 kHz + // 入出力の準備 + SaiIO mySai(SaiIO::BOTH, 256, FS, + INPUT_DEVICE_DIGITAL_MICROPHONE_2); + + ButtonGroup *onOff; // "ON", "OFF" + ButtonGroup *menu; // "THROUGH", "F_SHIFTER" + SeekBar *barFqCh; // シフトする周波数を設定するシークバー + NumericLabel<int> *frqLabel; + WaveformDisplay *displayIn, *displayOut; + + // GUI 部品の初期化 + InitializeGUI(onOff, menu, barFqCh, frqLabel, displayIn, displayOut); + + // 処理に応じて GUI 部品の状態を変更する関数の割り当て + void (*fPtr[])(SeekBar*, NumericLabel<int>*) + = { SetThrough, SetFrqShifter }; + ProcessingBase through; // 0: 信号処理なしで出力 + FrqShifterHilbert shifter(FS, 100); // 1: 周波数シフト(シフトの初期値:100 Hz) + + ProcessingBase *func[2] = { &through, &shifter }; + + Array<int16_t> snIn(mySai.GetLength()); // 入力波形表示で使用 + Array<int16_t> snOut(mySai.GetLength()); // 出力波形表示で使用 + + mySai.RecordIn(); // 入力開始 + mySai.PlayOut(); // 出力開始 + mySai.PauseOut(); // 出力一時停止 + + int menuNum = 0; + while (true) + { + // On/OFF の設定 + int num; + if (onOff->GetTouchedNumber(num)) + { + if (num == 0) mySai.ResumeOut(); // 出力再開 + else mySai.PauseOut(); + } + + // メニューにより GUI の状態を設定しなおす + if (menu->GetTouchedNumber(menuNum)) + fPtr[menuNum](barFqCh, frqLabel); + + // 周波数シフトの値の設定 + if ( (menuNum == 1) && (barFqCh->Slide()) ) + { + frqLabel->Draw(barFqCh->GetIntValue()); + shifter.SetFrequency(barFqCh->GetIntValue()); + } + + //--------------------------------------------- + // 1フレーム分の信号処理を行い,その結果を出力する + if (mySai.IsCompleted()) + { + for (int n=0; n<mySai.GetLength(); n++) + { + int16_t xL, xR; + mySai.Input(xL, xR); + int16_t xn = xL + xR; + snIn[n] = xn; // 表示用 + + //------------------------------------------------------- + int16_t yn = func[menuNum]->Execute(xn); // 信号処理実行 + //------------------------------------------------------- + + mySai.Output(yn, yn); // 左右チャンネルに同じ信号を出力 + snOut[n] = yn; // 表示用 + } + + displayIn->Execute(snIn); // 入力波形の表示 + displayOut->Execute(snOut); // 出力波形の表示 + } + // 1フレーム分の信号処理はここまで + //--------------------------------------------- + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon Mar 19 03:19:35 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/mbed_official/code/mbed/builds/aa5281ff4a02 \ No newline at end of file