製作モジュール一覧

ラベル MIDI Library の投稿を表示しています。 すべての投稿を表示
ラベル MIDI Library の投稿を表示しています。 すべての投稿を表示

2018年12月8日土曜日

MIDI-CV Converter MIDI_CV_CONVの構想

BSM02でシーケンサー+D/A混在シンセを作りましたが、打ち込んだデーターを外に取り出す仕組みは作っていないので、Nucleo_DCOに音階を与えるために流用するのは少しめんどうです。BSM02のハードウェア/ソフトウェアに手を入れる必要があるので、思いつきで崩してしまうのは抵抗があります。

ひとまずは、パソコン上のDAWを使ってMIDI信号を出力し、CV/GATE信号に変換する、MIDI-CVコンバーターを作ってみることにしました。

作戦


ArduinoとmbedのMIDI Libraryを簡単に試したことがありますが(https://dad8893.blogspot.com/search/label/MIDI%20Library)、サイズの小さいNucleo F303K8を使ってmbedでやってみると、どうもMIDIメッセージを読みこぼすようです。

Nucleo-F303K8は12bit DACが3系統内蔵されていて、性能もCortex-M4でそこそこ高スペックなので使ってみたかったのですが、まずはArduinoでやってみることにしました。

Arduino NANOかArduino Pro Miniを使うつもりです。

想定している使用例

ArduinoにSPI/DACのMCP4922を接続し、アナログ値を出力してCVとします。MCP4922は2ch DACなのでうまく行けば2ch分CVを出力できます。

Nucleo_DCOは3.3V系(5Vトレラント)なのでCVの信号レベルは0V~3.3Vとします。

「DASS01」(「ASS01」という名前にしようと思ってましたが、「DASS01」に変更します)はアナログ系でCV信号にもう少し振幅が欲しいので「DASS01」内の「Amp」で増幅します。増幅率は「Mod Level」POTで可変します。

GATE信号はGPIOから出力します。「Arduino_EG」のプロセッサーは5V駆動のArduino Pro Miniなので入力するGATE信号レベルは5VでOKです。「KIK01」はNucleo-F446RE、「BSM02」はNucleo-F401REでSyncIn(GATEの立ち上がりで評価)は3.3V系ですが5Vトレラントなのでこれも5VでOKです。

開発環境


ArduinoでMIDI Libraryを使うとデフォルトではハードウェアUART(D0, D1)を使うので、デバッグ用にArduinoのSerialクラスが使えません。簡単なメッセージの表示しかできませんが、I2C接続のキャラクタLCD(秋月のAE-AQM1602A)を使うことにしました。

ブレッドボード配線図

※Arduinoで書き込むときは、RXの配線を抜く。


※MCP4922はまだ接続していません。

Arduinoのスケッチ


AE-AQM1602Aのライブラリは「I2CLiquidCrystal-1.5 https://n.mtng.org/ele/arduino/i2c.html」を使いました。

<MIDI_Display_Message_I2C_LCD_Gate.ino>

#include <Wire.h>
#include <I2CLiquidCrystal.h>
#include <MIDI.h>

#define LCD_TRACE   (0)
#define PIN_CHECK   (1)
#define TITLE_STR1  ("MIDI to GATE    ")
#define TITLE_STR2  ("20181204        ")

// Pin assign
const int GateOutPin1 = 2;
const int GateOutPin2 = 3;
const int GateOutPin3 = 4;
const int GateOutPin4 = 5;
const int GateOutPin5 = 6;
const int GateOutPin6 = 7;

const int CheckPin1   = 8;

// MIDI
MIDI_CREATE_DEFAULT_INSTANCE();

// I2C LCD
#if (LCD_TRACE)
I2CLiquidCrystal lcd(32, (bool)true);
#endif

// MIDI -------------------------------------------------------------------------

#if (LCD_TRACE)
void printNoteOnOff(const char* type, byte inChannel, byte inNote, byte inVelocity)
{
  lcd.setCursor(0, 1);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print(type);
  lcd.print(' ');
  lcd.print(inChannel);
  lcd.print(' ');
  lcd.print(inNote);
  lcd.print(' ');
  lcd.print(inVelocity);
}
#endif

void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
  digitalWrite(GateOutPin1, HIGH);
#if (LCD_TRACE)
  printNoteOnOff("On", inChannel, inNote, inVelocity);
#endif
}

void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
  digitalWrite(GateOutPin1, LOW);
#if (LCD_TRACE)
  printNoteOnOff("Off", inChannel, inNote, inVelocity);
#endif
}

// -----------------------------------------------------------------------------

void setup()
{
  pinMode(GateOutPin1, OUTPUT);
  pinMode(GateOutPin2, OUTPUT);
  pinMode(GateOutPin3, OUTPUT);
  pinMode(GateOutPin4, OUTPUT);
  pinMode(GateOutPin5, OUTPUT);
  pinMode(GateOutPin6, OUTPUT);
  pinMode(CheckPin1,   OUTPUT);

  MIDI.setHandleNoteOn(handleNoteOn);
  MIDI.setHandleNoteOff(handleNoteOff);
  MIDI.begin(MIDI_CHANNEL_OMNI);

#if (LCD_TRACE)
  lcd.begin(16, 2);
  lcd.print(TITLE_STR1);
  lcd.setCursor(0, 1);
  lcd.print(TITLE_STR2);
  delay(2000);
#endif
}

void loop()
{
#if (PIN_CHECK)
  digitalWrite(CheckPin1, HIGH);
#endif

  if (MIDI.read()) {

    // Display Raw Midi Message

    byte type    = MIDI.getType();
    byte channel = MIDI.getChannel();
    byte data1   = MIDI.getData1();
    byte data2   = MIDI.getData2();

#if (LCD_TRACE)
    lcd.setCursor(0, 0);
    lcd.print("                ");
    lcd.setCursor(0, 0);
    lcd.print(type, HEX);
    lcd.print(' ');
    lcd.print(channel, HEX);
    lcd.print(' ');
    lcd.print(data1, HEX);
    lcd.print(' ');
    lcd.print(data2, HEX);
#endif
  }
#if (PIN_CHECK)
  digitalWrite(CheckPin1, LOW);
#endif
}

メモ:


LCD_TRACEを有効化(1)するとMIDIメッセージを読みこぼす。

中華製のArduino NANOはプロセッサでATMega328P(Old bootloder)を指定する必要がある場合がある。

メインループの処理時間は92kHzぐらいで変わらない?

DACからの出力にはLPFを入れる。Nucleo_DCOはADCにCV信号を入力するので注意。

厳密にするならDACやGPIOからの信号はデジタル回路でラッチをかけて同期させたいけど、そこまでは必要ないか・・・

2018年5月5日土曜日

Nucleo(mbed)でMIDIメッセージを受信する

mbedにもArduinoのMIDI Libraryを移植したものがあるので、テストしました。

ブレッドボード配線図


Nucleo F446REにはSerialが複数あるので、今回はSerial 2を使用しました。NucleoのArduino HeaderのD2(RX)、D10(TX)です。Serial 1はプログラミングやPCとのSerial通信に使用します。

ファームウェア <main.cpp>

#include "mbed.h"
#include "MIDI.h"

#define TITLE_STR1  ("MIDI Message")
#define TITLE_STR2  ("20180502")

MIDI midi(D10, D2); // Serail2
Serial pc(D1, D0);  // Serial1

// -----------------------------------------------------------------------------

void printNoteOnOff(const char* type, byte inChannel, byte inNote, byte inVelocity) 
{
    pc.printf("%s %3d %3d %3d\r\n", type, inChannel, inNote, inVelocity);
}

void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
    midi.sendNoteOn(inNote, inVelocity, inChannel);
    printNoteOnOff("On ", inChannel, inNote, inVelocity);
}

void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
    midi.sendNoteOff(inNote, inVelocity, inChannel);
    printNoteOnOff("Off", inChannel, inNote, inVelocity);
}

// -----------------------------------------------------------------------------

int main()
{
    midi.setHandleNoteOn(handleNoteOn);
    midi.setHandleNoteOff(handleNoteOff);
    midi.begin(MIDI_CHANNEL_OMNI);
    
    pc.baud(115200);

    for (;;) {
        if (midi.read()) {
            byte type    = midi.getType();
            byte channel = midi.getChannel(); 
            byte data1   = midi.getData1();
            byte data2   = midi.getData2();
    
            pc.printf("%2x %2x %2x %2x\r\n", type, channel, data1, data2);
        }
    }
}

mbed repository
https://os.mbed.com/users/ryood/code/MIDI_Display_Message/

動作としては、MIDI IN(D2)で受信したMIDIメッセージをMIDI OUT(D10)にオウム返しし、SerialでPCに送信するものです。

Puttyで受信

送受信のようす

ch1:MIDI IN ch2:MIDI OUT

Nucleoは3.3V駆動なので、MIDI OUTの信号レベルが3.3Vになっています。MIDIの受信はフォトカプラで受けることになっているので3.3Vでも大丈夫なようですが、できれば5V系にレベル・シフトしておいた方よさそうです。

2018年5月3日木曜日

Arduino + LCDで受信したMIDIメッセージを表示

MIDIの規格はよくわからないので、MIDI OXというPCのMIDIユーティリティ・ソフトを使って受信したメッセージを表示させてみました。

「SF BAY MUSIC TECH」さんの「MIDI OX を使って MIDI メッセージを記録する方法」に使用方法があります。

MIDI接続


MIDI Keyboard(Novation Remote 25)からPCに直接USBでPCに接続し、同時にMIDI OUTからUSBオーディオ・インターフェイスのMIDI INに接続しました。PCへは2系統のMIDI入力になります。

MIDI Deviceの設定


MIDI OXの[Option]-[MIDI Devices...]でダイアログが開きます。


認識しているデバイスが表示されるので、[MIDI Inputs:]で[1) ReMOTE]と[2) TASCAM US-144 MKII MIDI]両方を選択しました。


MIDIキーボードで適当に鍵盤を押すと[Monitor - Input]に受信したMIDIメッセージが表示されます。2系統で受信しているので[IN]カラムの値が1と2で同じメッセージが受信されています。

Arduino UnoでMIDIメッセージを表示する。


Arduinoに16行2桁のキャラクタLCD(HD44780互換)を接続して受信したMIDIメッセージを表示させました。フォトカプラを使ったMIDIの受信についていては前回の記事にあります。

ブレッドボード配線図




Arduinoのスケッチ <MIDI_Display_Message.ino>

#include <MIDI.h>
#include <LiquidCrystal.h>

#define TITLE_STR1  ("MIDI Message    ")
#define TITLE_STR2  ("20180430        ")

// MIDI
MIDI_CREATE_DEFAULT_INSTANCE();

// LCD
static const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// -----------------------------------------------------------------------------

void printNoteOnOff(const char* type, byte inChannel, byte inNote, byte inVelocity) 
{
    lcd.setCursor(0, 1);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    lcd.print(type);
    lcd.print(' ');
    lcd.print(inChannel);
    lcd.print(' ');
    lcd.print(inNote);
    lcd.print(' ');
    lcd.print(inVelocity);  
}

void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
    printNoteOnOff("On", inChannel, inNote, inVelocity);
}

void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
    printNoteOnOff("Off", inChannel, inNote, inVelocity);
}

// -----------------------------------------------------------------------------

void setup()
{
    MIDI.setHandleNoteOn(handleNoteOn);
    MIDI.setHandleNoteOff(handleNoteOff);
    MIDI.begin(MIDI_CHANNEL_OMNI);

    lcd.begin(16, 2);
    lcd.print(TITLE_STR1);
    lcd.setCursor(0, 1);
    lcd.print(TITLE_STR2);
}

void loop()
{
    if (MIDI.read()) {

        // Display Raw Midi Message

        byte type    = MIDI.getType();
        byte channel = MIDI.getChannel(); 
        byte data1   = MIDI.getData1();
        byte data2   = MIDI.getData2();

        lcd.setCursor(0, 0);
        lcd.print("                ");
        lcd.setCursor(0, 0);
        lcd.print(type, HEX);
        lcd.print(' ');
        lcd.print(channel, HEX);
        lcd.print(' ');
        lcd.print(data1, HEX);
        lcd.print(' ');
        lcd.print(data2, HEX);
    }
}


LCDの1行目は、受信したMIDIメッセージの
Type | Channel | Data1 | Data2
を表示。2行目はNoteON、NoteOFFのときのみ
ON/OFF | Channel | Note | Velocity
を表示するようにしています。

ちゃんとはチェックしていませんがだいたいのメッセージは拾えているようです。

まずは、NoteON/NoteOFF、PitchBend、Modulation、Control Changeが使えればいいかな~と思っています。

2018年4月25日水曜日

Arduino MIDILibraryを使ってみる。

自作のシンセをPCのDAWからコントロールできないかと思ってマイコンのMIDIライブラリを調べてみました。

わたくしが使えるデバイスはArduino、Nucleo(mbed)、PSoC5LPなので一通り当たってみました。

Arduino


開発環境がこなれているので一番信頼しています。←だいたいすでに誰かがやってくれている。

オフィシャルサイトのPlaygroundにMIDILibraryがあります。これは基本的にはシーケンサーやMIDI機器とMIDIケーブルで接続してArduinoと通信するライブラリです。

Midi Firmware for Arduino Uno (Moco)というファームウェアもあって、MIDILibraryと組み合わせればusb midiとして使えるそうです。

参考「超簡単!Arduino UNOをMIDIコントローラーにしよう! <Arduino MIDI Library 4.2対応版>

ただしMocoはオフィシャルのArduino UnoのようにUSB-Serial変換にATMega8U2/16U2を用いているものしか使えません。互換品はUSB-Serial変換にたいがいCH340を使っているのでMocoは使えません。

オフィシャルのArduino Unoも持っているのですが、ATMega16U2のファームウェアを書き換えるので、下手して動かなくなったらいやなので保留(^q^;

互換品でも「Elegoo UNO R3 ボード」や「keyestudio UNO R3 開発ボード」はUSB-Serial変換にATMega16U2を搭載しているようです。←持ってないので未確認です。


mbed


mbedにもArduinoから移植されたMidiLibraryがあります。

また、オフィシャルサイトのCookBookにUSBMIDIライブラリがあります。手軽そうでいいのですが、ターゲットにNucleo F446を指定してコンパイルすると
Error: #error directive: "CMSIS Target not recognised" in "extras/mbed/cmsis.h", Line: 16, Col: 3
というエラーが出てコンパイルできませんでした。ライブラリのソースをチラ見すると「LPC_USB」というオブジェクト名が出てきてLPC専用なのかもしれません。

LPCもトラ技の付録のライターを持っているのですが(参考「トラ技ARMライタでLチカ (LPC810)」)全然使っていないのでこれも保留。

PSoC5LP


PSoC5LPにもUSBMIDIがあるようです。

参考「USBMIDI of PSoC5LP 」「CE95395 - USB MIDI with PSoC 3/5LP

Arduino MIDILibraryを使ってみる。


というわけで、まずはArduinoのMIDILibraryを試してみることにしました。フォトカプラを使ったMIDI受信は以前簡単に試したことがあります。

MIDI信号を受信する TLP552編(メモ)

BASIC_IO


Arduino IDEにMIDILibraryをインストールしてExample/BASIC_IOを動作させました。

ブレッドボード配線図


<追記:2018.04.28>

Arduinoへのプログラミング中にMIDI信号を受信すると書き込みに失敗するので、プログラミング中はD0とフォトカプラ間のケーブルは抜いておいた方がいいです。

</追記>

MIDI信号はPCのAbleton LiveからTASCAM US-144MKIIに出力し、MIDIケーブル経由でフォトカプラ(TLP552)で受信しました。

Ableton Liveの設定は下図の通りトラックの「MIDI To」で送信するデバイス(TASCAM US-144MKII)とMIDI Ch.に4を指定しました。←BASIC_IO Exampleはch.4を受信するように書かれています。


MIDIシーケンスはBPMに60を指定し、1小節目の1拍目にC3を1発出して、2秒に1回MIDI信号が送られるようにしました。BASIC_IOはMIDI信号を受信すると1秒間LEDを点灯させるので、これで2秒に1回の周期でArduinoのLEDが点滅します。

また、RX(D0)にMIDI信号を受信したとき、TX(D1)からプログラムに書かれている通りのMIDIメッセージが送出されます。

MIDI送受信のようす

ch1:MIDI IN(D0)  ch2:MIDI OUT(D1)


SimpleSynth

受信したMIDI信号の音階をArduinoのtone()関数で再生するものです。

ブレッドボード配線図


D10にスピーカーをつないで、無事チープなメロディーが再生されました(^q^/

※普通のスピーカーをつなぐと爆音になると思います。実験用に低効率のスピーカーをつないでいます。

こちらは送出側のMIDI Ch.を1にしないとダメなようです。

<追記:2018.04.28>

MIDILibraryのMIDI.hを見ると

public:
    void begin(Channel inChannel = 1);

となっていて、引数を省略してMIDI.begin()するとCh. 1を指定したことになるようです。

その他@yudai220さんの「Arduino MIDI Library の使い方」でかなり詳細に解説されています。

</追記>

D13にGate信号も出力されているので(LEDの点滅に利用されています)、CV出力用にDACをつなげれば、これだけでMIDI-CV/Gateコンバーターになりそうな感じです。

メモ:


MIDIに限ればDominoというフリーのシーケンサーがあって、こちらの方が慣れればAbleton Liveより使いやすいかも?