LPC810とArduino UNOでSCC互換音源を鳴らす(2)

さて、続き

1バイトずつ直で書き込んでいくのも面倒なので、例によってArduino用のライブラリを作りました。
SCC810.zip
↑まずこのファイルを解凍して、Arduinoのライブラリのフォルダ(C:\Users\ユーザ名\Documents\Arduino\libraries とか)に突っ込んどいて下さい。

Arduino IDEのメニューから、スケッチ→Include Library→SCC810 すると

#include <Frequency.h>
#include <LPCSound.h>
#include <PSG810.h>
#include <PSG810_defs.h>
#include <SCC810.h>
#include <SCC810_defs.h>

こんな感じでどどっとヘッダファイルが増えますが、この中で実際に必要なのは、PSG810.hSCC810.hだけです。
あとLPC810と通信するためのWire.hが要ります。

PSGに関しては前に書いたYMZ294 Arduino Libraryと似てますが、

  • SoundCortex自体がエンベロープに対応していない
  • ノイズの使い方が難しい

ということで、その2点の機能は削ってあります。矩形波3チャンネルだけです。

回路は前回と同じ。LPC810のpin2のSound Out(SO)とスピーカーの間にはてきとーに抵抗挟むなり、アンプ回路を通すなりすると良いでしょう。
SCC810_回路図

PSGでドレミファソラシドを鳴らすシンプルなスケッチを書いてみました。

#include <Wire.h>
#include <PSG810.h>
#include <SCC810.h>

PSG810* psg;

void setup() {
  psg = new PSG810();
  psg->SetMixer(0x110);
  psg->SetVolume(PSG::PSG_A, 0x0f);
}

void loop() {
  sound(5, 'C');
  sound(5, 'D');
  sound(5, 'E');
  sound(5, 'F');
  sound(5, 'G');
  sound(5, 'A');
  sound(5, 'B');
  sound(6, 'C');
}

void sound(byte octave, char note){
  psg->SetFrequency(PSG::PSG_A, CalcFreqByMML(octave, note));
  delay(200);
}

PSG部分は上記の通り簡単に鳴るんですが、SCC部分はもうちょっと面倒です。
PSGの波形は「矩形波」(四角い波)と言うだけあって、音量の最大値と最小値の繰り返しによる四角い波形です。オンとオフを繰り返しているだけなので、波形を意識する必要がありません。
一方、SCCは「波形メモリ音源」といい、矩形の代わりに「波形テーブル」に登録された32バイトの音量リストを繰り返し再生します。ものすごい周期の短いサンプラーみたいな感じ。
使い方次第でパルス波や三角波、サイン波に似た音も出せます。

波形メモリ音源はコナミのSCC/SCC+以外にも、ファミコンのディスクシステム(1音)、ゲームボーイ(1音)、PCエンジン(4~6音)などに使われています。
アーケードゲームだとコナミのバブルシステム(グラディウスやツインビー)、俗に言うところのナムコWSG音源などなど。
ゲーム音楽の歴史は、ビープ音→矩形波(ゲームウォッチやMSXなど)→カスタムパルス波+三角波(ファミコンなど)→波形メモリ音源(PCエンジンなど)→FM音源(メガドライブなど)→PCM音源(ネオジオやスーファミなど)→MIDIやCD-DA……と進化していくのですが、この辺書き始めると長いのでまた今度書くことにします。

PSG(矩形波)とSCC(波形メモリ音源)の波形の一例をOpen Officeでグラフ化してみました。
PSG&SCCグラフ

この赤で描かれた波形、16進数で書くと
F0604010406020F0407050002030F0A0005010C0D0F0A080B000D090B0E0B090
となる32バイトをSoundCortexの波形テーブルに書き込んでやらないといけません。

波形テーブルに書き込む処理を加えて、SCCでドレミファソラシドを鳴らすスケッチです。

#include <Wire.h>
#include <PSG810.h>
#include <SCC810.h>

SCC810* scc;

void setup() {
  scc = new SCC810();
  scc->SetMixer(0x00001);
  scc->SetVolume(SCC::SCC_A, 0x0f);
  char wave[] = "F0604010406020F0407050002030F0A0005010C0D0F0A080B000D090B0E0B090";
  byte wt[SCC::WT_SIZE];
  strtowt(wave, sizeof(wave), wt);
  scc->SetWaveTable(SCC::SCC_A, wt, sizeof(wt));
}

void loop() {
  sound(5, 'C');
  sound(5, 'D');
  sound(5, 'E');
  sound(5, 'F');
  sound(5, 'G');
  sound(5, 'A');
  sound(5, 'B');
  sound(6, 'C');
}

void sound(byte octave, char note){
  scc->SetFrequency(SCC::SCC_A, CalcFreqByMML(octave, note));
  delay(200);
}

void strtowt(char* str, int str_size, byte* wt)
{
   for (int i = 0; i < str_size; i+=2){
     byte b = (ctob(str[i]) * 0x10) + (ctob(str[i+1]));
     wt[i/2] = b;
   }
}
byte ctob(char c)
{
  byte ret = 0;
  if (c >= '0' && c <= '9')
    ret = c - '0';
  else if(c >= 'a' && c <= 'f')
    ret = c - 'a' + 10;
  else if (c >= 'A' && c <= 'F')
    ret = c - 'A' + 10;
  return ret;
}

やっぱりSCCは音がキラキラしてますね。
波形テーブルの中身を変えると、音色もかなり違うものになります。色々とwaveの中身を変えて、試してみてください。

※私のライブラリなのかWire.hなのか問題がどのレベルにあるのかよく分かりませんが、I2Cでwriteに行ってそのまま固まることがよくあります。書き込む量が多いので、PSGよりSCCの方が固まりやすいようです。

※追記(2016/10/05)
電圧レベル変換モジュールでArduino UNOの5VとLPC810の3.3Vを変換したら安定しました。
今回使用したのはFXMA2102です。
秋月のAE-FXMA2102にはプルアップ用の抵抗が搭載されていないため、LPC810側はSCK,SDAともに抵抗器越しに3.3Vにつなぐ必要があります。
最初、横着してプルアップなしで試したんですが、動作しませんでした。(Arduino UNOの5V側はプルアップの必要はないようです)

プルアップ抵抗には10kΩを使いました。回路はこんな感じ。
LPCSCC+Arduino UNO_回路図 LPCSCC+Arduino UNO_ブレッドボード

PCA9306でも動作確認しました。
こちらの場合は基板上にプルアップ抵抗があるので、別で抵抗器を用意する必要がありません。PCA9306使った方が楽です。ほんのり安いしね。


「LPC810とArduino UNOでSCC互換音源を鳴らす(2)」への4件のフィードバック

  1. 質問させてください。

    LPC810のSCC音源で和音を鳴らそうと思っています。
    ブログの通りにプログラムを実行してもB以降の音が鳴りません。

    何か心当たりがありましたらアドバイスお願いします

    1. まず、「B以降の音」というのは音程のBではなく、SCCのBチャンネル以降という事で良いですか?

      その前提で話を進めると、この3ヶ所を書換え・追加する必要があります
      ・scc->SetMixer(0x00011);
       ↑1-5bit目を1にすることでA~EチャンネルがONになる。この場合はA,BチャンネルがONになる
      ・scc->SetVolume(SCC::SCC_B, 0x0f);
       ↑Bチャンネルのボリューム設定
      ・scc->SetWaveTable(SCC::SCC_B, wt, sizeof(wt));
       ↑Bチャンネルの波形を設定
      この3ヶ所を設定していないと、SetFrequencyしても音が鳴りません。

      とりあえず、ありそうなのはその3ヶ所でしょうか。確認してみて下さい。

      1. 先ほどコメントしたはずでしたが、反映されていないようなので再送します

        問題は解決しました。
        scc->SetMixer(0x00011);
        ここ16進数で0x0011を代入すると2進数では10001となってしまいch_Aのみしかonになりません。

        そこで
        scc->SetMixer(0x00f);
        として2進数で1111となるよう16進数を設定したら正常に和音が出ました。

        ありがとうございました

        1. あ、素でボケてました。16進じゃなくて2進表記ですね。
          0x00011→B00011
          実機で確かめないとダメですねえ。

コメントを残す