![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
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
main.cpp
- Committer:
- MikamiUitOpen
- Date:
- 2018-10-07
- Revision:
- 5:fcc1b0b4737e
- Parent:
- 4:fe1885675421
- Child:
- 6:c38ec7939609
File content as of revision 5:fcc1b0b4737e:
//--------------------------------------------------------------------- // スペクトログラム (Nucleo-F446RE 用) // // ● ST-Link Firmware の V2.J28.M16 で動作確認 // // ● ST-Link Firmware のアップグレードには stsw-link07.zip // に含まれている "ST-LinkUpgrade.exe" を使う // // ● PC 側のプログラム: "F446_Spectrogram" // ● ボーレート: 460800 baud // ● 受信データの文字列の終了マーク: "\r" // // ● 入力 A0: 左チャンネル,A1 右チャンネル // ● 出力 A2: 左チャンネル,D13 右チャンネル // 入力をそのまま出力する // // 2018/10/07, 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" using namespace Mikami; #ifndef __STM32F446xx_H #error "Use Nucleo-F446RE" #endif const int N_FFT_ = 512; // FFT の点数 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; // 高域強調器の係数 // 入力チャンネルを選択する関数とそれを割り当てる関数ポインタ float InputL(float x1, float x2) { return x1; } float InputR(float x1, float x2) { return x2; } float InputLR(float x1, float x2) { return (x1 + x2)/2; } typedef float (*FP_INPUT)(float, float); FP_INPUT InputCurrent = InputLR; // 最初は左右チャンネルを使う FP_INPUT InputNew = InputCurrent; // ADC 変換終了割り込みに対する割り込みサービス・ルーチン void AdcIsr() { uint16_t sn1, sn2; myAdc_.Read(sn1, sn2); uint16_t xn = InputCurrent(sn1, sn2); inPtr_[inCount_] = 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_); // 高域強調の有無の指令 } } 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 に対応 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 myAdc_.SetIntrVec(&AdcIsr); // AD変換終了割り込みの割り当て while (true) { // PC からのコマンドの解析 if (rxTx_.IsEol()) // 受信バッファのデータが有効になった場合の処理 { string str = rxTx_.GetBuffer(); if (str == "Spectrogram") rxTx_.Tx("ACK\n"); // PC からの "Spectrogram" に対して "ACK" を送信する else if (str.substr(0, 2) == "GO") { // str の内容 // [0] 'G' // [1] 'O' // [2] 入力チャンネルの選択:'L', 'R', or '+' // [3] スペクトルの値のレベルシフト:' ' ~ 'I' が -20 ~ 20 に対応 // [4] 高域強調器の有無:'Y', 'N' // [5] 解析方法 F: FFT,L: 線形予測法 switch (str[2]) { case 'L': InputNew = InputL; break; case 'R': InputNew = InputR; break; case '+': InputNew = InputLR; break; default : InputNew = InputLR; break; } levelShift = (float)(str[3] - ' '); // dB 計算の際のシフト量 if (str[4] == 'Y') empha_ = 1.0f; // 高域強調器は有 else empha_ = 0; // 高域強調器は無 if (str[5] == 'F') analyzer_ = fftAnlz_; // FFT else analyzer_ = lpcAnlz_; // 線形予測法 okGo = true; // データの転送要求あり } } if (full_) // 入力データが満杯かどうか調べる { 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); // スペクトル解析の実行 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; // スペクトル解析終了 } // 転送要求がありスペクトル解析が終了している場合にデータを PC へ転送する if (okGo && ready) { Xfer(txData); // データを PC へ転送 ready = false; okGo = false; } } }