今回は、前回に引き続いてM5StackのSD利用して、音の再生・生成・録音で遊んでみました。
やりきっていませんが、現状をまとめておきます。
やったこと
・環境構築
・SDカードのwavを再生
・関数生成波形ドレミ音として再生
・マイク音をSDに録音
・環境構築
多分SDを持っているM5Stack製品では、適用できると思うが、今回はCore2を利用しました。
参考は、以下のとおりですが、環境はPlatformIOは利用せず、通常のArduinoIDEで実施しました。
Libは、通常のCore2と音の処理のために
・ESP8266audio
を利用します。これは、普通にツールーライブラリを管理でインストールでESP8266Audioでインストールできます。
ESP32,RaspiPicoなどにも対応しているようです。
また、ボタン処理のために以下のVButtonを利用しています。今回は、VButton.cppとVButton.hのコードをAruduinoIDEでタグを追加して貼り付けて利用しました。
・furuya02/M5Stack_Core2_VirtualButton
【参考】
[M5Stack Core2 for AWS] Arduinoでオーディオファイルを再生してみました
・SDカードのwavを再生
このコードは、ほぼ上記参考のとおりです。
変更したのは、Wav再生に変更し、SDにaudioW.wav等をあらかじめ保存しているので、pathを合わせています。
ボタンの表示は、オリジナルのまま(bach, beethoven, chopin)としています。
# include <M5Core2.h>
# include <driver/i2s.h>
# include "AudioFileSourceSD.h"
# include "AudioGeneratorWAV.h"
//#include "AudioGeneratorMP3.h"
# include "AudioOutputI2S.h"
# include "VButton.h"
AudioGeneratorWAV *wav = NULL;
//AudioGeneratorMP3 *mp3 = NULL;
AudioFileSourceSD *file;
AudioOutputI2S *out;
# define BCLK_PIN 12
# define LRCK_PIN 0
# define SADTA_PIN 2
# define EXTERNAL_I2S 0
# define OUTPUT_GAIN 80
VButton * button_bach;
VButton * button_beethoven;
VButton * button_chopin;
uint16_t getColor(uint8_t red, uint8_t green, uint8_t blue){
return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
}
void button_callback(char *name, bool use_toggle, bool is_toggled){
char path[128];
// 再生中の場合は、停止する
if(wav != NULL){
wav->stop();
}
//sprintf(path,"/wav/%s.wav",name);
sprintf(path,"/%s.wav",name);
file = new AudioFileSourceSD(path);
out = new AudioOutputI2S(I2S_NUM_0, EXTERNAL_I2S);
out->SetPinout(BCLK_PIN, LRCK_PIN, SADTA_PIN);
out->SetOutputModeMono(true);
out->SetGain((float)OUTPUT_GAIN/100.0);
wav = new AudioGeneratorWAV();
wav->begin(file, out);
}
void setup() {
M5.begin();
M5.Lcd.setBrightness(255);
M5.Lcd.fillScreen(WHITE);
// スピーカーの初期化
M5.Axp.SetSpkEnable(true);
// 操作ボタン
ushort dark_gray = getColor(80, 80, 80);
button_bach = new VButton("audioW", button_callback, false, -70, dark_gray);
button_beethoven = new VButton("merody", button_callback, false, 0, dark_gray);
button_chopin = new VButton("Chopin", button_callback, false, 70, dark_gray);
}
void loop() {
button_bach->loop();
button_beethoven->loop();
button_chopin->loop();
if(wav != NULL){
if (wav->isRunning()) {
if (!wav->loop()) wav->stop();
} else {
Serial.printf("WAV done\n");
delay(1000);
}
}
}
・関数生成波形ドレミ音として再生
このコードは、ESP8266audioのスケッチ例PlayWAVFromFunctionと上記のコードを融合して作成しました。
スケッチ例は out = new AudioOutputI2SNoDAC();
を利用していますが、ここでは上記のCore2のスピーカーである out = new AudioOutputI2S(I2S_NUM_0, EXTERNAL_I2S);
を利用しています。
また、ボタンで再生するのは同様です。関数は、ドレミの音を生成していますが、表示は当初440Hz, 1000Hz,2000Hzを計算していたので、その表示になっています。
実は、ここCallback中のif文が苦労しています。
最終的に
sprintf(path,"%s",name);
f=atof(path);
したあと、ifで判別しています。
pathには正しく文字(char)がはいっているので、そのままファイル名に使えそうですが、なぜか使えません。ということで、上記のようにatofで数値変換してif文という処理でごまかしています。
# include <M5Core2.h>
# include <driver/i2s.h>
# include "AudioFileSourceFunction.h"
# include "AudioGeneratorWAV.h"
# include "AudioOutputI2S.h"
# include "VButton.h"
AudioGeneratorWAV *wav;
AudioFileSourceFunction* file;
AudioOutputI2S *out;
# define BCLK_PIN 12
# define LRCK_PIN 0
# define SADTA_PIN 2
# define EXTERNAL_I2S 0
# define OUTPUT_GAIN 80
VButton * button_bach;
VButton * button_beethoven;
VButton * button_chopin;
uint16_t getColor(uint8_t red, uint8_t green, uint8_t blue){
return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
}
float sine_wave0440(const float time) {
float hz = 2*261.626f; //440.f;
float v = sin(TWO_PI * hz * time); // C
v *= fmod(time, 1.f); // change linear
v *= 0.5; // scale
return v;
};
float sine_wave1000(const float time) {
float hz = 2*293.665f;
float v = sin(TWO_PI * hz * time); // C
v *= fmod(time, 1.f); // change linear
v *= 0.5; // scale
return v;
};
float sine_wave2000(const float time) {
float hz = 2*329.628f;
float v = sin(TWO_PI * hz * time); // C
v *= fmod(time, 1.f); // change linear
v *= 0.5; // scale
return v;
};
void button_callback(char *name, bool use_toggle, bool is_toggled){
char path[14]="";
float f=0.44;
file = new AudioFileSourceFunction(8.);
file->addAudioGenerators(sine_wave0440);
// 再生中の場合は、停止する
if(wav != NULL){
wav->stop();
}
sprintf(path,"%s",name);
f=atof(path);
if(f==0.44){
file = new AudioFileSourceFunction(8.);
file->addAudioGenerators(sine_wave0440);
Serial.print(f);
}
if(f==1.00){
file = new AudioFileSourceFunction(8.);
file->addAudioGenerators(sine_wave1000);
Serial.print(f);
}
if(f==2.00){
file = new AudioFileSourceFunction(8.);
file->addAudioGenerators(sine_wave2000);
Serial.print(f);
}
//Serial.print(f);
out = new AudioOutputI2S(I2S_NUM_0, EXTERNAL_I2S);
out->SetPinout(BCLK_PIN, LRCK_PIN, SADTA_PIN);
out->SetOutputModeMono(true);
out->SetGain((float)OUTPUT_GAIN/100.0);
wav = new AudioGeneratorWAV();
wav->begin(file, out);
}
void setup() {
M5.begin();
M5.Lcd.setBrightness(255);
M5.Lcd.fillScreen(WHITE);
M5.Axp.SetSpkEnable(true);
// 操作ボタン
ushort dark_gray = getColor(80, 80, 80);
button_bach = new VButton("0.44", button_callback, false, -70, dark_gray);
button_beethoven = new VButton("1.00", button_callback, false, 0, dark_gray);
button_chopin = new VButton("2.00", button_callback, false, 70, dark_gray);
}
void loop() {
M5.update();
button_bach->loop();
button_beethoven->loop();
button_chopin->loop();
if(wav != NULL){
if (wav->isRunning()) {
if (!wav->loop()) wav->stop();
} else {
Serial.printf("function done\n");
delay(1000);
}
}
}
・マイク音をSDに録音
もともと録音した音声を再生するがしたかったのですが、参考のままになっています。
つまり、音声録音してAudacityでRawデータ再生までです。
また、再生に利用したAudacityはWindowsにインストールして利用しました。
※RasPi4ではうまく動きませんでした
【参考】
・[M5Stack Core2 for AWS] Arduinoでマイクの利用方法を確認してみました
include <M5Core2.h>
# include <driver/i2s.h>
# include "VButton.h"
//#include "Led.h"
# define SAMPLE_RATE 16000 //サンプリングレート
# define CHUNK 1024 // I2Sバッファサイズ
# define PERIAD 10 // 録音時間(sec)
# define CONFIG_I2S_LRCK_PIN 0 // I2Sクロック
# define CONFIG_I2S_DATA_IN_PIN 34 // I2Sデータ
bool InitMic(){
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 2,
.dma_buf_len = CHUNK
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NU・LL);
i2s_pin_config_t tx_pin_config = {
.bck_io_num = I2S_PIN_NO_CHANGE,//未使用
.ws_io_num = CONFIG_I2S_LRCK_PIN,
.data_out_num = I2S_PIN_NO_CHANGE, // 未使用
.data_in_num = CONFIG_I2S_DATA_IN_PIN
};
i2s_set_pin(I2S_NUM_0, &tx_pin_config);
}
VButton *button;
//Led *led;
void button_callback(char *title, bool use_toggle, bool is_toggled){
//led->set(255, 0, 0);
//File f = SD.open("/audio.dat", FILE_WRITE);
File f = SD.open("/audio.wav", FILE_WRITE);
if (f) {
uint8_t buffer[CHUNK];
size_t byte_read;
// サンプリングレート * 16bit * 1ch * 録音時間 (bit数)
// /8 (byte数)
// /CHANK (chank数)
int chank_max = (int)((SAMPLE_RATE*16*1*PERIAD)/8/CHUNK);
for(int i=0; i < chank_max; i++){
i2s_read(I2S_NUM_0, buffer, CHUNK, &byte_read, (100 / portTICK_RATE_MS));
Serial.printf(".");
f.write(buffer, CHUNK);
}
f.close();
}
//led->set(0, 0, 0);
}
void setup() {
M5.begin(true, true, true, true);
M5.Lcd.setBrightness(200);
M5.Lcd.fillScreen(WHITE);
button = new VButton("REC", button_callback);
//led = new Led();
InitMic();
}
void loop() {
button->loop();
}
まとめ
・SD利用して音の再生、関数生成、録音で遊んでみた
・関数生成音声のSD保管
・Wavファイルに変換し保管
・FFT分析などpythonでやってきたことを実現したいと思う