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:
2019-11-24
Revision:
7:5ba884060d3b
Parent:
6:c38ec7939609

File content as of revision 7:5ba884060d3b:

//---------------------------------------------------------------------
//  スペクトログラム (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/11/24, Copyright (c) 2018 MIKAMI, Naoki
//---------------------------------------------------------------------

#include "mbed.h"
#include <string>
#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
#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 int FS_ = 16000;      // 標本化周波数: 16 kHz
AdcDual_Intr myAdc_(FS_);   // "F446_ADC_Interrupt.hpp" で定義
DacDual myDac_;             // "F446_DAC.cpp/hpp" で定義
DoubleBuffer<float> buf_(N_FRAME_); // AD の結果を保存するダブル・バッファ

// 入力チャンネルを選択する関数とそれを割り当てる関数ポインタ
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()
{
    float sn1, sn2;
    myAdc_.Read(sn1, sn2);
    float xn = InputCurrent(sn1, sn2);
    buf_.Store(xn);     // バッファへ格納
    myDac_.Write(xn, xn);

    if (buf_.IsFullSwitch())        // バッファが満杯であればバッファを切り替える
        InputCurrent = InputNew;    // 入力の切り替え
}

int main()
{
    // 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 計算の際のシフト量の初期値

    bool ready = false;     // スペクトルの計算終了で true
    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 (buf_.IsFull())  // 入力データが満杯の場合,以下の処理を行う
        {
            // フレームの後半のデータを前半に移動する
            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_] = buf_.Get(n);

            analyzer->SetHighEmphasizer(empha); // 高域強調の有無の指令
            analyzer->Execute(sn, db);  // スペクトル解析の実行
            tx.Convert(db, levelShift); // スペクトル解析の結果を転送する形式に変換
            ready = true;               // スペクトル解析終了
        }

        // 転送要求がありスペクトル解析が終了している場合にデータを PC へ転送する
        if (okGo && ready)
        {
            tx.ToPC();      // データを PC へ転送
            ready = false;
            okGo = false;
        }
    }
}