// プログラムと電子工作・楽曲演奏(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");
}