Teensy 4.1からI2Sで音声出力をする(PCM5102A)
Teensy 4.1について
Teensy 4.1はARM Cortex-M7を搭載した高性能なマイコンです。
Cortex-M7にはFPUが搭載されており、ESP32よりも高速で浮動小数点演算ができます。
定格600MHzで動作しオーバークロックも可能、マイクロSDカードスロット付きでI/Oピン数は55とこれひとつで様々な物が作れそうです。
公式でオーディオライブラリが提供されている
Teensyでは音声データの再生や合成、録音、フィルタリングやミキシング等の機能を備えたオーディオツールキットを提供しています。
Teensyのオーディオライブラリはかなり豊富な内容で、オーディオの入出力だけではなく、シンセサイザー用の機能やコーラス、リバーブ、グラニュラー等のエフェクト機能もあり、このライブラリだけでシンセサイザーを作ることもできてしまいます。
ビット深度は16bit、サンプルレートは44.1kHzでオーディオのストリーミングが可能です。
Audio System Design Toolで視覚的にオーディオシステムの設計をすることができます。
I2SDACを使って音声出力
TeensyのオーディオライブラリではAudioPlayQueueを使って音声の再生ができます。
AudioPlayQueueに自身で用意したオーディオデータを渡してI2S通信をします。
配線
Teensy 4.1にPCM5102Aを接続します。
割り当て表を参考に各ピンを接続してください。
PCM5102AはMCLKが不要なので、LRCLK、BCLK、SDATAを接続します。
※PCM5102Aの動作に必要な回路は構築済みとします。
Teensy 4.1 | PCM5102A |
---|---|
OUT2 (ピン番号2) | DIN |
LRCLK2 (ピン番号3) | LRCK |
BCLK2 (ピン番号4) | BCK |
コード
今回は以下のようなコードを書きました。
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
AudioPlayQueue queue_L, queue_R;
AudioOutputI2S2 i2s2;
AudioConnection patchCord1(queue_R, 0, i2s2, 1);
AudioConnection patchCord2(queue_L, 0, i2s2, 0);
#define SAMPLE_RATE 44100.0
#define BUFFER_SIZE 128
float phase = 0;
float delta = 440.0 / SAMPLE_RATE;
int16_t samples_L[BUFFER_SIZE];
int16_t samples_R[BUFFER_SIZE];
int16_t triangle(float phase) {
return static_cast<int16_t>((1.0 - fabs(2.0 * phase - 1.0)) * 32767);
}
void update() {
for(size_t i = 0; i < BLOCK_SIZE; i++) {
samples_L[i] = triangle(phase);
samples_R[i] = triangle(phase);
phase += delta;
if (phase >= 1.0) phase -= 1.0;
}
queue_L.play(samples_L, BUFFER_SIZE);
queue_R.play(samples_R, BUFFER_SIZE);
}
void setup() {
queue_L.setMaxBuffers(2);
queue_R.setMaxBuffers(2);
AudioMemory(4+2);
}
void loop() {
if(queue_L.available() && queue_R.available()) {
update();
}
}
オーディオの設定は以下の部分で行われています。
AudioPlayQueue queue_L, queue_R;
AudioOutputI2S2 i2s2;
AudioConnection patchCord1(queue_R, 0, i2s2, 1);
AudioConnection patchCord2(queue_L, 0, i2s2, 0);
AudioPlayQueue
はステレオを想定して左チャンネルと右チャンネル用で2つ用意しました。
AudioOutputI2S2
は、I2S通信時にOUT2,LRCLK2,BCLK2,IN2,MCLK2が割り当てられたピンを使用します。OUT1,LRCLK1...のピンを使いたい場合はAudioOutputI2S
を使用します。
AudioConnectionで各オブジェクトの接続をしています。
メモリの割り当て
void setup() {
queue_L.setMaxBuffers(2);
queue_R.setMaxBuffers(2);
AudioMemory(4+2);
}
オーディオライブラリでは128byte(約2.9ms)を1ブロックとしてメモリを確保します。
setMaxBuffers()
で、キューのバッファサイズを設定します。
Teensy 4.1の場合はデフォルトで80ブロック割り当てられますが、シンセサイザーとして使う場合はバッファが多すぎるため、今回は最小値の2を設定しています。
AudioMemory()
は、オーディオライブラリ全体で使用するメモリ量を設定します。
今回はAudioPlayQueueが2つあり、バッファサイズがそれぞれ2ブロックなので4ブロックは確定で使用します。
少し余裕を持たせて、6ブロック分設定します。
メモリが足りないと感じたら、200ブロックなど大きな数値を割り当ててから、AudioMemoryUsageMax();
を使用して、最適なメモリ量を探すこともできます。
オーディオの再生
queue_L.play(samples_L, BUFFER_SIZE);
queue_R.play(samples_R, BUFFER_SIZE);
play()
でサンプルデータとサンプル数を渡して再生します。
実行してみる
プログラムを実行した結果、無事に三角波が出力されました。
Discussion