5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ESP32でMMLを使って懐かしの音楽を再生する

5
Last updated at Posted at 2026-03-01

ESP32を使って、昔ファミコンにあったビープ音を使った音楽を再生します。
楽譜はMML(Music Macro Language)で書かれている前提です。
スピーカーはブザーを使います。M5StickCPlusに標準でついているブザーか、外付けのブザーを使います。

https://www.switch-science.com/products/6470
https://www.switch-science.com/products/9350
https://www.switch-science.com/products/7629

いつもの通り、ESP32で動作するJavascriptで実行します。
以下を使わせていただきました。ありがとうございました。

Javascriptコード

MMLの楽譜を再生するJavascriptソースコードです。
内部的には定期的なタイマを使っていますので、並行で別の処理をしていても大丈夫なようにしています。

main.js
import * as ledc from "Ledc";
import * as mml from "Mml";

const BUZZER_PIN = 2;
const LEDC_RESOLUTION = 8;
const LEDC_CHANNEL = 0;

var text = "[MMLの楽譜]";

function setup(){
	ledc.setup(LEDC_CHANNEL, 100, LEDC_RESOLUTION);
	ledc.attachPin(BUZZER_PIN, 0);

	mml.begin(LEDC_CHANNEL, 10, LEDC_RESOLUTION);
	mml.play(text, false);
}

解説

まずは、BUZZERが接続されたPINのLEDCの設定をします。

main.js
	ledc.setup(LEDC_CHANNEL, 100, LEDC_RESOLUTION);
	ledc.attachPin(BEEP_PIN, 0);

M5StickCPlusの場合は、GPIO02にブザーがついています。外付けのブザーの場合は、接続したGPIOのpin番号を指定します。

次に、MMLを再生する前の準備をします。LEDCに指定したパラメータを教えてあげます。

main.js
	mml.begin(LEDC_CHANNEL, 10, LEDC_RESOLUTION);

あとは、mml.playでMMLの楽譜を指定すれば、再生されます。もし、連続で延々と再生したければ、第二引数をtrueにします。

main.js
	mml.play(text, false);

楽譜の準備

本当は、ドラクエのオープニングをMMLにしたのですが、著作権的にNGなので、フリーの「さくらさくら」を楽譜にします。

階名でいくと以下になります。

ララシ ララシ ラシドシラシラファ ミドミファミミドシ ラシドシラシラファ ミドミファミミドシ ララシ ララシ ミファシラファミ

オクターブやテンポがありませんが、ちゃんとMMLの楽譜に反映が必要です。それが面倒なのですが、結果として以下のようになります。

t120l4o5 a a b r a a b r a b > c < b a b8 a8 f r e c e f e e8 c8 < b r > a b > c < b a b8 a8 f r e c e f e e8 c8 < b r > a a b r a a b r e f b8 a8 f e2

さきほどのJavascriptでは以下のようにすればよいです。

main.js
var text = "t120l4o5 a a b r  a a b r  a b > c < b a b8 a8 f r  e c e f e e8 c8 < b r > a b > c < b a b8 a8 f r  e c e f e e8 c8 < b r  > a a b r  a a b r  e f b8 a8 f e2";

先頭には、テンポやデフォルトの音の長さ最初のオクターブを指定しています。
aがラで、cがドで、ソがgですね。

ファ
c d e f g a b

詳細は以下を参照してください。

内部実装

内部的には、以下を使わせていただき、C言語で実装しています。

FreeRTOSのタイマーを使っています。こんな感じの関数を用意します。

module_mml.cpp
static void mmlTimerCallback(TimerHandle_t xTimer) {
  if( timerRunning ){
    if (mml.isBGMPlay()) {
      if (mml.available()) 
        mml.playTick();
    }else{
      xTimerStop(mmlTimer, 0);
      timerRunning = false;
      if( g_repeat ){
        mml.playBGM();
        startIntervalTimer(g_period);
      }
//      Serial.println("startIntervalTimer end");
    }
  }
}

static void stopIntervalTimer(void){
  if( timerRunning ){
    g_repeat = false;
    mml.stop();
    xTimerStop(mmlTimer, 0);
    timerRunning = false;
    if( gp_mml_text != NULL ){
      free(gp_mml_text);
      gp_mml_text = NULL;
    }
  }
}

static void startIntervalTimer(uint32_t period){
  if( timerRunning ){
    xTimerStop(mmlTimer, 0);
    timerRunning = false;
  }

//  Serial.println("startIntervalTimer start");
  xTimerChangePeriod(mmlTimer, pdMS_TO_TICKS(period), 0);
  xTimerStart(mmlTimer, 0);
  timerRunning = true;
}

long initialize_mml(void)
{
  mml.init(nullptr, func_tone, func_notone, nullptr /* debug */);
  mmlTimer = xTimerCreate(
    "mmlTimer",
    pdMS_TO_TICKS(g_period),
    pdTRUE,
    nullptr,
    mmlTimerCallback
  );

  return 0;
}

あとはこうやって再生を開始しています。

module_mml.cpp
  mml.setText(gp_mml_text);
  mml.playBGM();

  startIntervalTimer(g_period);

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

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

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

サポートサイト

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

以上

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?