Arduino Touchscreen PWM Sound Output
https://diyelectromusic.com/2021/08/09/arduino-touchscreen-pwm-sound-output/
の #機械翻訳
https://youtu.be/AbL5x0P905Y
ダフネ・オラムに多少インスパイアされて、私がタッチスクリーンで本当にやりたかったのは、波形を描くことでした。これを実現するために必要な部品はすべて揃ったので、このプロジェクトではタッチスクリーンとPWMによる音声出力、そしてスライダー・ポテンショメータから得た「描く」というアイデアを組み合わせています。
警告!実験には古い機器や中古機器の使用を強くおすすめします。高価な機器が故障しても当方は一切責任を負いません!
本プロジェクトで使う主要な Arduino チュートリアルはこちらです:
Arduino PWM Sound Output – Part 2
Arduino MIDI スライダー PWM 波形ジェネレーター
Arduino を初めて使う方は、まず「はじめに」ページをご覧ください。
部品リスト
Arduino Uno
ILI9488 タッチスクリーンディスプレイ&シールド
市販の MIDI モジュールおよび MIDI ソース
(オプション)Arduino PWM 出力フィルター回路 – パート 2
アンプ回路およびスピーカー
回路構成
これは以前作成したモジュールをつなぎ合わせているだけなので、特に新たに「回路図」を示すほどのものはありません。Arduino PWM 出力フィルター回路シールドを使用しており、ディスプレイで使用しているピンの都合上、ピン 9 での PWM 出力ができないため、「Arduino PWM Sound Output – Part 2」で紹介されている手法を応用し、ピン 3 を音声出力用に使っています。
また、Arduino の RX ピンには MIDI 入力を接続し、デモ用としてフィルター後の出力端子に安価なオシロスコープも接続しています。
コード概要
このプロジェクトの目的は、タッチスクリーン上で波形を描画し、その波形を PWM 合成のソースとして、再生中の MIDI ノートに応じた周波数で出力できるようにすることです。
多くのコードは前述のプロジェクトから流用しています。私が最も苦労したのは、ピン9ではなくピン3で PWM サウンドを生成する方法を考え出すことでした。そのため、Timer2 の設定方法と使い方を詳細にまとめたブログ記事のフォローアップとして、その手順を組み込みました(詳しくはこちら)。
“MIDI 波形生成” の基本処理は「Arduino MIDI Slider PWM Waveform Generator」から取っていますが、マルチプレクサのコードやウェーブテーブル生成時の補間処理は不要になったため、大幅に簡略化しています。代わりに「Arduino Touchscreen MIDI Controller」からグラフィック描画ルーチンを流用し、ディスプレイ上でタッチした点から直接ウェーブテーブルを構築しています。
最初の試作では、ディスプレイ座標とウェーブテーブルインデックスを 1:1 で対応させました。動作自体は問題なかったものの、表示領域の大部分が未使用のまま中央の 256×256 ピクセル領域だけを使う形になってしまいました。
そこで最終的には、ウェーブテーブルをディスプレイ全体に拡大・縮小できる機能を追加しました。ディスプレイの解像度が 320×480 なので、Y 軸はスケール 1、X 軸はスケール 1.5 が理想的です。小数点スケーリングに対応するため、すべての表示パラメータを 12.4 固定小数点(整数部12ビット+小数部4ビット)の 16 ビット値として扱い、実際の座標の 16 倍の値を使うようにしています。この変換を簡単に行うために、2 つのマクロを定義しました。
以下に、ディスプレイのスケーリングに用いた関連コードと定数を示します。
code:scale
#define fp2d(x) ((uint16_t)((x) >> 4)) // 固定小数点12.4を整数座標に戻す(16で除算)
#define d2fp(x) ((uint16_t)((x) << 4)) // 整数座標を固定小数点12.4に変換(16を乗算)
#define WTX_MIN (48*16UL) // ウェーブテーブル描画領域のX最小位置(固定小数点)
#define WTX_SCALE 24UL // X方向スケール(1.5*16)
#define WTX_SIZE (384*16UL) // X方向描画サイズ(固定小数点)
#define WTY_MIN (32*16UL) // ウェーブテーブル描画領域のY最小位置(固定小数点)
#define WTY_SCALE (1*16UL) // Y方向スケール(1*16)
#define WTY_SIZE (256*16UL) // Y方向描画サイズ(固定小数点)
#define BORDER 4 // 波形描画枠の太さ
void displayWaveTable () {
// 全体をクリアして枠を描き、現在のウェーブテーブルに合わせて再描画
gfx->fillRect(fp2d(WTX_MIN)-BORDER,
fp2d(WTY_MIN)-BORDER,
fp2d(WTX_SIZE)+BORDER*2,
fp2d(WTY_SIZE)+BORDER*2,
RED);
gfx->fillRect(fp2d(WTX_MIN),
fp2d(WTY_MIN),
fp2d(WTX_SIZE),
fp2d(WTY_SIZE),
BLACK);
// 全 256 エントリを更新
for (int i = 0; i < WTSIZE; i++) {
updateWaveTable(i);
}
}
void updateWaveTable (int idx) {
// 指定インデックスのウェーブテーブル値を画面上に再描画
int drawval = 255 - wavetableidx;
// まず縦線で前回のドットを消去
gfx->drawFastVLine(fp2d(WTX_MIN + idx*WTX_SCALE),
fp2d(WTY_MIN),
fp2d(WTY_SIZE),
BLACK);
// 新しい位置にピクセルを描画
gfx->drawPixel(fp2d(WTX_MIN + idx*WTX_SCALE),
fp2d(WTY_MIN + drawval*WTY_SCALE),
GREEN);
}
void setWaveTable (int xc, int yc) {
// タッチ座標を固定小数点に変換
xc = d2fp(xc);
yc = d2fp(yc);
// 描画範囲外なら何もしない
if (xc < WTX_MIN || xc >= WTX_MIN + WTX_SIZE ||
yc < WTY_MIN || yc >= WTY_MIN + WTY_SIZE) {
return;
}
// X座標→ウェーブテーブルインデックス、Y座標→波形値に変換
int index = (xc - WTX_MIN) / WTX_SCALE;
int value = 255 - ((yc - WTY_MIN) / WTY_SCALE);
wavetableindex = value;
// そのポイントだけ再描画
updateWaveTable(index);
}
displayWaveTable() 関数は起動時に呼び出され、現在の設定に応じてウェーブテーブル全体をクリアして再描画します。
updateWaveTable() 関数は、ウェーブテーブルの特定のエントリに対応するピクセルだけをクリアして再描画します。そのため、テーブル全体を描画するにはこの関数を 256 回呼び出す必要があります。どちらの関数も、12.4 固定小数点形式から実際のディスプレイ座標へ変換を行っています。
setWaveTable() 関数は、タッチイベント検出時に呼び出すよう設計されています。画面上のタッチ位置(X, Y 座標)を受け取り、描画範囲内かチェックした後、X 座標をウェーブテーブルのインデックスに、Y 座標をウェーブテーブル内の値にスケーリングして変換します。そしてそのインデックスだけ updateWaveTable() を呼んで再描画します。
以前のタッチ操作プロジェクトと同様に、ディスプレイの初期化とスキャンには gfxSetup() と gfxLoop() の 2 つの関数を使います。後者 (gfxLoop()) がタッチを検出した際に setWaveTable() を呼び出す役割を担っています。
PWM と MIDI の処理部分は「Arduino MIDI Slider PWM Waveform Generator」とまったく同じですが、PWM 出力をタイマー2 とピン3 で行うために「Arduino PWM Sound Output – Part 2」のコードを流用しています。
GitHub でもソースコードを公開していますので、そちらからご覧ください。
締めくくりの考察
以前からずっと試してみたいと思っていたアイデアです。結果は動画でお聞きいただけます。ひとつ面白いクセがあって、波形を描く際に素早く動かすとピクセルが抜け落ちることがあります。そのため、本来は滑らかな波形にところどころズレた点が散在し、素早く描くと画面上に二重の波形が重なるような効果が得られることもあります。
このおかげで、手描きの波形には独特の「ザラつき」が生まれます。エッジ部分や不規則なテーブルエントリが追加の倍音を生み出し、PWM 出力ではその影響がはっきりと聴き取れます。これにより、生成できる音色は非常に複雑になります。
フィルターなどでさらに加工するオシレーター源として使うのも面白いでしょう。
このアイデアは、1960年代にフィルムに手描きで音を描いていたダフネ・オラムの「オラミクス」に大いに触発されたものです。今では市販の部品を組み合わせるだけで、まさに同じことがデジタルで実現できるのです!
また、これを利用してシンセサイザーの他のパラメータを制御するのも非常に興味深いです。エンベロープを描くのは明白な応用例ですね。画面に一度にいくつ並べられるか試してみたいです。
— Kevin