LoginSignup
1
1

Arduino: MIDI shieldを使って一本のフェーダーで複数のフェーダーを操作する

Last updated at Posted at 2024-04-26

ご同業の方以外には伝わりづらい内容だとは思いますが一応書き残しておきます。

動機

ミュージカルPAをやるのですが、俳優のかかえるワイヤレスマイクの本数が多すぎて手が追いつかないのです。
そこで、一本のフェーダーで複数のフェーダーが同時に操作できればいいのでは?と閃きました。

こんな風にしたい

(っていうか完成映像):

  • 特に忙しいシーンをよっつ、空きフェーダーに登録して親フェーダーとし、最大13本のチャンネルから動かしたいやつを選んで子フェーダーにする。
  • onボタンが点灯してるときだけ動作する(誤動作防止のため)。
  • onボタンを点灯したときに親フェーダーの値を一斉に子フェーダーにフラッシュする。
  • 親フェーダーを動かしたとき、子フェーダー同士の相対位置を保ったまま上下する。
  • 親フェーダーを下げきったら、子フェーダーも下げきる。相対位置は一旦リセットされる。

使うもの

IMG20240423183016.jpg

Arduino Leonardoと Arduino MIDI Shield

Leonardoはその辺にころがってたやつ。MIDI Shieldは、AlliexpressとAmazonで購入しました。

こいつにプログラムを入れて、

  • 音響卓のMIDI OUT->ShieldのMIDI IN、
  • ShieldのMIDI OUT->音響卓のMIDI IN

と結線して使います。あと、USB電源も必要。

コード

fader_to_faders_4.ino
#define countof(array) (sizeof(array) / sizeof(array[0]))
#include <MIDI.h>

MIDI_CREATE_DEFAULT_INSTANCE();

constexpr byte NUM_INS = 4;

constexpr byte IN_FADER_CC[ NUM_INS ] = {21, 22, 23, 24};
constexpr byte IN_ON_CC[ NUM_INS] = {84, 85, 86, 87};

constexpr byte OUT_CCS_ALL[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
constexpr byte OUT_CCS_0[] = {3, 8, 9, 10, 11, 12, 13};
constexpr byte OUT_CCS_1[] = {3, 7, 8, 9, 10, 11};
constexpr byte OUT_CCS_2[] = {3, 6, 7, 8, 9, 10, 11};
constexpr byte OUT_CCS_3[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

constexpr size_t N_OUT_CCS_ALL = countof(OUT_CCS_ALL);
constexpr size_t N_OUT_CCS_0 = countof(OUT_CCS_0);
constexpr size_t N_OUT_CCS_1 = countof(OUT_CCS_1);
constexpr size_t N_OUT_CCS_2 = countof(OUT_CCS_2);
constexpr size_t N_OUT_CCS_3 = countof(OUT_CCS_3);

struct CCArray{
  const byte* CCS;
  const size_t N;
  CCArray(const byte* ccs, const size_t n): CCS(ccs), N(n){};
};

CCArray OUT_FADERS[ NUM_INS ] = {
  CCArray(OUT_CCS_0, N_OUT_CCS_0)
  , CCArray(OUT_CCS_1, N_OUT_CCS_1)
  , CCArray(OUT_CCS_2, N_OUT_CCS_2)
  , CCArray(OUT_CCS_3, N_OUT_CCS_3)
  };

class MyProcessing{
  private:
    byte in_on_recent[NUM_INS] = {0, 0, 0, 0};
    byte in_fader_recent[NUM_INS] = {0, 0, 0, 0};
    byte out_fader_recent[N_OUT_CCS_ALL] = 
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

  public:
    void operator()(byte channel, byte num, byte value){

      for(int i = 0; i < N_OUT_CCS_ALL; i++){
        if(num == OUT_CCS_ALL[i]){
          out_fader_recent[i] = value;
          return;
        }
      }
      
      for(int i = 0; i < NUM_INS; i++){
        if(num == IN_ON_CC[i]){
          if( ! in_on_recent[i] ){
            for (int j = 0; j < OUT_FADERS[i].N; ++j) {
              byte out_cc = OUT_FADERS[i].CCS[j];

              byte out_fader_index;
              for(int k = 0; k < N_OUT_CCS_ALL; k++){
                if(OUT_CCS_ALL[k] == out_cc){
                  out_fader_index = k;
                }
              }

              MIDI.send(
                midi::ControlChange
                , out_cc
                , out_fader_recent[out_fader_index] = in_fader_recent[i]
                , channel
                );
              if(in_fader_recent[i] == 0){
                MIDI.send(
                  midi::ControlChange
                  , out_cc + 32
                  , 0
                  , channel
                  );
              }
            }
            in_on_recent[i] = value;
            return;
          }  
          in_on_recent[i] = value;
          return;
        }

        if(num == IN_FADER_CC[i]){ 
          if( in_on_recent[i] ){
            int diff = value - in_fader_recent[i];

            for(int j = 0; j < OUT_FADERS[i].N; ++j) {
              byte out_cc = OUT_FADERS[i].CCS[j];

              byte out_fader_index;
              for(int k = 0; k < N_OUT_CCS_ALL; k++){
                if(OUT_CCS_ALL[k] == out_cc){
                  out_fader_index = k;
                }
              }
              
              if(value == 0){
                MIDI.send(
                  midi::ControlChange
                , out_cc
                , out_fader_recent[out_fader_index] = 
                  0
                , channel
                );
                MIDI.send(
                  midi::ControlChange
                , out_cc + 32
                , 0
                , channel
                );
              }
              else{
                MIDI.send(
                  midi::ControlChange
                , out_cc
                , out_fader_recent[out_fader_index] = 
                    constrain(
                      out_fader_recent[out_fader_index] + diff
                      , 0
                      , 127
                      )
                , channel
                );
              }
            }
            in_fader_recent[i] = value;
            return;
          }
          in_fader_recent[i] = value;
          return;
        }
      }
    }
};

MyProcessing handleControlChange;

void setup() {
  MIDI.setHandleControlChange(
    [&handleControlChange](byte channel, byte num, byte value) {
      handleControlChange(channel, num, value);
    }
  );
  MIDI.begin(MIDI_CHANNEL_OMNI);
}

void loop() {
  MIDI.read();
}

あとで解説書くかも。

音響卓の設定

扱うコントロールチェンジはコードの中に決め打ちされているので、必要なら卓のMIDI設定を変更します。

  • MIDIコントロールチェンジ送受信を許可する。
  • 親フェーダーはCC21~24、onボタンはCC84~87を使用する。
  • 子フェーダーはCC1~13を使用する。
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1