Arduino+YMZ294を2発でステレオサウンド

こーいう音が出ます。魔界塔士SaGaのタイトル画面の曲(の冒頭2小節の繰り返し)

回路図とブレッドボードの配置はこんな感じ
ymz294x2_breadboardymz294x2_circuit
一見ややこしそうですが、YMZ294の1,2番(WRとCS)以外は全部、そのまま平行してつなげちゃえば良いということです。

プロトシールドもどき上だとこんなふうになります。
YMZ294x2

ワイヤ作りにはちょっと前に買った新兵器が役に立ちました。

ワイヤーストリッパーにはBreadBoardManiacの、ワイヤーストリッピングゲージをそうび。
wsg
オフィシャル以外のショップでは、秋月電子共立エレショップで売ってます。
(大須、第二アメ横のクニ産業でも売ってるのを確認しました)

ブレッドボード用のジャンパワイヤは単芯のものを使います。AWG26(0.4mm)かAWG28(0.3mm)くらいのワイヤを使ってますが、太すぎるとブレッドボードに刺さりにくいし、細すぎると抜けやすいので、悩みどころ。内経の割に外径が細いやつを選ぶと良いです。
どこかのブレッドボードを買った時に付いてきたジャンパワイヤがすごい使いやすいやつだったんですが、どのワイヤを買えば、あの使いやすさに近づけるのか……
jumper_wire
左から、外径が細くて使いやすいやつ、サンハヤト製、自作ワイヤ(細)、自作ワイヤ(太)。結構使いやすさが違います。

こちらはマザーボードに刺すようなコネクタを作るための工具。
connecters
左から、マザーボード用のコネクタ(メス・既成品)、今回Arduino-YMZ294間に使ったケーブル(オス-オス)、前に作ったハンダ付けで作ったケーブル(オス)。
……いかに私が圧着工具を必要としているかがよく分かるかと思います。端子を一個一個圧着するのも、それなりにコツがいるみたいですけどね。
これはコンタクトピン(オス)や、コンタクトピン(メス)でワイヤを圧着して、QIコネクタに差し込んで使います。
デジットBlog:【Tips】電子工作でよく使う『QIコネクタ』の圧着方法が詳しいです。

リボンケーブル(フラットケーブル)とヘッダソケットを用意して、100円ショップのベンチバイス(万力)でも簡単に作れるのはいいんですが
flatcable_connecter
リボンケーブルを挟み込む都合上、2列のケーブルしか作れないんですね。そこがちょっと不便。

ケーブルトークが長くなってしまいましたが、今回のスケッチ。
例によって、A tiny MML parserと、MetroライブラリYMZ294ライブラリを使います。

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

// YMZ294 defines
const byte WRCS_PIN = 8;
const byte A0_PIN = 9;
const byte WRCS2_PIN = 10;
const byte RESET_PIN = 11;
static YMZ294 ymz[2] = {
  YMZ294(WRCS_PIN, A0_PIN, RESET_PIN), 
  YMZ294(WRCS2_PIN, A0_PIN, RESET_PIN)
};

Metro psgMetro[2] = {Metro(), Metro()};

// mml defines
MML mml[2];
MML_OPTION mmlopt;

char *text[2] = {
  "o5 l16 e8e8b2 b-b>d-8<b1 >e8e8b2b-b>d-8<b1",
  "o6 l16 b1< e8e8b2b-b>d-8<b1 >e8e8b2b-b>d-8"
};

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

void snd(int num, int ms, YMZ294* y)
{
  if (num == 0)
    y->SetFreqBit(CH_A, 0);
  else{
    y->SetEnvShape(1,1,0,1);
    y->SetEnvFrequency(200);
    y->SetFrequency(CH_A, noteFreq[num]);
  }
  if (y == &(ymz[0]))
    psgMetro[0].interval(ms);
  else if (y == &(ymz[1]))
    psgMetro[1].interval(ms);
}

void setup() {
  DDRD = 0b11111111;
  pinMode(WRCS_PIN, OUTPUT);
  pinMode(WRCS2_PIN, OUTPUT);
  pinMode(A0_PIN, OUTPUT);
  pinMode(RESET_PIN, OUTPUT);
  
  MML_OPTION_INITIALIZER(&mmlopt, 5, 8, 600);
  for (int i = 0; i < 2; i++){
    ymz[i].SetMixer(0b111, 0b000);
    ymz[i].SetEnvEnable(CH_A, true);

    mml_init(&mml[i], mml_callback, &(ymz[i]));
    snd(0, 0, &(ymz[i]));
    mml_setup(&mml[i], &mmlopt, text[i]);
  }
}

void loop() {
  for (int i = 0; i < 2; i++){
    if (psgMetro[i].check() == 1){
      if(mml_fetch(&mml[i]) != MML_RESULT_OK){
        snd(0, 0, &(ymz[i]));
        mml_setup(&mml[i], &mmlopt, text[i]);
      }
    }
  }
}


static YMZ294 ymz[2]とYMZ294クラスを2個作成。
同様にMML mml[2]char *text[2]Metro psgMetro[2]も2個ずつ作成します。

mml_init()関数の引数としてYMZ294クラスのポインタを渡してるので、今までのスケッチよりちょっとむずかしいかも。
私も人に説明する自信がないので解説はしませんが、ポインタはC/C++でプログラミングするには絶対必要になってくることなので、頑張って理解して下さい。

snd()関数内での2基のYMZ294の判別方法が大変ダサい感じになってますが、まあ動くので良しということで。
あと、リセット用に1ピン使ってますが、これぶっちゃけ、無くてもいいです。次にYMZ294用のライブラリ作る時には、Arduino上のRESETピン(3.3Vの隣)につなげることにします。

digitalWriteでリセットを使わなくなるため、YMZ294は5基まで接続可能になりますが、Metroはそこまでたくさん同時に扱えないでしょうね……

2 thoughts on “Arduino+YMZ294を2発でステレオサウンド”

コメントを残す