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