Newer
Older
M5StickCPlus_FactoryTest2022 / SampleSrc / speakerhat01.ino
// プログラムと電子工作・楽曲演奏(3)SpeakerHATへDAC出力 by gokkunhebi
// https://note.com/pclub/n/neb539f9b5b1f を少しだけ改変

#include <M5StickCPlus.h>
// wavデータを読み込み
#include "coin16k.h"
#include "vivaldi_1ch_16000hz_uint8.h"
#define SAMPLING_FREQ 16000
#define VOLUME 11         /*音量、0~11 最大*/
#define DACWRITE_DELAY 20 /*dacWrite()処理時間(ms)*/
//  実験的に dacWrite()の実行時間が 20マイクロ秒かかることが判明した。

#define SPEAKER_PIN GPIO_NUM_26 /*SpeakerHAT 26ピン*/

//------------------------------------------------------------------------------
//  setup()
void setup()
{
  //  電源ON時に 1回だけ実行する処理をここに書く。
  M5.begin(115200);        /*M5を初期化する*/
  M5.Axp.ScreenBreath(30); /*画面の輝度*/
  M5.Lcd.setTextSize(3);   /*文字サイズはちょっと小さめ*/
  M5.Lcd.setRotation(3);   /*上スイッチが左になる向き*/

  /*setCpuFrequencyMhz(80);*/
  /*240, 160, 80, 40, 20MHz と変えるとだんだん演奏速度が遅くなる。*/
  M5.Lcd.fillScreen(GREENYELLOW);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextColor( BLACK, GREEN );
  M5.Lcd.println("dacwrite");
  M5.Lcd.println("Press BtnA/B");

  pinMode(SPEAKER_PIN, OUTPUT);
}
//------------------------------------------------------------------------------
//  loop()
void loop()
{
  //  自動的に繰り返し実行する処理をここに書く。
  uint32_t start_millis = 0;

  M5.update();

  if (M5.BtnA.wasPressed())
  {
    //  M5StickC Plus の場合、SPEAKER_PINが GPIO_NUM_2では動作しない。
    //  GPIO_NUM_2には DA変換機能がない。
    M5.Lcd.println("coin!!");
    //  ■  演奏が終了するまで playMusic()関数はブロックされる。
    Serial.println("*** playMusic()");
    start_millis = millis(); /*演奏開始時刻*/
    playMusic(coin16k, coin16k_len / sizeof(coin16k[0]), SAMPLING_FREQ, VOLUME);
    Serial.printf("*** elapsed time: %4.1f sec\n", (millis() - start_millis) / 1000.0); /*演奏時間*/
  }
  else if (M5.BtnB.wasReleased())
  {
    //  ヴィヴァルディ「春」を演奏する。
    M5.Lcd.println("spring!!");
    playMusic(wav, sizeof(wav) / sizeof(wav[0]), SAMPLING_FREQ, VOLUME);
    Serial.printf("*** elapsed time: %4.1f sec\n", (millis() - start_millis) / 1000.0); /*演奏時間*/
  }
  delay(1);
}
//------------------------------------------------------------------------------
//  playMusic
//  楽曲データを演奏する。
//  ■  演奏が終了するまでこの関数はブロックされる。
//      in: const uint8_t* music_data 8ビット符号なし整数の音の振幅データ
//          const uint32_t length music_dataのバイト数
//          const uint32_t sample_rate 楽曲データのサンプリング周波数
//          const uint8_t volume 音量、最大 11、最小 0
//  ・  wavファイルの場合は、dataチャンクを 8ビット符号なし整数に変換する。
//  ・  M5StickC Plus の場合、SPEAKER_PIN は GPIO_NUM_25、GPIO_NUM_26 の 2ピンでのみ動作する。
//  ・  delay_interval は理論値から DACWRITE_DELAYを減じる。なぜこれほど処理時間がかかるのか不明。
void playMusic(const uint8_t *music_data, const uint32_t length, const uint32_t sample_rate, const uint8_t volume)
{
  uint32_t delay_interval = (uint32_t)1000000 / sample_rate - DACWRITE_DELAY; /*dacWrite()処理時間を引く*/
  uint8_t vol = (volume >= 11) ? 1 : 11 - volume;
  for (uint32_t i = 0; i < length; i++)
  {
    dacWrite(SPEAKER_PIN, music_data[i] / vol);
    delayMicroseconds(delay_interval);
  }
  //  フェードアウト
  for (int t = music_data[length - 1] / vol; t >= 0; t--)
  {
    dacWrite(SPEAKER_PIN, t);
    delay(2);
  }

  // reset screen
  M5.Lcd.fillScreen(GREENYELLOW);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("dacwrite");
  M5.Lcd.println("Press BtnA/B");
}