公開日:2023/06/24
title.png

26th Season


#26-2 アナログPWMで音楽を奏でる

 また、新しい言葉を作ってしまったのですが……、今回のテーマはアナログPWMという技術です。この技術を使って、MMLを使った音楽演奏プログラムを作成してみようと思います。

アナログPWMとは?

 本ページでは、まず、これまで使って来た通常のPWMを、便宜上「バイナリPWM」と呼びます。

 バイナリPWMは、出力電圧値がLOW/HIGHの二値しか取りません。デジタルI/Oポートを使うため、「二値しか取れない」と言った方が近いです。

 今回使うのは、出力電圧が0〜3.3[V]間のアナログ値を取るPWMなので、これを「アナログPWM」と称します。下図にイメージ図を示します。

a262_anlgpwm.png

 この様な特徴を持ったアナログPWM出力を使って、音を鳴らして行きます。

PWMで音を鳴らす

 本HPに於いて、一番初めに音楽を演奏した方法は、バイナリPWMを低域通過フィルタ(LPF、CR回路)に通して三角波を作り、これをアンプ内蔵スピーカに出力した、以下の記事です。

  「#5-8 MMLで音楽を奏でる

 PWM出力(方形波)をLPFに通すことによって、以下図の様な三角波を作り出し、これを音声波形としてスピーカやイヤホンから鳴らしました。

ar58_lpf.png

 LPFを使ったサウンド出力回路(下図)では、音量を決定するのは回路定数である抵抗R1とコンデンサC1の時定数C1×R1です。上図の三角波は、時定数CRの取り方によって三角波の振幅が増減することを実際に測定して調べた実測値です。容量C1=10μFに固定の上、R1を変化させて観測しました。

ar58_schem.png

上回路図の時定数は目安です。アンプ内蔵スピーカ(またはイヤホン)に出力してみて、音量が大きすぎる場合は時定数を大きく、音量が小さすぎる場合は時定数を小さく、調整してください。イメージ的に、R1=10kΩは大きすぎる気がします。R1=1kΩ辺りから調整、で、良いのではないでしょうか?

 なお、LPFを通す前のバイナリPWMを、ダンピング抵抗を通してパッシブブザーに接続すると、上の様な回路やアンプ内蔵スピーカを使わなくても、手軽に音を鳴らすことができるので、そちらの手法も多用してきました。

音量の制御

 音程は、出力するPWMの周波数に従った音程となるので、MMLを使って、音楽を演奏することができます。ここまでは、従前のバイナリPWMでも可能でした。
 今回のテーマである「アナログPWM」は言い換えると、ソフトウェアで動的に音量を制御するテーマと言うことができます。

 音量は、電圧波形の振幅で決まります。三角波の振幅の大小が、音量の大小と対応します。
 前節のサウンド出力回路を通すと、PWM(矩形波)が、適切な振幅を持った三角波(CR充放電波形)となります。三角波の振幅電圧は、元のPWMの振幅電圧と、時定数C1×R1で決まっています。
 ところが、振幅値はサウンド出力回路の回路定数(C1,R1)で固定だし、バイナリPWMでは、電源電圧に起因する0Vと3.3Vの二値しか出力できないため、PWMの振幅電圧も固定値です。
 つまり、この回路構成では、ソフトウェアで動的に音量を制御することはできないのです。

 そこで、アナログPWMが登場します。
 こちらの図に示した様に、アナログPWMは0〜3.3[V]間のアナログ電圧を出力できるため、アナログPWMの振幅電圧を絞ると音量は小さくなるし、振幅電圧を大きくすると音量も大きくすることができるのです。

アナログPWMを出力する

 普通の汎用I/Oポートですと、LOW/HIGHの二値しか出力できないので、アナログ電圧を出力する為にDA変換器を使います。今回は、DA変換器を2チャンネル有するArduino DUEを使用します(下図)。

a119_due.jpg


 バイナリPWMなら、タイマユニットなどのPWM出力機能を設定しておくと、一々CPUが介在しなくても、自動的に所望のPWMを出力可能です。
 アナログPWMは、どうでしょうか?現時点では、アナログPWMを出力可能なI/Oユニットは見当たらないので、以下の様にして生成します。


 DUEで三和音の音楽演奏した時(以下リンク)のプログラム(play_triad.ino)をベースに変更して行きました。

  「#13-2 MMLで三和音を演奏する

 三和音演奏では、モータ制御などにも応用可能なPWMユニット(DUEのマイコンSAM3X8Eに搭載)で、デューティ50%のバイナリPWMを出力して、音を鳴らしました。この時もタイマの周期割り込みを起こし、割り込み処理(ISR)にて音階の演奏時間を制御したため、バックグラウンド演奏が可能なプログラムとなっていました。

 今回は、アナログ電圧のPWM出力が必要なので、周期的なタイマ割り込みを利用して、割り込みの度にDA変換器出力をanalogWrite()関数でトグル(変化)させることにより、ソフトウェァによるアナログPWM出力も実行します。
 つまり、アナログPWMで音楽を演奏すると共に、バックグラウンド演奏も可能なプログラムとなります。

振幅の制御

 アナログPWMを使い、演奏する音符毎に、演奏開始時間からの振幅の変化を以下図の様に変化させてみます。

a262_profamp.png

 上図の横軸は、音符の演奏開始から、全音符の演奏終了時間までの時間変化、縦軸は、バイアス値(中央値)からの振幅を取っています。上図に従って、PWMをバイアス(=3.3÷2[V])から上下に振幅させると、上下に折り返した鏡像のPCM波形となります。

 上図の波形は、MIDI等の奏でるピアノ音のPCM波形などから決めた振幅プロファイルです。

※振幅、すなわち音量のプロファイルを合わせただけなので、「ピアノみたいな音に聞こえる」わけでは、ありません。が、ピアノなどの音量の変化を真似したもの、と言うことができます。

 上図から、縦/横方向共に256段階量子化した、以下のようなテーブルを作って、プログラムに組み込みます。

  const uint8 ply_prof_vpp[ ]=
  {
    255, 255, 255, 255, 251, 251, 251, 251,
    251, 251, 251, 251, 247, 243, 239, 231,
    219, 207, 203, 199, 195, 195, 191, 191,
    :
    : (中略)
    :
    4, 4, 4, 4, 4, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0
  };


 このテーブルを参照しながら、割り込み周期毎にアナログPWMをトグルすると同時に、振幅(すなわち音量)を制御します。

どんな効果があるのか?

 バイナリPWMを使って音を鳴らす場合と比べてアナログPWMを使った場合に、音楽の演奏に関して、どのような効果が見込めるのか?と、言うことが一番気になりますよね?以下図を再掲して、解説を続けます。

a262_anlgpwm.png

 後の実験で聞き比べてもらいますが、油断すると差が判らない方も居るかもしれないので、上の様な点に注意して聞き分けてみてください。

プログラム

 それでは、完成したプログラムを以下にリンクしておきます。

  完成プログラム:play_mml_dac.ino

 Arduino IDEでコンパイルして、Arduino DUEに書き込んでください。シリアルモニタから、以下のコマンドを受け付けています。

  シリアルモニタコマンド仕様: こちらを参照してください。

 使用可能なMMLの仕様はこちらです。

 なお、このプログラム(play_mml_dac.ino)では、テーマである、アナログPWMをDA変換器のチャンネル1(チャンネル0では無い……)から出力しています。と、同時に、今まで音楽演奏に使用していた、バイナリPWM版との音の比較ができるよう、バイナリPWMもD36ピンから出力しています。
 DUEのピンヘッダ位置を確認して、DAC1ピンとD36ピンを切り替えながら、音の違いを確かめてみてください。

違いの判る実験

 実験で動作検証と音の違いを確認して行きます。

 ブレッドボードなどに、こちらのサウンド出力回路を構築して、以下表の様に、DUEとアンプ内蔵スピーカ(またはイヤホン)と接続してください。

Arduino DUE 接続 サウンド出力回路 接続 アンプ内蔵スピーカ
(またはイヤホン)
GND GND  
DAC1(※) D9/D11
  ミニジャック(J1) 入力端子

※以下実験の中で、バイナリPWMの音と比較確認する場合は、D36端子に繋ぎ変えます。
※回路図のC1=10μF、R1=1kΩとして、時定数CR=10ms程で実験しました。各自、音量を確認して、調整してください。



 以下図に実験中の様子を示します。

a262_expr.jpg


 上の写真には写っていませんが、まずは、Logiscopeを実行したSTM32を接続して、アナログPWMの出力波形を確認しておきます。STM32とDUEのGND同士、および、DUE.DAC1 → STM32.A0をジャンパ線で接続して、以下の実験を行いました。

  実験サンプル:【 】内はSTM32側Logiscopeからの送信データ
  【osc,0,32,4,0】

a262_logosc.png

Logiscopeのオシロスコープ機能【oscコマンド】で観測可能な様、256ms程の極短い音を鳴らした時のキャプチャ波形です。

 DUEから2音鳴らして【,T256,A31,A41】観測した実際のキャプチャ波形です。

 ただし、汎用I/Oポートと違って、アナログ(中間)電圧を出力可能なDA変換器の分、電流駆動能力は限定されているはずです。PWMの周波数が上がってくると、想定した電圧まで上がりきらないことも想定できます。周波数が上がる → 音程が高くなる、事なので、あまり高い音を鳴らすのは得意ではないかも、知れません。


 続いて、アナログPWMによって奏でられる、音に注目して実験を進めましょう。

  実験サンプル:
  【,T1000,C41,D41,E41,F41,G41,A41,B41,C51】
  OK: short-cut (0)

 全音符の長さを1s【T1000】に設定して、「ドレミファソラシド」を演奏してみました。スピーカ(またはイヤホン)から音が聞こえましたか?
 着目すべきは、1音毎の音量の変化です。
 1音の鳴り始めは大きな音量で、鳴り終わりにかけて音量が低下して行くことを確認できましたか?


 今回のプログラム(play_mml_dac.ino)では、アナログPWMとバイナリPWMを聞き比べられる様に、D36端子からバイナリPWMも出力してあります。サウンド出力回路に接続する信号を、DUEのDAC1出力から、D36(PWMチャンネル1出力)に接続変更して、実験してみましょう。

  【,T1000,C41,D41,E41,F41,G41,A41,B41,C51】
  OK: short-cut (0)

 今度は、鳴り始めから鳴り終わりまで、一定の音量で鳴り続けていたはずです。違いが判りましたか?


 以下動画で聞き比べていただけますので、是非、聞き分けてみてください。



 前の節で解説した音の違いを、予めご確認の上で、それでも違いが判らなかった方にとっては、アナログPWMは無用な技術でございました……m(1;_;)m。でも、違いをお判りいただけた方には、その意義をご理解いただけたものと確信します。


 その他、MMLで記述した音楽の演奏については、こちらの記事でご確認ください。いくつかサンプルのMMLも取り揃えています。

壊してしまった

 DUEのDA変換器チャンネル0出力が壊れてしまいました。

 大失敗です。DUEにはDA変換器出力が2チャンネル備わっているので、「二和音の演奏に対応できるかも?」等と目論んでいたのですが、チャンネル0出力が壊れてしまったので、それは叶いませんでした。

  ガックシ💔orz。

 完成プログラム(play_mml_dac.ino)をご覧になって、「何でDA変換器チャンネル1(0ではなく)を使ったんだろ?」と、不思議に思った方も居るかもしれませんが、チャンネル0が壊れて使えなくなってしまったから止むを得ず、の対処です。


 DA変換器出力1本を壊してしまったせいで、将来を見据えると、色々なアイディアの実現可能性を窄めてしまった気がする……。ショック。


btn_arduino.png

btn_prev.png btn_next.png

home