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
Diff: main.cpp
- Revision:
- 7:5ba884060d3b
- Parent:
- 6:c38ec7939609
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; }