Realtime sound spectrogram using FFT or linear prediction. Spectrogram is displayed on the display of PC. リアルタイム・スペクトログラム.解析の手法:FFT,線形予測法.スペクトログラムは PC のディスプレー装置に表示される.PC 側のプログラム:F446_Spectrogram.
Dependencies: Array_Matrix mbed SerialTxRxIntr F446_AD_DA UIT_FFT_Real
Revision 7:5ba884060d3b, committed 2019-11-24
- Comitter:
- MikamiUitOpen
- Date:
- Sun Nov 24 11:14:01 2019 +0000
- Parent:
- 6:c38ec7939609
- Commit message:
- 8
Changed in this revision
diff -r c38ec7939609 -r 5ba884060d3b Array_Matrix.lib --- a/Array_Matrix.lib Sun Nov 04 10:41:02 2018 +0000 +++ b/Array_Matrix.lib Sun Nov 24 11:14:01 2019 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/MikamiUitOpen/code/Array_Matrix/#a25dba17218c +https://os.mbed.com/users/MikamiUitOpen/code/Array_Matrix/#d9dea7748b27
diff -r c38ec7939609 -r 5ba884060d3b DoubleBufferMatrix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DoubleBufferMatrix.hpp Sun Nov 24 11:14:01 2019 +0000 @@ -0,0 +1,59 @@ +//-------------------------------------------------------- +// ダブル・バッファの template クラス +// バッファに2次元配列(Matrix クラス)を使用 +// +// 2019/11/22, Copyright (c) 2019 MIKAMI, Naoki +//-------------------------------------------------------- + +#ifndef DOUBLE_BUFFER_MATRIX_HPP +#define DOUBLE_BUFFER_MATRIX_HPP + +#include "Matrix.hpp" +using namespace Mikami; + +template<class T> class DoubleBuffer +{ +public: + // コンストラクタ + explicit DoubleBuffer(int size, T initialValue = 0) + : N_(size), buf_(2, size, initialValue), ping_(0), pong_(1), + index_(0), full_(false) {} + + // データを格納 + void Store(T data) { buf_[ping_][index_++] = data; } + + // 出力バッファからデータの取り出し + T Get(int n) const { return buf_[pong_][n]; } + + // バッファが満杯でバッファを切り替える + bool IsFullSwitch() + { + if (index_ < N_) return false; + + ping_ ^= 0x1; // バッファ切換えのため + pong_ ^= 0x1; // バッファ切換えのため + index_ = 0; + full_ = true; + return true; + } + + // バッファが満杯で,true を返す + bool IsFull() + { + bool temp = full_; + if (full_) full_ = false; + return temp; + } + +private: + const int N_; // バッファのサイズ + Matrix<T> buf_; // バッファ + int ping_, pong_; // バッファ切替用 + int index_; // 入力データのカウンタ + bool full_; // 満杯の場合 true + + // コピー・コンストラクタおよび代入演算子の禁止のため + DoubleBuffer(const DoubleBuffer&); + DoubleBuffer& operator=(const DoubleBuffer&); +}; +#endif // DOUBLE_BUFFER_MATRIX_HPP
diff -r c38ec7939609 -r 5ba884060d3b MySpectrogram/FFT_Analyzer.cpp --- a/MySpectrogram/FFT_Analyzer.cpp Sun Nov 04 10:41:02 2018 +0000 +++ b/MySpectrogram/FFT_Analyzer.cpp Sun Nov 24 11:14:01 2019 +0000 @@ -1,7 +1,7 @@ //------------------------------------------------------- // Class for spectrum analysis using FFT // -// 2018/11/04, Copyright (c) 2018 MIKAMI, Naoki +// 2018/11/24, Copyright (c) 2018 MIKAMI, Naoki //------------------------------------------------------- #include "FFT_Analyzer.hpp" @@ -17,7 +17,7 @@ fft_.Execute(xn, yFft_); // Execute FFT // Translate to dB - for (int n=2; n<=N_FFT_/2; n++) + for (int n=0; n<=N_FFT_/2; n++) yn[n] = 10.0f*log10f(Norm(yFft_[n])); } }
diff -r c38ec7939609 -r 5ba884060d3b SerialTxRxIntr.lib --- a/SerialTxRxIntr.lib Sun Nov 04 10:41:02 2018 +0000 +++ b/SerialTxRxIntr.lib Sun Nov 24 11:14:01 2019 +0000 @@ -1,1 +1,1 @@ -http://os.mbed.com/users/MikamiUitOpen/code/SerialTxRxIntr/#190d94fba10d +https://os.mbed.com/users/MikamiUitOpen/code/SerialTxRxIntr/#a90a9abeaea0
diff -r c38ec7939609 -r 5ba884060d3b Xfer.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Xfer.hpp Sun Nov 24 11:14:01 2019 +0000 @@ -0,0 +1,58 @@ +//--------------------------------------------------------------------- +// スペクトル解析の結果を PC へ転送するためのクラス +// +// 2019/11/24, Copyright (c) 2019 MIKAMI, Naoki +//--------------------------------------------------------------------- + +#include <string> +#include "Array.hpp" +#include "SerialRxTxIntr.hpp" +using namespace Mikami; + +#ifndef XFER_CONVERT_TOPC_HPP +#define XFER_CONVERT_TOPC_HPP + +class Xfer +{ +public: + // コンストラクタ + Xfer(SerialRxTxIntr& rxTx, int size) + : SIZE_(size), xn_(size), rxTx_(rxTx) {} + + // スペクトル解析の結果を転送する形式に変換 + void Convert(const float db[], float levelShift) + { + static const float FACTOR = 10000.0f/80.0f; // 表示範囲: 0 ~ 80 dB + for (int n=0; n<SIZE_; n++) + { + float xDb = FACTOR*(db[n] + 30.0f + levelShift); + if (xDb > 10000) xDb = 10000; + if (xDb < 0) xDb = 0; + xn_[n] = (uint16_t)xDb; + } + } + + // データを PC へ転送(0 ~ 10,000 の範囲の値を 2 文字で表すコード化を利用) + void ToPC() + { + string str = ""; + for (int n=0; n<SIZE_; n++) + { + div_t a = div(xn_[n], 100); + str += a.quot + 0x10; + str += a.rem + 0x10; + } + rxTx_.Tx(str+"\n"); + rxTx_.Tx("EOT\n"); + } + +private: + const int SIZE_; // PC に送るデータ数 + Array<uint16_t> xn_; // PC に送るデータ + SerialRxTxIntr& rxTx_; + + // コピー・コンストラクタおよび代入演算子の禁止のため + Xfer(const Xfer&); + Xfer& operator=(const Xfer&); +}; +#endif // XFER_CONVERT_TOPC_HPP
diff -r c38ec7939609 -r 5ba884060d3b main.cpp --- a/main.cpp Sun Nov 04 10:41:02 2018 +0000 +++ b/main.cpp Sun Nov 24 11:14:01 2019 +0000 @@ -14,16 +14,17 @@ // ● 出力 A2: 左チャンネル,D13 右チャンネル // 入力をそのまま出力する // -// 2018/11/04, Copyright (c) 2018 MIKAMI, Naoki +// 2018/11/24, Copyright (c) 2018 MIKAMI, Naoki //--------------------------------------------------------------------- #include "mbed.h" #include <string> -#include "myFunction.hpp" #include "Array.hpp" #include "F446_ADC_Interrupt.hpp" #include "FFT_Analyzer.hpp" #include "LPC_Analyzer.hpp" +#include "DoubleBufferMatrix.hpp" +#include "Xfer.hpp" using namespace Mikami; #ifndef __STM32F446xx_H @@ -34,30 +35,11 @@ const int N_DATA_ = N_FFT_ + 1; // スペクトル解析に使うデータ数(差分処理を考慮) const int N_FRAME_ = N_FFT_/2 + 1; // 1フレーム当たり標本化するデータ数 const int N_FFT_2_ = N_FFT_/2; // FFT の点数の半分 -const float AMP_ = 1.0f/2048.0f; // uint16_t 型のデータを float 型に変換する際の定数 - -uint16_t xPing_[N_FRAME_]; // 標本化したデータのバッファ1 -uint16_t xPong_[N_FRAME_]; // 標本化したデータのバッファ2 -uint16_t *inPtr_ = xPing_; // AD 変換データの格納先を指すポインタ -uint16_t *outPtr_ = xPing_; // 取り出すデータを指すポインタ - -__IO int inCount_ = 0; // 入力データのカウンタ -__IO int pingPong_ = 0; // 入力データの格納先,0: xPing_[], 1: xPong_[] -__IO bool full_ = false; // AD 変換データが満杯のとき true const int FS_ = 16000; // 標本化周波数: 16 kHz AdcDual_Intr myAdc_(FS_); // "F446_ADC_Interrupt.hpp" で定義 DacDual myDac_; // "F446_DAC.cpp/hpp" で定義 - -// FFT によるスペクトル解析オブジェクトの生成 -FftAnalyzer *fftAnlz_ = new FftAnalyzer(N_DATA_, N_FFT_); -// 線形予測法 によるスペクトル解析オブジェクトの生成 -LpcAnalyzer *lpcAnlz_ = new LpcAnalyzer(N_DATA_, N_FFT_, 20); -AnalyzerBase *analyzer_ = fftAnlz_; // 最初は FFT を使う - -SerialRxTxIntr rxTx_(32, 115200*4); // PC との通信用 - -float empha_ = 1.0f; // 高域強調器の係数 +DoubleBuffer<float> buf_(N_FRAME_); // AD の結果を保存するダブル・バッファ // 入力チャンネルを選択する関数とそれを割り当てる関数ポインタ float InputL(float x1, float x2) { return x1; } @@ -70,50 +52,48 @@ // ADC 変換終了割り込みに対する割り込みサービス・ルーチン void AdcIsr() { - uint16_t sn1, sn2; + float sn1, sn2; myAdc_.Read(sn1, sn2); - uint16_t xn = InputCurrent(sn1, sn2); - inPtr_[inCount_] = xn; + float xn = InputCurrent(sn1, sn2); + buf_.Store(xn); // バッファへ格納 myDac_.Write(xn, xn); - if (++inCount_ >= N_FRAME_) // データが満杯か調べる - { - full_ = true; // データが満杯 - inCount_ = 0; // 以降のデータ取得のため - pingPong_ = (pingPong_+1) & 0x01; // バッファの切り替えのため - inPtr_ = (pingPong_ == 0) ? xPing_ : xPong_; // バッファのポインタ指定 - InputCurrent = InputNew; // 入力の切り替え - analyzer_->SetHighEmphasizer(empha_); // 高域強調の有無の指令 - } + if (buf_.IsFullSwitch()) // バッファが満杯であればバッファを切り替える + InputCurrent = InputNew; // 入力の切り替え } int main() { - const int DATA_SIZE = N_FFT_/2 + 1; - Array<uint16_t> txData(DATA_SIZE); // 送信用データ - float sn[N_DATA_]; // スペクトル解析の対象となるデータ - float db[N_FRAME_]; // 解析結果である対数スペクトル [dB] - for (int n=0; n<N_DATA_; n++) sn[n] = 0; - for (int n=0; n<N_FRAME_; n++) xPong_[n] = 2048; // uint16_t 型の 0 に対応 + // FFT によるスペクトル解析オブジェクトの生成 + FftAnalyzer *fftAnlz_ = new FftAnalyzer(N_DATA_, N_FFT_); + // 線形予測法 によるスペクトル解析オブジェクトの生成 + LpcAnalyzer *lpcAnlz_ = new LpcAnalyzer(N_DATA_, N_FFT_, 20); + AnalyzerBase *analyzer = fftAnlz_; // 最初は FFT を使う + float empha = 1.0f; // 高域強調器の係数 + + SerialRxTxIntr rxTx(32, 115200*4); // PC との通信用 + Xfer tx(rxTx, N_FFT_/2+1); // PC に転送するためのオブジェクトの生成 + + Array<float> sn(N_FFT_, 0.0f); // スペクトル解析の対象となるデータ + Array<float> db(N_FRAME_); // 解析結果:対数スペクトル [dB] NVIC_SetPriority(ADC_IRQn, 0); // AD変換終了割り込みの優先度が最高 NVIC_SetPriority(USART2_IRQn, 1); float levelShift = 20; // dB 計算の際のシフト量の初期値 - full_ = false; - __IO bool ready = false; // スペクトルの計算終了で true - __IO bool okGo = false; // "GO" を受信したら true + bool ready = false; // スペクトルの計算終了で true + bool okGo = false; // "GO" を受信したら true myAdc_.SetIntrVec(&AdcIsr); // AD変換終了割り込みの割り当て while (true) { // PC からのコマンドの解析 - if (rxTx_.IsEol()) // 受信バッファのデータが有効になった場合の処理 + if (rxTx.IsEol()) // 受信バッファのデータが有効になった場合の処理 { - string str = rxTx_.GetBuffer(); + string str = rxTx.GetBuffer(); if (str == "Spectrogram") - rxTx_.Tx("ACK\n"); // PC からの "Spectrogram" に対して "ACK" を送信する + rxTx.Tx("ACK\n"); // PC からの "Spectrogram" に対して "ACK" を送信する else if (str.substr(0, 2) == "GO") { // str の内容 @@ -134,46 +114,35 @@ levelShift = (float)(str[3] - ' '); // dB 計算の際のシフト量 - if (str[4] == 'Y') empha_ = 1.0f; // 高域強調器は有 - else empha_ = 0; // 高域強調器は無 + if (str[4] == 'Y') empha = 1.0f; // 高域強調器は有 + else empha = 0; // 高域強調器は無 - if (str[5] == 'F') analyzer_ = fftAnlz_; // FFT - else analyzer_ = lpcAnlz_; // 線形予測法 + if (str[5] == 'F') analyzer = fftAnlz_; // FFT + else analyzer = lpcAnlz_; // 線形予測法 okGo = true; // データの転送要求あり } } - if (full_) // 入力データが満杯かどうか調べる + if (buf_.IsFull()) // 入力データが満杯の場合,以下の処理を行う { - full_ = false; - - outPtr_ = (pingPong_ == 1) ? xPing_ : xPong_; // フレームの後半のデータを前半に移動する for (int n=0; n<N_FFT_2_; n++) sn[n] = sn[n+N_FRAME_]; // フレームの後半には新しいデータを格納する for (int n=0; n<N_FRAME_; n++) - sn[n+N_FFT_2_] = AMP_*(outPtr_[n] - 2048); - - analyzer_->Execute(sn, db); // スペクトル解析の実行 + sn[n+N_FFT_2_] = buf_.Get(n); - const float FACTOR = 10000.0f/80.0f; // 表示範囲: 0 ~ 80 dB - for (int n=0; n<DATA_SIZE; n++) - { - float xDb = FACTOR*(db[n] + 30.0f + levelShift); - if (xDb > 10000.0f) xDb = 10000.0f; - if (xDb < 0.0f) xDb = 0.0f; - uint16_t spc = (uint16_t)xDb; - txData[n] = spc; - } - ready = true; // スペクトル解析終了 + analyzer->SetHighEmphasizer(empha); // 高域強調の有無の指令 + analyzer->Execute(sn, db); // スペクトル解析の実行 + tx.Convert(db, levelShift); // スペクトル解析の結果を転送する形式に変換 + ready = true; // スペクトル解析終了 } // 転送要求がありスペクトル解析が終了している場合にデータを PC へ転送する if (okGo && ready) { - Xfer(txData); // データを PC へ転送 + tx.ToPC(); // データを PC へ転送 ready = false; okGo = false; }
diff -r c38ec7939609 -r 5ba884060d3b mbed.bld --- a/mbed.bld Sun Nov 04 10:41:02 2018 +0000 +++ b/mbed.bld Sun Nov 24 11:14:01 2019 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/a7c7b631e539 \ No newline at end of file +https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400 \ No newline at end of file
diff -r c38ec7939609 -r 5ba884060d3b myFunction.hpp --- a/myFunction.hpp Sun Nov 04 10:41:02 2018 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -//--------------------------------------------------------------------- -// データを PC へ転送 -// -// 2018/10/07, Copyright (c) 2018 MIKAMI, Naoki -//--------------------------------------------------------------------- - -#include <string> -#include "Array.hpp" -#include "SerialRxTxIntr.hpp" -using namespace Mikami; - -#ifndef MY_FUNCTION_XFER_HPP -#define MY_FUNCTION_XFER_HPP - -extern SerialRxTxIntr rxTx_; - -// データを PC へ転送(0 ~ 10,000 の範囲の値を 2 文字で表すコード化を利用) -void Xfer(Array<uint16_t> &xn) -{ - string str = ""; - for (int n=0; n<xn.Length(); n++) - { - div_t a = div(xn[n], 100); - str += a.quot + 0x10; - str += a.rem + 0x10; - } - rxTx_.Tx(str+"\n"); - rxTx_.Tx("EOT\n"); -} - -#endif // MY_FUNCTION_XFER_HPP