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

Files at this revision

API Documentation at this revision

Comitter:
MikamiUitOpen
Date:
Sun Nov 24 11:14:01 2019 +0000
Parent:
6:c38ec7939609
Commit message:
8

Changed in this revision

Array_Matrix.lib Show annotated file Show diff for this revision Revisions of this file
DoubleBufferMatrix.hpp Show annotated file Show diff for this revision Revisions of this file
MySpectrogram/FFT_Analyzer.cpp Show annotated file Show diff for this revision Revisions of this file
SerialTxRxIntr.lib Show annotated file Show diff for this revision Revisions of this file
Xfer.hpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
myFunction.hpp Show diff for this revision Revisions of this file
--- 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
--- /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
--- 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]));
     }
 }
--- 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
--- /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
--- 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;
         }
--- 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
--- 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