RX72N Envision Kit での開発(その6)FMシンセサイザー 編


FM シンセサイザー用 GUI を作る

以前に、DX7 FM シンセサイザー用オープンソースをポーティングしました。

当初は、スタンダード MIDI ファイルの演奏を考えていましたが、パースするのが意外と大変そうなので、ストールしていました。

その後、MIDI 演奏は、とりあえず後回しにして、鍵盤を作り、音色を変更する GUI を実装しました。

当初、鍵盤の GUI も、widget_director クラスで管理できるものと思いましたが、鍵盤の場合、同時押しなど、通常の GUI とは異なるケアを行う必要があります、現在の widget クラスを改修するのは、かなり複雑になると考え、専用のクラスを実装しました。

また、タッチパネル(FT5206)サービスクラスでは、同時二点押しまでしかサービスしていませんでしたが、FT5206 の最大数4点までのサービスに修正しました。
※和音を出すのに3点以上が必要です。

DX7 の音色ファイルのロードと切り替え

FM シンセサイザーのソースには、音色は1つだけしか設定されていません。

実機は最大32色まで変更が可能なバンクがあります。

音色ファイルは、「DX7_0628.SYX」というもので、ネットで見つけました。
※たったの4Kバイト程度です。

このアプリでは、SD カードのルートに「DX7_0628.SYX」に置いておくと、起動して3秒くらいしてからロードします。
※SDカードのマウントに遅延がある為

使える音色は以下のものです:

 0: 'PIANO 1   '
 1: 'FM PIANO A'
 2: 'PIANO 1   '
 3: 'HARD ROADS'
 4: 'PIANOBELL2'
 5: 'T 23      '
 6: 'RHODES-CHO'
 7: 'PIPES    A'
 8: 'PIPES   2 '
 9: 'ROADSFLUTE'
10: 'ROADFLUTE2'
11: 'PLUCKEDRUM'
12: 'LO/HI STR2'
13: 'OBEHIND   '
14: 'ANLGBRASS '
15: 'FATSYNTH A'
16: 'JL PONTY 1'
17: 'Strings #2'
18: 'Orchestra '
19: 'PLUCKIN'  '
20: 'OOH AHH EE'
21: 'T 22      '
22: 'BassFlute2'
23: 'BassFlute3'
24: 'TIGHTPIANO'
25: 'BASS FLUTE'
26: 'PAUL STRGS'
27: 'BASS/PIA.1'
28: '2 OR MORE '
29: 'TIMPANI  2'
30: 'MALE CHOIR'
31: 'F.CHORUS 2'

シンセサイザークラスに音色をセットするのは、内部的には、MIDI データとして食わせるようです。
※ロードしたデータをそのままMIDIのストリームに食わせるだけです。

    bool read_synth_color_(const char* filename) noexcept
    {
        utils::file_io fin;
        if(fin.open(filename, "rb")) {
            uint8_t tmp[4096 + 8];
            if(fin.read(tmp, sizeof(tmp)) == sizeof(tmp)) {
                ring_buffer_.Write(tmp, sizeof(tmp));
                {  // データを処理させる為、エンジンを動かす。
                    const uint32_t len = SYNTH_SAMPLE_RATE / 60;
                    int16_t tmp[len];
                    synth_unit_.GetSamples(len, tmp);
                }
                for(int i = 0; i < 32; ++i) {
                    char tmp[12];
                    synth_unit_.get_patch_name(i, tmp, 12 - 1);
                    tmp[11] = 0;
                    utils::sformat(" %2d: %s", &synth_color_name_[i * 16], 16) % (i + 1) % tmp;
                }
            }
            return true;
        } else {
            return false;
        }
    }

マルチタッチと発音

RX72N Envision Kit のタッチパネルは、静電容量タイプで、4点までのマルチタッチが可能なタイプです。

音の強弱は難しいとしても、キーボード(ピアノ)のようなインターフェースには向いています。

    void service_note_() noexcept
    {
        for(int i = 0; i < 21; ++i) {
            const auto& key = synth_gui_.get_keyboard().get(static_cast<SYNTH_GUI::KEYBOARD::key>(i));
            if(key.positive_) {
                uint8_t tmp[3];
                tmp[0] = 0x90;
                tmp[1] = 0x3C + i;
                tmp[2] = 0x7f;
                ring_buffer_.Write(tmp, 3);
            }
            if(key.negative_) {
                uint8_t tmp[3];
                tmp[0] = 0x80;
                tmp[1] = 0x3C + i;
                tmp[2] = 0x7f;
                ring_buffer_.Write(tmp, 3);
            }
        }
    }

GUI キーボードで得た、押した状態、離した状態で、それぞれ、MIDI データを作成して、食わせると音が鳴ります。
押している間、音が連続して鳴ります。

また、マルチタッチでは「和音」が鳴らせる事が大きいです。

流石に、このサイズ(1.5オクターブ程)では、簡単な曲しか演奏できませんが、ガジェットとして楽しいものです。


GUI を作成する要点

widget_director クラスでは、現在は、widget の配置ツールなどは無く、プログラムコードによって widget の配置などを行う必要があります。

例えば、電卓のような物なら、グリッド状にボタンが配置されているので、配置ツールが無くても簡単です。


今回は、音色を変更する為、二つのスイッチと、音色名を表示するボックスを作成しました。

このような場合、座標は、比較的簡単な計算で求められるので、定数を定義して、計算式で widget のリソースを生成しています。

とりあえず、ルールとして:

  • 中央の上部に配置する
  • 左右に音色を変更するボタンを配置する
  • 中央に音色名を表示する
  • オクターブボタンは、画面の端に表示する
  • オクターブ領域表示は、中央に表示する
        static const int16_t SC_NAME_LEN = 16;   ///< With EOT
        static const int16_t SC_NUM = 32;   ///< 音色最大数
        static const int16_t OCT_NUM = 5;   ///< オクターブ域

...

        static const int16_t SC_LOC = 10;   ///< ボタン関係、縦の位置
        static const int16_t SC_SPC = 10;   ///< ボタンとの隙間
        static const int16_t CENTER = 480/2;   ///< X 中心
        static const int16_t SC_BTN_SZ = 30;   ///< ボタンサイズ
        static const int16_t SC_TEX_W = 8 * SC_NAME_LEN;  ///< テキスト横幅
        static const int16_t SC_TEX_H = 24;       ///< テキスト高さ

        static const int16_t OCT_LOC = 40;   ///< オクターブ関係、縦の位置
        static const int16_t OCT_AREA_W = 300;
        static const int16_t OCT_AREA_H = 30;
        static const int16_t OCT_BTN_SZ = 50;   ///< ボタンサイズ

...

        typedef gui::widget WIDGET;
        typedef gui::button BUTTON;
        typedef gui::text TEXT;
        typedef gui::slider SLIDER;
        BUTTON      sc_idx_m_;
        TEXT        sc_name_;
        BUTTON      sc_idx_p_;

        BUTTON      octave_m_;
        SLIDER      octave_d_;
        BUTTON      octave_p_;

...

        synth_gui(RENDER& render, TOUCH& touch) noexcept :
            render_(render), touch_(touch), widd_(render, touch),
            keyboard_(render, touch),
            sc_idx_m_(vtx::srect(CENTER-SC_TEX_W/2-SC_BTN_SZ-SC_SPC, SC_LOC, SC_BTN_SZ, SC_BTN_SZ), "<"),
            sc_name_ (vtx::srect(CENTER-SC_TEX_W/2, SC_LOC+(SC_BTN_SZ-SC_TEX_H)/2, SC_TEX_W, SC_TEX_H),  ""),
            sc_idx_p_(vtx::srect(CENTER+SC_TEX_W/2+SC_SPC,        SC_LOC, SC_BTN_SZ, SC_BTN_SZ), ">"),
            octave_m_(vtx::srect(0,              OCT_LOC, OCT_BTN_SZ, OCT_BTN_SZ), "<<"),
            octave_d_(vtx::srect(CENTER-OCT_AREA_W/2, OCT_LOC+(OCT_BTN_SZ-OCT_AREA_H)/2, OCT_AREA_W, OCT_AREA_H)),
            octave_p_(vtx::srect(480-OCT_BTN_SZ, OCT_LOC, OCT_BTN_SZ, OCT_BTN_SZ), ">>"),
            sc_idx_(0), sc_idx_before_(0), sc_name_org_(nullptr),
            oct_idx_(2)
        { }

BUTTON、TEXT、SLIDER クラスは、コンストラクターで座標を計算して設定しています。

このように定数を設けて、それを起点に座標を生成すると、簡単なルールで、座標の設定を自動化出来て便利です。
ボタンの大きさや隙間など、パラメーターとしているので、簡単に変更出来ます。
計算で行うと、計算式が正しければ、理にかなった正しい表示が行えます。

C++17 では、より複雑なルールや条件分岐など、プログラム的な物を、constexpr を使って定義する事も出来ます。
※ constexpr については、ググッて下さい。
constexpr はコンパイル時に計算されるので、どんなに複雑でも、実機の処理負荷には影響を与えません。

まとめ

マルチタッチを有効に活用するアプリとして、「鍵盤」は、最適なアプリと思えます。

今後、MIDI ファイルの演奏など、アプリを充実させていきたいと思います。

SYNTH_sample


追記

Arduino 環境用に、スタンダード MIDI のプレイヤー(パーサー)があったので、ポーティングしてみました。
多少、改造しましたが、想定の範囲で演奏出来るようです。
ソースコードは、コミットしてあります。
スタンダード MIDI ファイルはネットにあるものが大体使えるようですが、演奏出来ないファイルもあり、その点は調査中です。

「@」ボタンを押すと、ファイラーが開くので、MIDIファイルを選択すれば演奏が始まります。

演奏中、音色を変更する事も出来ます。