作るもの
こんなやつ。これでMac/WindowsにつながればUSB-MIDI音源と認識され、スタンドアロンでMIDIN INがついたハードウェアのシンセサイザーになる。ちなみにUSB-MIDIだけでなくUSB-Audioとしても認識させることができる。奥のはTeensy4で組んだ先代。
用意するもの
- Teensy4.1
- PCM5102が乗った基板
- SSD1306なOLED基板
- フォトカプラー (ここではPC900Vを使用)
- 220Ωの抵抗 x2
- ダイオード
- ポテンションメーター x2
- タクトスイッチ x4
- ピンヘッダ
- ジャンパワイヤ
- ハンダごて
- ハンダ
- USBケーブル (microUSB -> USB-A or USB-C)
- MacかWindows PC (ここではMac版で説明)
接続
PCM5102との接続
- VCC -> 5V
- GND -> GND
- LCK -> 20
- DIN -> 7
- BCK -> 21
- SCK -> 23
とりあえずこの6本繋げば良い。他にも色々な配線がネット上では散見されたが、実際にハードウェアソフトウェア両面の改変無くTeensy4.1で動いたのはここに書いてあった上記の接続。
SSD1306 OLEDディスプレイの接続
- GND -> GND
- VCC -> 5V
- SCK (SCL) -> 19
- SCA (SDA) -> 18
MIDI IN
下記の回路を組む。MIDI INだけ必要でTHRUとOUTはいらない。VCCに繋がってる抵抗は220Ωで良い。
- VCC -> 3.3V (5Vじゃないので注意!)
- MIDI RX -> 0
- GND -> GND
タクトスイッチ
4つ繋ぐ。片方の足をそれぞれのGND、もう片方の足を2, 3, 4, 5に繋ぐ。
ポテンションメーター
2つ繋ぐ。右の足をそれぞれ5V、左の足をそれぞれGND、真ん中の足を16、17に接続。
動作テスト
Audio (PCM5102A)
Teensyduinoを起動。
- ツール -> ボード -> Teensyduino -> Teensy 4.1
- ツール -> USB Type -> Serial + MIDI + Audio
と設定し、ファイル -> スケッチ例 -> Audio -> HardwareTesting -> PassThroughUSB でサンプルを開き、「マイコンボードに書き込む」ボタン(丸に→)をクリックしてボードに書き込み実行。すると、デバイスがUSB-Audio/MIDIデバイスとして認識される。サンプリング周波数は44100Hz固定のよう。Macのシステム環境設定→サウンドで出力デバイスとして指定し、何か再生して音が正常に鳴ればOK。
OLED
スケッチ -> ライブラリをインクルード -> ライブラリを管理...で「SSD1306」を検索。「Adafruit SSD1306」をインストール。さらに、「GFX」で検索して「Adafruit GFX Library」をインストール。両方とも特に理由がなければ最新バージョンを。
ファイル -> スケッチ例 -> Adafruit SSD1306 -> ssd1306_128x64_i2c (すごい下の方にある) を選択。ソースの中でI2Cのアドレスを指定している部分があるので、これをハードウェアに合わせる。こちらで使っているデバイスでは128x64のもので0x78と基板に書いてあるのに0x3Cだった (半分の値にするのが正解だったということ?)。
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 ←ここを変える
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
:
実行してAdafruitのロゴに続いて各デモが流れればOK。
MIDI IN
ファイル -> スケッチ例 -> MIDI Library -> SimpleSynth でサンプルを開き、handleNoteOn() と handleNoteOff() に下記のように追記。
void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
const bool firstNote = midiNotes.empty();
midiNotes.add(MidiNote(inNote, inVelocity));
handleNotesChanged(firstNote);
Serial.printf("Note On note=%d, velocity=%d\n", inNote, inVelocity); // これ
}
void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
midiNotes.remove(inNote);
handleNotesChanged();
Serial.printf("Note Off note=%d, velocity=%d\n", inNote, inVelocity); // これ
}
実行してシリアルモニタにMIDI Note On/Off時にメッセージが出ればOK。基板上のLEDも点灯する。
サンプル
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <MIDI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// MIDI
MIDI_CREATE_DEFAULT_INSTANCE();
// Audio
// GUItool: begin automatically generated code
AudioSynthWaveformSine sine1; //xy=290,143
AudioOutputI2S i2s1; //xy=433,142
AudioConnection patchCord1(sine1, 0, i2s1, 0);
AudioConnection patchCord2(sine1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=140,154
// GUItool: end automatically generated code
// OLED
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
// Audio
AudioMemory(20);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
sine1.amplitude(0.75);
sine1.frequency(1000);
// MIDI
MIDI.setHandleNoteOn(handleNoteOn);
MIDI.setHandleNoteOff(handleNoteOff);
MIDI.begin();
// OLED
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library in/itializes this with an Adafruit splash screen.
//display.display();
display.clearDisplay();
display.setTextSize(2); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.cp437(true); // Use full 256 char 'Code Page 437' font
display.println("Start");
display.display();
}
void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
sine1.amplitude(0.75);
double freq = 440.0 * pow (2.0, ((double)inNote - 69.0) / 12.0);
sine1.frequency(freq);
display.setTextSize(1);
display.printf("Note On %d, %d\n", inNote, inVelocity);
display.display();
}
void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
sine1.amplitude(0.0);
display.setTextSize(1);
display.printf("Note Off %d, %d\n", inNote, inVelocity);
display.display();
}
void loop() {
// put your main code here, to run repeatedly:
MIDI.read();
}