Teensy4.1と激安パーツでハードウェアシンセサイザーを作る (準備)

## 作るもの ##
こんなやつ。これでMac/WindowsにつながればUSB-MIDI音源と認識され、スタンドアロンでMIDIN INがついたハードウェアのシンセサイザーになる。ちなみにUSB-MIDIだけでなくUSB-Audioとしても認識させることができる。奥のはTeensy4で組んだ先代。
![IMG_7611.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/b633045b-785a-8139-575e-8ef90fb63f30.jpeg)

## 用意するもの ##
* [Teensy4.1](https://hb.afl.rakuten.co.jp/ichiba/22462e2f.8bb44da4.22462e30.325767be/?pc=https%3A%2F%2Fitem.rakuten.co.jp%2Frobotshopjapan%2Frb-pjr-21%2F&link_type=hybrid_url&ut=eyJwYWdlIjoiaXRlbSIsInR5cGUiOiJoeWJyaWRfdXJsIiwic2l6ZSI6IjI0MHgyNDAiLCJuYW0iOjEsIm5hbXAiOiJyaWdodCIsImNvbSI6MSwiY29tcCI6ImRvd24iLCJwcmljZSI6MSwiYm9yIjoxLCJjb2wiOjEsImJidG4iOjEsInByb2QiOjAsImFtcCI6ZmFsc2V9) 
* [PCM5102が乗った基板](https://amzn.to/31wFhNA)
* [SSD1306なOLED基板](https://amzn.to/3IlUK3Z)
* フォトカプラー (ここでは[PC900V](https://wakamatsu.co.jp/biz/products/list.php?category_id=1903&pageno=2)を使用)
* 220Ωの抵抗 x2
* ダイオード
* ポテンションメーター x2
* タクトスイッチ x4
* ピンヘッダ
* [ジャンパワイヤ](https://amzn.to/31qsrAB)
* ハンダごて
* ハンダ
* 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で動いたのは[ここ](https://forum.pjrc.com/threads/66479-Help-me-decide-on-a-DAC-for-a-poly-synth?highlight=PCM5102)に書いてあった上記の接続。

### SSD1306 OLEDディスプレイの接続 ###
4PIN (I2C) の場合
* GND -> GND
* VCC -> 5V
* SCK (SCL) -> 19
* SCA (SDA) -> 18

I2Cは描画が遅かったので今はSPI接続のを使ってる。

### MIDI IN ###
下記の回路を組む。MIDI INだけ必要でTHRUとOUTはいらない。VCCに繋がってる抵抗は220Ωでも良い。
https://www.pjrc.com/tech/midi-drums/sch-midi.html

よって必要な部品は、
* SHARP PC900V (色々代用品はある)
* 5PIN DINジャック
* ダイオード
* 220Ωの抵抗 x2

ユニバーサル基板に実際に組んだのがこれ。DINの2番はGNDに落とした方がいいらしい(しなくても問題はなかったが)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/c4bbb79f-716a-4d6e-a16c-5b55975717c7.png)

* VCC -> 3.3V (5Vじゃないので注意！...でも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();
}
```

![5D60690A-942C-491A-B9A9-0A0A28C9B49F.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/bf17bffa-4b94-8e31-43eb-41430bda367d.jpeg)