SSブログ

SPRESENSEスペクトログラムをアップデート!! [SPRESENSE]

SPRESENSEスペクトログラムをアップデートしました。今回は微妙な変化がわかるようにグレースケールで表示してみました。あとピーク周波数を出すようにしています。


sDSC_0008.jpg


実際の動きはこちらをご覧ください。





スケッチはこちら。解析はメインコアで、表示はサブスコアで行っています。MP.Mutex でサブスコアにデータを送るタイミングをとっています。


■ メインコアのスケッチ
#ifdef SUBCORE
#error "Core selection is wrong!!"
#endif

#include 
#include 
#include 
#define SUBCORE1 1

MPMutex mutex(MP_MUTEX_ID0);
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

#include 
#include 
#include 
#define FFT_LEN  256
#define CHANNEL_NUM 1

// #define LPF_ENABLE
#define LPF_CUTOFF 3000
#define LPF_QVALUE 0.70710678

// #define SMA_ENABLE
#define SMA_WINDOW 8

FFTClass FFT;
IIRClass LPF;

AudioClass *theAudio = AudioClass::getInstance();
static const int32_t buffer_sample = FFT_LEN;
static const int32_t buffer_size = buffer_sample * sizeof(int16_t);
static const uint32_t sampling_rate = AS_SAMPLINGRATE_48000;
static char buff[buffer_size];

static float pDst[FFT_LEN];
static float pOut[FFT_LEN];

#ifdef SMA_ENABLE
static float pSMA[SMA_WINDOW][FFT_LEN];

void applySMA(float sma[SMA_WINDOW][FFT_LEN], float dst[FFT_LEN]) 
{
  int i, j;
  static int g_counter = 0;
  if (g_counter == SMA_WINDOW) g_counter = 0;
  for (i = 0; i < FFT_LEN; ++i) {
    sma[g_counter][i] = dst[i];
    float sum = 0;
    for (j = 0; j < SMA_WINDOW; ++j) {
      sum += sma[j][i];
    }
    dst[i] = sum / SMA_WINDOW;
  }
  ++g_counter;
}
#endif

static void audioReadFrames() {
  int err, ret;
  uint32_t read_size;
  static bool bInit = true;
  static q15_t pLPFSig[buffer_size];
  static const unsigned long wait_usec =  (double)(buffer_sample)/sampling_rate*1000000;
  Serial.println(wait_usec);
  
  while(1) {
    err = theAudio->readFrames(buff, buffer_size, &read_size);
    if (err != AUDIOLIB_ECODE_OK && err != AUDIOLIB_ECODE_INSUFFICIENT_BUFFER_AREA) {
      Serial.println("Error err = " + String(err));
      theAudio->stopRecorder();
      exit(1);
    }

    if (read_size < buffer_size) {
      usleep(wait_usec);
      continue;
    }
    
#ifdef LPF_ENABLE
    // 3kHz low pass filter
    LPF.put((q15_t*)buff, buffer_sample);
    LPF.get(pLPFSig, 0);
    FFT.put(pLPFSig, buffer_sample);
#else
    FFT.put((q15_t*)buff, buffer_sample);
#endif

    if (bInit) { bInit = false; continue; }

    // Using mutex to protect pDst array
    if (pthread_mutex_lock(&m) != 0) Serial.println("Mutex Lock Error");
    FFT.get(pDst, 0);
#ifdef SMA_ENABLE
    applySMA(pSMA, pDst);
#endif
    if (pthread_mutex_unlock(&m) != 0) Serial.println("Mutex UnLock Error");

  }
}

void setup() {
  int8_t sndid; 
  Serial.begin(115200);
  
  MP.begin(SUBCORE1);
  sndid = 101;
  MP.Send(sndid, FFT_LEN, SUBCORE1);
  MP.RecvTimeout(MP_RECV_POLLING); 


#ifdef LPF_ENABLE
  LPF.begin(TYPE_LPF, CHANNEL_NUM, LPF_CUTOFF, LPF_QVALUE);
#endif
  FFT.begin(WindowRectangle, CHANNEL_NUM, (FFT_LEN/2));
  
  theAudio->begin();
  // theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC, 200);
  theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC);
  int err = theAudio->initRecorder(AS_CODECTYPE_PCM ,"/mnt/sd0/BIN" 
                           ,AS_SAMPLINGRATE_48000 ,AS_CHANNEL_MONO);                             
  if (err != AUDIOLIB_ECODE_OK) {
    Serial.println("Recorder initialize error");
    while(1);
  }

  theAudio->startRecorder(); 
  Serial.println("Start Recording");
  task_create("audio recording", 120, 1024, audioReadFrames, NULL);
  sleep(1);
}

void loop() {
  int err, ret;
  int8_t sndid;

  // Using mutex to protect pDst array
  if (pthread_mutex_lock(&m) != 0) Serial.println("Mutex Lock Error");
  memcpy(pOut, pDst, buffer_size);
  if (pthread_mutex_unlock(&m) != 0) Serial.println("Mutex UnLock Error");

  // Using MPMutex to check the availablity of SubCore
  if (mutex.Trylock() != 0) { usleep(20000); return; }
  sndid = 100;
  err = MP.Send(sndid, &pOut, SUBCORE1);
  if (err < 0) Serial.println("MP Send error\n");
  mutex.Unlock();  
}



■ サブコアのスケッチ

#if (SUBCORE != 1)
#error "Core selection is wrong!!"
#endif

#define ARM_MATH_CM4
#define __FPU_PRESENT 1U
#include 

#include 
#include 
#include 

#include 
#include 

#define CS  10
#define DC   9   
#define RST  8
Adafruit_ILI9341 tft = Adafruit_ILI9341(CS, DC, RST);

MPMutex mutex(MP_MUTEX_ID0);

#define SPECTRO_WIDTH  (128)
#define SPECTRO_HEIGHT (320)
static uint16_t frameBuffer[SPECTRO_HEIGHT][SPECTRO_WIDTH];

#define DEBUG
// #define PSEUDO_COLOR

static uint16_t fft_len = 0;

float get_peak_frequency(float *pData, int fftLen)
{
  float g_fs = 48000.0f;
  uint32_t index;
  float maxValue;
  float delta;
  float peakFs;

  arm_max_f32(pData, fftLen / 2, &maxValue, &index);

  delta = 0.5 * (pData[index - 1] - pData[index + 1])
    / (pData[index - 1] + pData[index + 1] - (2.0f * pData[index]));
  peakFs = (index + delta) * g_fs / (fftLen - 1);

  return peakFs;
}

void setup() {
  int8_t recvid;
  uint32_t msg; 
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(35, 210);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);
  tft.println("FFT Spectrogram Viewer");
  tft.setTextColor(ILI9341_BLUE);
  tft.setTextSize(2);
  tft.setCursor(35, 35);
  tft.print("Peak FS:");
  tft.setCursor(210, 35);
  tft.print("Hz");
  tft.setRotation(2);
  memset(frameBuffer, 255, SPECTRO_WIDTH*SPECTRO_HEIGHT*sizeof(uint16_t));

  MP.begin();

  MP.Recv(&recvid, &msg);
  fft_len = msg;
  MPLog("%d FFT_LEN %d\n", recvid, fft_len);
  MP.RecvTimeout(MP_RECV_POLLING); 
}

void loop() {

  // based on CXD5247 technical manual
  static const float spr_signal_noise_ratio = 90.0; // SNR 
 
  static uint16_t colormap[] = {
    ILI9341_MAGENTA,
    ILI9341_BLUE,
    ILI9341_CYAN,
    ILI9341_GREEN,
    ILI9341_YELLOW,
    ILI9341_ORANGE,
    ILI9341_RED,
    ILI9341_WHITE
  };
  
  int8_t msgid;
  float *data;
  int ret, i, j;
  float f_max, f_min;
  
  ret = MP.Recv(&msgid, &data);
  if (ret < 0)  return;

  // Using MPMutex to notify MainCore that SubCore is in busy
  do { ret = mutex.Trylock(); } while (ret != 0);

#ifdef DEBUG
  f_max = 0.0;
  f_min = 1000.0;
  for (i = 0; i < fft_len/2; ++i) {
    float val = abs(data[i]);
    // MPLog("[%d] %f\n", i, val);
    if (val == 0.0) continue;
    if (val > f_max) f_max = val;
    if (val < f_min) f_min = val;
  }
  MPLog("RANGE: %3.6f - %+3.6f\n", f_max, f_min);
#endif
  
  f_max = -1000.0;
  f_min =  1000.0;
  for (i = 0; i < fft_len/2; ++i) {
    // if (!isnan(data[i]) && data[i] > 0.0) { 
    if (data[i] != 0.0) { 
      data[i] = 20.*log10(abs(data[i])) + spr_signal_noise_ratio;
      if (data[i] > f_max) f_max = data[i];
      if (data[i] < f_min) f_min = data[i];
    } 
  }
  MPLog("Range: %3.6f - %3.6f\n\n", f_max, f_min);

  for (i = 1; i < SPECTRO_HEIGHT; ++i) {
    for (j = 0; j < SPECTRO_WIDTH; ++j) {
      frameBuffer[i-1][j] = frameBuffer[i][j];
    }
  }

  // display range:0:0Hz - 200:9.375kHz
  for (i = 0; i < SPECTRO_WIDTH; ++i) {
    float f_val = data[i];
#ifdef PSEUDO_COLOR
    static const float magnify = 1;
    uint8_t index = magnify*f_val/32; 
    frameBuffer[SPECTRO_HEIGHT-1][i] = colormap[index];
#else
    uint16_t val_6, val_5;
    val_6 = (uint32_t)(f_val) * 64 / 256;
    val_5 = (uint32_t)(f_val) * 32 / 256;
    uint16_t val = val_5 << 11 | val_6 << 5 | val_5; 
    frameBuffer[SPECTRO_HEIGHT-1][i] = val;
#endif
  }
  
  tft.drawRGBBitmap(40, 0, (uint16_t*)frameBuffer, SPECTRO_WIDTH, SPECTRO_HEIGHT);

  uint16_t peak_fs = get_peak_frequency(data, fft_len);

  static int g_counter = 0;

  if (g_counter % 10 == 0) {
    char num_buf[8];
    tft.setRotation(3);
    tft.setTextColor(ILI9341_BLUE);
    tft.setTextSize(2);
    tft.setCursor(140, 35);
    tft.fillRect(140, 35, 60, 15, ILI9341_BLACK);
    sprintf(num_buf, "%05d", peak_fs);
    tft.print(num_buf);
    tft.setRotation(2);
    g_counter = 1;
  } else {
    ++g_counter;
  }

  mutex.Unlock();
}



次はどうやって学習データを記録していくか考えたいと思います。
(。-`ω´-)ンー





SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



Voice Assistant Using SONY Spresense (English Edition)

Voice Assistant Using SONY Spresense (English Edition)

  • 作者: Perez, Guillermo
  • 出版社/メーカー:
  • 発売日: 2019/03/11
  • メディア: Kindle版




nice!(25)  コメント(0) 
共通テーマ:趣味・カルチャー

nice! 25

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。