ArduinoからYMZ294のエンベロープを操作

こんなのです。

YouTubeにも上げてあります。

回路図はこんな感じ
ymz294_with_env_sw_回路図ymz294_with_env_sw_ブレッドボード
デジタル用のピンがカツカツ(D0~D10までYMZ294につないでるので、残り3本)なので、A0~A3をデジタル入力としてディップスイッチのINPUTに使ってます。
A5はエンベロープの周波数入力に使用。

スケッチは以下の通り

#include <mml.h>
#include <YMZ294.h>

// YMZ294 defines
const byte WRCS_PIN = 8;
const byte A0_PIN = 9;
const byte RESET_PIN = 10;
YMZ294 ymz(WRCS_PIN, A0_PIN, RESET_PIN);

// Envelope Pins
const byte ENV_SHAPE_PINS[] = {14,15,16,17};
const byte ENV_FREQ_PIN = 5;

// mml defines
MML mml;
MML_OPTION mmlopt;
char *text = "o5 l8 def+ae2 >dc+<bf+a2 b>c+d<a4d4. gf+d<a2r b4>c+4d4g4 f+gaf+ef+ge <b>gf+<a>e4c+4 d4<a>ed2";

static void mml_callback(MML_INFO *p, void *extobj)
{
  switch (p->type) {
    case MML_TYPE_NOTE:
      {
      MML_ARGS_NOTE *args = &(p->args.note);
      snd(args->number, args->ticks);
      }
      break;
    case MML_TYPE_REST:
      {
      MML_ARGS_REST *args = &(p->args.rest);
      delay(args->ticks);
      }
      break;
  }
}

void snd(int num, int ms)
{
  set_env();
  ymz.SetFrequency(CH_A, noteFreq[num]);
  delay(ms);
}

void set_env()
{
  ymz.SetEnvShape(!digitalRead(ENV_SHAPE_PINS[3]),
                  !digitalRead(ENV_SHAPE_PINS[2]),
                  !digitalRead(ENV_SHAPE_PINS[1]),
                  !digitalRead(ENV_SHAPE_PINS[0]));
  ymz.SetEnvFrequency(analogRead(ENV_FREQ_PIN) >> 3);
}

void setup()
{
  DDRD = 0b11111111;
  pinMode(WRCS_PIN, OUTPUT);
  pinMode(A0_PIN, OUTPUT);
  pinMode(RESET_PIN, OUTPUT);
  pinMode(ENV_SHAPE_PINS[0], INPUT_PULLUP);
  pinMode(ENV_SHAPE_PINS[1], INPUT_PULLUP);
  pinMode(ENV_SHAPE_PINS[2], INPUT_PULLUP);
  pinMode(ENV_SHAPE_PINS[3], INPUT_PULLUP);
  
  ymz.Reset();
  ymz.SetMixer(0b111, 0b110);
  ymz.SetFreqBit(CH_A, 0);
  ymz.SetEnvEnable(CH_A, true);

  mml_init(&mml, mml_callback, 0);
  MML_OPTION_INITIALIZER(&mmlopt, 4, 8, 720);
}

void loop()
{
  mml_setup(&mml, &mmlopt, text);
  while (mml_fetch(&mml) == MML_RESULT_OK);
}

以前の記事で、Arduinoのtone()関数で音を鳴らすために使用したA tiny MML parserを、今度はYMZ294用に使用させて頂きました。
こちらの記事では、Arduino用にライブラリをアップデートして頂けたとの事。ありがたいです。
CuBeatSystems: Arduinoでも扱いやすくするために’A tiny MML parser’をアップデートしました (ついでにスーパーマリオも演奏してみました)
今回のスケッチでは短い曲(エイトメロディーズ)の再生しかしていないのでPROGMEMを使用していませんが、その内にお世話になると思います。

さて、今回のスケッチの話。
includeしてるライブラリは、↑のA tiny MML parserと、YMZ294ライブラリです。
両方ないと動きません。

Arduinoのtone()関数では当然普通の矩形波しか鳴りませんし、音量も変えられない訳ですが、YMZ294では音量を直接指定もできるし、「エンベロープ」という機能を使うことも出来ます。
PSGやFM音源でエンベロープというのは、自動で音量を変化させることです。
YMZ294では10種類(重複があるので、実質8種類)のエンベロープが使用できます。
manual_env
使用できるんですが、このデータシート見ただけだと実際にどんな風に変化するのかさっぱり分からないので、実際に使ってみて、音を聴いて理解することにしたのが今回のスケッチです。

A0~A3のディップスイッチ4つはそのままデータシートにあるCONT,ATT,ALT,HOLDのON/OFFに対応。
A5の半固定抵抗は一回分のエンベロープ周期の早さに対応しています。
エンベロープ周期はYMZ294の$0B,$0Cの2バイトで制御するので、本来は0~65535までの数値を指定できるんです。
が、そもそもArduinoのアナログ入力で取れる値は0~1023ですし、周期が早過ぎると音の違いがよく分からなくなってしまうので、このスケッチでは耳で違いが分かりやすい程度の範囲、0~127に幅を狭めています。

結果分かったことのまとめ
CONT:他で同じパターンがあるので1固定で良い
ATT(Attack):0…音量最大から始まる 1…音量最小から始まる
ALT(Alternate):0…パターン終了時にATTの音量に戻る 1…戻らずにパターン終了時の音量を維持する
HOLD(Hold):0…同じパターンを繰り返す 1…1回で終わり

1001(徐々に小さくなる)は使い勝手が良い
1000,1100(ノコギリ波)と1010,1110(三角波)は使い方次第でおもしろい音になる
1101(立ち上がりの遅い音)と1111(立ち上がりが遅く、最大音量になると止まる)も何か使い道ありそう
1011(最大音量から小さくなり、消えた後で最大音量で鳴り続ける)の使い道が分からない

MOTHERの音楽は超名曲なのてみんな聴くと良いと思う


3 thoughts on “ArduinoからYMZ294のエンベロープを操作”

  1. mml.hを用いないでエンベロープ(1001)を使いたい場合、どうすればいいでしょうか。

    1. この記事のスケッチでは同時に使っていますが、A tiny MML parserとYMZ294のエンベロープの機能には直接関係はありません。
      別記事で簡単なスケッチを用意しましたので、試してみてください。
      http://wp.hrmux.com/?p=3038

コメントを残す