RX72N Envision Kit で名機DX7を鳴らす

はじめに

ネットでDX7のエミュレーターを見つけたので、RX72N Envision Kit にポートしてみた。

DX7は、1983年5月に発売されたヤマハのシンセサイザーで、まだ、メモリの容量や価格が制限されていた時代に登場した画期的な名機で、FM 音源独特の、音を始めて聴いた時の感じを今でも覚えている。
現在は、サンプリングが主流となり、シンセサイザーの形態も様変わりしたが、今でも、その音色は色あせる事は無いと思う。

DX7は、6オペレーター、32アルゴリズムであるが、後年、それをスケールダウン(4オペレーター)した YM2151 が業務用ゲーム機やパソコンに採用された。

ただ、FM 音源方式では、自分が思った音色を作るのが、難しい難点があった。
※2151の時代でも、サウンドコンポーザが、音色を作るのに難儀していたのを覚えている。
※アタリのゲームで、口笛のような音を表現していて、ビックリした事があるw

ソースを眺める

まず、ソースコードを取って来て中身を見てみる。

どうやら、エンジン部は C++ のようで、Android で鳴らすのがメインのようだ。
※ARM/NEON 用に最適化されたコードも含まれているようだ。

いきなりターゲットで実験するのは大変そうなので、自分のフレームワーク glfw3_app で実験コードを作って実験してみる。

  • エンジンのソースで必要そうな物をコンパイル、リンクする。
  • 一応 C++ だけど、かなり痛い部分があるようだー、まぁこれは、後々修正するとして、とりあえず鳴らしてみないと・・
  • ※名前空間も定義されていないし、C++ 的に見ると、ツッコミ処が多いが、FM 音源のエンジンは優秀なのだろうと思う。
  • 自分は、警告をかなり厳しくしているので、駄目な部分は、引っかかって止まり、修正を繰り返す。

そんなこんなで、コンパイルが通り、リンクするまで来た。

早速、インスタンスを宣言して動かすのだが、勝手が判らないので、OS-X 用のソース(main.mm)があったので参考にした。
色々、しらべた結果、以下のようにする事が判った。(非常にシンプルなエンジンだ!)

使い方

        RingBuffer      ring_buffer_;
        SynthUnit       synth_unit_(&ring_buffer_);
  • 二つのインスタンスを定義。
    -「RingBuffer」は、MIDI データを食わせるクラスで、ノートのOn/Offなどほぼ全ての機能は、それで完結するようだ。
    -「SynthUnit」が本体のエンジンとなっている。
  • 初期化時、「RingBuffer」のポインターを食わせる。(ポインターなのが、駄目駄目w)
    SynthUnit::Init(44100);
  • 初期化を行う。
  • エンジンで運用するサンプリング周波数を設定する。
    const uint32_t n = 44100 / 60;
    int16_t wave[n];
    synth_unit_.GetSample(wave, n);
  • glfw3_app の「updata」ループ(60Hz)で、一定時間間隔でサービスする。
  • GetSample を呼び出す事で、エンジンで生成した波形(モノラル)を取得する。
  • この波形データを、システムの発音ジェネレータにキューイングする。

たったこれだけ・・・

鳴らしてみる

とりあえず、音を鳴らしてみたいが、どうするのか判らないのでエンジンのソースを見てみる。

どうやら、MIDI のノートを送れば、発音するようだ。

そこで、以下のコードを用意して、キーボードを押して鳴らしてみた。

            for(int i = 0; i < 13; ++i) {
                if(key_[i] && !key_back_[i]) {
                    uint8_t key[3] = { 0x90, 0x3C, 0x7F };
                    key[1] = 0x3C + i;
                    ring_buffer_.Write(key, 3);
                }
                if(!key_[i] && key_back_[i]) {
                    uint8_t key[3] = { 0x80, 0x3C, 0x7F };
                    key[1] = 0x3C + i;
                    ring_buffer_.Write(key, 3);
                }
            }

key_[] には、キーボードを押した状態が、「true」、「false」で入っている。

鳴った!、思った通りの音だ!

和音の響きも良い!


実験用アプリ:

※このアプリでは、MIDI キーボードを繋いで、キーボードから鳴らす事が出来るようにしている。

音色の変更(program change)

DX7 には、標準で32くらいはプリセットがあったと思い、音色を変更してみた。

            if(dev.get_positive(gl::device::key::_1)) {
                uint8_t tmp[2];
                tmp[0] = 0xc0;
                tmp[1] = 1;
                ring_buffer_.Write(tmp, sizeof(tmp));
            }

「1」のキーを押すと、1の音色

しかし、何も鳴らない・・・

調べると、初期の状態では、0番にしか、音色が設定されていなかった。

ネットを探すと、DX7 用の音色ファイルが沢山ある事が判った。
そこで、「DX7_0628.SYX」なるファイルを見つけて、ダウンロードした。

この音色、どうやって組み込むのか?

エンジンコアのソースを読むと、単に MIDI データとして渡す事が判った。

        utils::file_io fin;
        if(fin.open(file, "rb")) {
            uint8_t tmp[4096 + 8];
            if(fin.read(tmp, sizeof(tmp)) == sizeof(tmp)) {
                ring_buffer_.Write(tmp, sizeof(tmp));
            }
        }
  • 1音色128バイトで、32音色が1セットになっている。
  • 8バイトはヘッダーとサム(2バイト)で、サムは、検証していないようだ。

これで、音色を変えて発音できる事が判ったー


RX72N にポートしてみる。

RX マイコン用にプロジェクトを作成して、ソースを持ってきて、コンパイルしてみた。

  • RX マイコン用に、少し修正。
  • 修正は、そんなに多くは無いが、やはり鬼門の「define」・・・
  • #define N (1 << LG_N) 「N」は駄目!、当たる・・・

修正して、コンパイルし、エラーが無くリンクが通ったー

早速、鳴らしてみる。

普通に鳴る~

ターミナルを接続して、'z' から ','、's' から 'j' を押すと、1秒間発音する。

改造

  • RX72N では鳴るが、RX65N だと、メモリが厳しい・・
  • 少し調べると、sawtooth.cpp で、メモリを多く消費しているようだ。
  • とりあえず、「#define LG_N_SAMPLES 10」と分解能として10ビットになっており、その配列がある。
  • 上記パラメーターは二次元配列になっている。
#define N_SLICES 36

int32_t sawtooth[N_SLICES][N_SAMPLES];

となっているので、10ー>9にしてみた、聴いた感じ、音の劣化を感じなかったので、とりあえず、それで・・

処理負荷

  • 一応、120MHz の RX65N Envision Kit でも動作するのだが、120MHz では厳しいようだ。
  • 同時発音数が増えると、簡単に処理オーバーする。
  • かと言って、RX72N の 240MHz でも余裕とは言えない。
  • DSP 命令とかを駆使すれば、波形の合成処理を最適化出来るかもしれない。
  • この感じだと、ゲームやアプリに組み込んで、効果音や BGM として鳴らすには厳しいかもしれない。
  • 一応、RX65N の場合、発音数は8(通常16)にしてある。

※現状のコードはプッシュ済み

スタンダード MIDI ファイルをパース

「鳴る」事は判ったので、何か演奏させてみようと思い、スタンダード MIDI ファイルのパースをしてみる。

簡単だと思ったら、結構面倒そうで、今回はここまで・・・

※自分で作らなくても、MIDI シーケンサくらいは、色々ありそう、ちょっと探してみるか・・・

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください