9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【SD音声入門】M5Stackで音の再生・生成・録音で遊んでみた♪

Posted at

今回は、前回に引き続いて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)としています。

再生.c
# 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文という処理でごまかしています。

生成.c
# 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でマイクの利用方法を確認してみました

録音.c
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, NULL);

    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でやってきたことを実現したいと思う

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?