2024/06/03 電子書籍「電子書籍出版・技術文書作成を劇的に加速!秀丸エディタ + Markdown + Pandocの驚異」を出版

M5Unifiedを使用したスピーカー制御

M5Stack

M5Unifiedハンズオンのスライド 及び スピーカーのサンプルコード1を基に、スピカーのtone関数の使い方を説明します。

M5.Speaker.tone 関数

beep音を鳴らす関数です。

M5.Speaker.tone 関数の引数

  • 引数1 : 音の周波数
  • 引数2 : 音の長さ [ミリ秒]
  • 引数3 : 使用する仮想チャンネル番号 (0~7=チャンネル番号 / -1=自動選択)
  • 引数4 : 今鳴っている音を止めるか否か (true=止める/false=待機する)

M5Unifiedの Speaker 機能の特徴

  • 別タスクで動作し、メインタスクの処理を停止させずに鳴らすことが可能。
  • 8個の仮想チャンネルという仕組みがあり、最大8音を同時に出力できる。
  • 各チャンネルには待機枠があり、再生中に次の指示をひとつ溜めておける。
  • 単純な音の tone 関数の他に、波形データを再生する playRaw を同時使用できる。
  • サンプリングレートの異なる複数の波形データを同時に再生できる。

M5.Speakerのしくみ

M5Unifiedの Speaker は、ソフトウェアで波形の変換や合成を行っています。

例えば I2S(オーディオを扱うペリフェラル) への実際の出力レートが 48kHz の場合、
24kHzの音源データをそのまま渡すと、通常なら倍速再生になってしまいます。

M5.Speaker は出力先と入力データのレートの相違を変換する仕組みがあるため、
I2Sへの出力レートを意識せずとも、音源データのレートさえ分かれば再生が可能です。レートの変換の他に、ボリューム変換・ステレオ⇔モノラル変換・複数チャンネル合成といった、
複数の異なる音源を同時に再生するための工夫が盛り込まれています。

M5Unifiedの Speaker はバックグラウンド再生ができます。
また、再生待ちキューという仕組みが各チャンネルにひとつ用意されています。

これを利用すると音の再生中に、処理待ちせずに次の音データを指示できます。
例えば3回連続で同じチャンネルに再生指示を出した場合、以下のようになります。

・1回目の再生指示 : 即座に再生される。処理待ちは起こらない。
・2回目の再生指示 : 処理待ちキューに積まれ、処理待ちは起こらない。
・3回目の再生指示 : 処理待ちキューが空くまで、待機が発生する。

サンプル

step1():ド・ミ・ソ を同時に鳴らす

void step1()
{
  /// ド・ミ・ソ を同時に鳴らす。
  M5.Speaker.tone(523.251, 400);  // ドの音
  M5.Speaker.tone(659.255, 400);  // ミの音
  M5.Speaker.tone(783.991, 400);  // ソの音

  /// このように連続で呼び出した場合は同時に再生され、和音になる。

  /// 厳密には、最大8音まで重ねる事ができる「仮想チャンネル」という概念があり
  /// 引数3 にて0~7を指定し、8個のうち どのチャンネルを使用するかを設定できるようになっている。
  /// 引数3 を省略または -1 を指定した場合、空いている仮想チャンネルが自動的に割り当てられる。
  /// この例では 引数3を省略しているため、3つのtone関数は別の仮想チャンネルが割当てられている。
}

step2():ド・ミ・ソ を順に鳴らす。

void step2()
{
  /// ド・ミ・ソ を順に鳴らす。
  M5.Speaker.tone(523.251, 400, 0, true);  // ドの音
  M5.Speaker.tone(659.255, 400, 0, false);  // ミの音
  M5.Speaker.tone(783.991, 400, 0, false);  // ソの音

  /// 引数3 : 仮想チャンネル番号
  /// 引数4 : true=再生中の音を止めて即座に再生を開始する / false=鳴り終わるのを待機する / 省略時はtrue。

  /// 前の例と比べて引数3と4の指定が追加されている。
  /// 仮想チャンネル0番を使用し、前の音が鳴り終わるのを待つ設定となっている。
  /// このため、この3つのtone関数の音は重なったり中断されたりせずに、順番に鳴る。

  /// 各仮想チャンネルには再生を待機できるキューがひとつだけ用意されている。
  /// この例では以下のような状況が発生する。
  /// 1つ目のtone : 即座に再生され、キューには積まれない。
  /// 2つ目のtone : 前のtoneが再生中なので、再生待ちキューに積まれる。
  /// 3つ目のtone : 再生待ちキューが使用中なので、キューが空くまで待機する。
  /// このような状況になるため、この例では実行時間が約400msecほどかかることになる。
}

step3():ド・ミ・ソ を重ねて順に鳴らす

void step3()
{
  /// ド・ミ・ソ を重ねて順に鳴らす
  M5.Speaker.tone(523.251, 1000, 0);  // ドの音
  delay(200);
  M5.Speaker.tone(659.255, 1000, 1);  // ミの音
  delay(200);
  M5.Speaker.tone(783.991, 1000, 2);  // ソの音

  /// 音を重ねつつ、タイミングをずらして順番に鳴らしたい場合は、待機処理を追加する。
  /// この例では 200ミリ秒ずつタイミングをずらして和音が鳴る。
}

step4():ド・ミ・ソ を順に、前の音を止めて鳴らす

void step4()
{
/// ド・ミ・ソ を順に、前の音を止めて鳴らす
M5.Speaker.tone(523.251, 1000, 0); // ドの音
delay(200);
M5.Speaker.tone(659.255, 1000, 0); // ミの音
delay(200);
M5.Speaker.tone(783.991, 1000, 0); // ソの音

/// step3では各tone関数が異なる仮想チャンネルを指定していたのに対し、
/// この例は全て同じ仮想チャンネルを指定している。
/// 前の音が鳴り終わる前に次のtone関数が実行されるため、
/// 再生中の音を停止して次の音が鳴り始める。
}

step5():トーンデータ (8bit unsigned の波形ひとつ分の生データ)

void step5()
{
  /// トーンデータ (8bit unsigned の波形ひとつ分の生データ)
  static constexpr const uint8_t wav[64] = { 132,138,143,154,151,139,138,140,144,147,147,147,151,159,184,194,203,222,228,227,210,202,197,181,172,169,177,178,172,151,141,131,107,96,87,77,73,66,42,28,17,10,15,25,55,68,76,82,80,74,61,66,79,107,109,103,81,73,86,94,99,112,121,129 };

  M5.Speaker.tone(523.251, 1000, 1, true, wav, sizeof(wav));
  delay(200);
  M5.Speaker.tone(659.255, 1000, 2, true, wav, sizeof(wav));
  delay(200);
  M5.Speaker.tone(783.991, 1000, 3, true, wav, sizeof(wav));

  /// 波形データを用意すれば、音色を変更することができる。
  /// 引数5 : 波形データのポインタ
  /// 引数6 : 波形データの配列要素数
  /// 引数7 : 波形データがステレオか否か (true=ステレオ / false=モノラル) 省略時=false
}

step6():ピポッ音

void step6()
{
  /// ピポッ
  M5.Speaker.tone(2000, 100, 0, true);
  M5.Speaker.tone(1000, 100, 0, false);
}

step7():音階

void step7()
{
  /// 音階
  for (int i = 0; i < 60; ++i)
  {
    float Hz = 220 * powf(2.0, i / 12.0f);
    M5.Speaker.tone(Hz, 50, 0, false);
  }
}

Speakerクラス

ヘッダファイルは、”utility\Speaker_Class.hpp”となります。

以下、関数一覧です。(2023/10/24)

  • void begin();
  • void end();
  • bool isEnabled(void);
  • bool isPlaying(void);
  • size_t isPlaying(uint8_t channel);
  • size_t getPlayingChannels(void);
  • void setVolume(uint8_t master_volume);
  • uint8_t getVolume(void);
  • void setAllChannelVolume(uint8_t volume)
  • void setChannelVolume(uint8_t channel, uint8_t volume);
  • uint8_t getChannelVolume(uint8_t channel);
  • void stop(void);
  • void stop(uint8_t channel);
  • bool tone(float frequency, uint32_t duration, int channel, bool stop_current_sound, const uint8_t* raw_data, size_t array_len, bool stereo = false);
  • bool tone(float frequency, uint32_t duration = UINT32_MAX, int channel = -1, bool stop_current_sound = true);
  • bool playRaw(const int8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);
  • bool playRAW(const int8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);
  • bool playRaw(const uint8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);
  • bool playRAW(const uint8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);
  • bool playRaw(const int16_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);
  • bool playRAW(const int16_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);
  • bool playWav(const uint8_t* wav_data, size_t data_len = ~0u, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);

参考URL

以下のURLも参照ください。

タイトルとURLをコピーしました