0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5StickS3でMP3を再生する

0
Last updated at Posted at 2026-02-22

M5StickS3にしっかりオーディオコーデックがついているので、MP3を再生します。
M5StickS3には、ES8311が積んであって、I2Cで制御するそうです。
でも、サンプルコードを見てみたら、M5Unifiedがセットアップしてくれているので、なんと全く意識することなく使えそうです。

ESP32上で動作するJavascript環境でMP3を鳴らしてみます。
以下のESP32での例を示します。I2Sを使う方法とDACを使う方法があり、ESP32のモジュールに搭載している状況に合わせます。

I2S方式

  • M5StickS3
  • M5Stack Core2
  • M5Atom Echo

DAC方式

  • M5StickC+Spearker Hat
  • M5Stack Fire

ちなみに、DAC方式は結構CPUを使うため、音が途切れやすく音質が悪くなります。

Javascriptソースコード

以下、I2S方式の場合です。ポート番号は自動的に判別してくれるので、指定はなくても大丈夫でした。

main.js
import * as audio from "Audio";
import * as input from "Input";

function setup(){
	audio.begin();
	var ret = audio.playUrl("http://192.168.1.16:20080/g_07.mp3");
	if( !ret )
		console.log("playUrl error");
	input.onButtonWasPressed(input.BUTTON_A, (e) =>{
		console.log("pressed");
		audio.playUrl("http://192.168.1.16:20080/g_07.mp3");
	});
	console.log("setup finished");
}

function loop(){
	esp32.update();
	audio.update();
}

もし、自動で判別されないモジュール種の場合は、以下のようにポート番号等を指定します。
第一引数はサンプリングレート、第二引数は内部で確保する内部バッファサイズです。第四引数にESP32のモジュールに合わせてポート番号を指定しています。

main.js
    // M5StickS3の場合
	audio.begin( 48000, 1536, audio.OUTPUT_EXTERNAL_I2S, { bck: 17, lrck: 15, dout: 14, mck: 18 } );

    // M5Stack Core2の場合
	audio.begin( 48000, 1536, audio.OUTPUT_EXTERNAL_I2S, { bck: 12, lrck: 0, dout: 2 } );

    // M5Atom Echoの場合
	audio.begin( 48000, 1536, audio.OUTPUT_EXTERNAL_I2S, { bck: 19, lrck: 33, dout: 22 } );

DACの場合は以下の通りです。ポート番号は、26固定です。ですので、M5StickCもM5Stack Fireも同じです。

main.js
import * as audio from "Audio";
import * as input from "Input";

function setup(){
	audio.begin( 48000, 1536, audio.OUTPUT_INTERNAL_DAC, { dout: 26 } );
	audio.setVolume(100);
	var ret = audio.playUrl("http://192.168.1.16:20080/g_07.mp3");
	if( !ret )
		console.log("playUrl error");
	input.onButtonWasPressed(input.BUTTON_A, (e) =>{
		console.log("pressed");
		audio.playUrl("http://192.168.1.16:20080/g_07.mp3");
	});	
	console.log("setup finished");
}

function loop(){
	esp32.update();
	audio.update();
}

内部実装

内部的にはJavascsriptではなく通常のC言語で実現していますが、Javascriptを使うのみであれば、意識する必要はありません。
M5Unifiedには、MP3を解釈する機能はありません。そこで、ESP8266Audioの力を借りています。

抜き出すとこんな感じです。
まずは準備部分(audio.begin())です。

module_audio.cpp
  auto spk_cfg = M5.Speaker.config();
  spk_cfg.sample_rate = sample_rate;
    
  if( output_mode == AUDIO_OUTPUT_INTERNAL_DAC ){
    spk_cfg.use_dac = true;
  }else if( output_mode == AUDIO_OUTPUT_EXTERNAL_I2S ){
    spk_cfg.use_dac = false;
  }else if( output_mode == AUDIO_OUTPUT_AUTO ){
  }else{
    return JS_EXCEPTION;
  }

// ポート番号は必須ではなく、必要に応じて
    spk_cfg.pin_bck = pin_bck;
    spk_cfg.pin_ws = pin_lrck;
    spk_cfg.pin_data_out = pin_dout;
    spk_cfg.pin_mck = pin_mck;

  if( M5.Speaker.isRunning() )
    M5.Speaker.end();
  M5.Speaker.config(spk_cfg);
  M5.Speaker.begin();

  out = new AudioOutputM5Speaker(&M5.Speaker);

最後のAudioOutputM5Speakerは、M5.SpeakerとESP8266Audioとの間の音声データの橋渡しをしています。
M5UnifiedのSpeakerサンプルからほぼそのまま持ってきています。

module_audio.cpp
class AudioOutputM5Speaker : public AudioOutput
{
  public:
    AudioOutputM5Speaker(m5::Speaker_Class* m5sound, uint8_t virtual_sound_channel = 0)
      : _m5sound(m5sound), _virtual_ch(virtual_sound_channel){}
    
    bool setBufferSize(uint32_t buf_size){
      for (int i = 0; i < 3; i++) {
        if( _tri_buffer[i] != NULL ){
          free(_tri_buffer[i]);
          _tri_buffer[i] = NULL;
        }
      }
      for (int i = 0; i < 3; i++) {
        _tri_buffer[i] = (int16_t*)malloc(buf_size * sizeof(int16_t));
        if (_tri_buffer[i] == NULL) {
          for (int j = 0; j < i; j++) {
            free(_tri_buffer[j]);
            _tri_buffer[j] = NULL;
          }
          return false;
        }
      }
      tri_buf_size = buf_size;
      return true;
    }

    virtual ~AudioOutputM5Speaker(void) {
      for (int i = 0; i < 3; i++) {
        if( _tri_buffer[i] != NULL )
          free(_tri_buffer[i]);
      }
    };
    
    virtual bool begin(void) override { return true; }

    virtual bool ConsumeSample(int16_t sample[2]) override
    {
      if (_tri_buffer_index < tri_buf_size)
      {
        _tri_buffer[_tri_index][_tri_buffer_index  ] = sample[0];
        _tri_buffer[_tri_index][_tri_buffer_index+1] = sample[1];
        _tri_buffer_index += 2;

        return true;
      }

      flush();
      return false;
    }

    virtual void flush(void) override
    {
      if (_tri_buffer_index)
      {
        _m5sound->playRaw(_tri_buffer[_tri_index], _tri_buffer_index, hertz, true, 1, _virtual_ch);
        _tri_index = _tri_index < 2 ? _tri_index + 1 : 0;
        _tri_buffer_index = 0;
      }
    }

    virtual bool stop(void) override
    {
      flush();
      _m5sound->stop(_virtual_ch);
      return true;
    }

    const int16_t* getBuffer(void) const { return _tri_buffer[(_tri_index + 2) % 3]; }

    virtual bool SetRate(int hz) override
    {
//      Serial.printf("SetRate called: %d\n", hz);
      hertz = hz;
      return true;
    }

  protected:
    m5::Speaker_Class* _m5sound;
    uint8_t _virtual_ch;
    size_t tri_buf_size = 0;
    int16_t *_tri_buffer[3] = { NULL, NULL, NULL };
    size_t _tri_buffer_index = 0;
    size_t _tri_index = 0;
};

次が、MP3再生部分です。インターネット上からMP3ファイルを取得する例です。

module_audio.cpp
  file_http = new AudioFileSourceHTTPStream(url);
  buff = new AudioFileSourceBuffer(file_http, bufsize);
  mp3 = new AudioGeneratorMP3();
  bool ret = mp3->begin(buff, out);

SDカードからMP3ファイルを取得する場合は以下になります。

module_audio.cpp
  file_sd = new AudioFileSourceSD(path);
  mp3 = new AudioGeneratorMP3();
  bool ret = mp3->begin(file_sd, out);

あとは、以下を繰り返すだけです。

module_audio.cpp
  if( mp3 != NULL ){
    if (mp3->isRunning()) {
      if( !audio_paused ){
        if (!mp3->loop()){
//          Serial.println("mp3 loop stop");
          mp3->stop();
        }
      }
    }

M5UnifiedのSpeakerのおかげで、これだけで再生できちゃいました。

(参考) ESP32で動作するJavascript実行環境

ESP32で動作するJavascript実行環境を公開しています。
この中で実装しています。
Javascriptのコードを書き換えるのに、いちいち再コンパイル不要なので、らくちんです。

「電子書籍:M5StackとJavascriptではじめるIoTデバイス制御」

サポートサイト

ぜひ、手に取ってみてください。

以上

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?