10Wアンプに電圧電流計を内蔵してみた

以前作った、秋月10Wモノラルアンプ内蔵のスピーカーボックスに電圧電流計を内蔵してみました。

10月25日(日曜)に流通センター(平和島とか大森に近い海沿い)で開催されていたM3に行ってきたので、そのついでに久々にアキバへ。アキバを訪れるのは7月末以来3ヶ月ぶりだったので、8月に新装オープン後の秋月電子に初めて寄ったのですが、店内がだいぶすっきりと一直線になり、混雑の中でも移動しやすくなっていました。

近くにはこんな懐かしいものまで。
三月兎の店舗の一つ(猫鍋が延々と流れていた店)が鈴商の向かいあたりにあったのですが、最近閉店し、さらにその後、隣のLivinaヤマギワなどがあったビルが解体されたことで、四半世紀…いやそれ以上前からあった亜土電子の看板が!
確かに、80年代後半、ちょうど大学生くらいの頃、ここに亜土電子がありました。
Z80マイコンCPW-1の基板とか、ここでよく買ってました。
これはちょっと外すのがもったいないくらい懐かしい!
アキバ遺産として残してほしいくらい。

すっきりとした秋月で、つい衝動買いしちゃったのがこれ。
LEDデジタルパネルメータ 電圧・電流同時表示 DE-2645-02
今月発売されたばかりらしく、これが店先にたくさん並んでいました。
「電気的の知識」などという微妙な日本語が書いてあるところが「MADE IN 台湾」を感じさせますww

火曜日に早く帰れたので、ハンドドリル、ナイフ、ニッパー、やすりを駆使してこのようにくりぬき。

で、つなげてみると、電圧電流計のLEDダイナミック点灯によるものと思われる、ビーーーー っていうノイズが。

何が起きてるんだろう?と思って、翌日以降、よくよく考えてみたら、

電源からのGNDを、前面パネルのオーディオジャック近くのGNDラインに直結し、そこから1本にまとめて10Wアンプ基板のGNDに配線していたことが原因っぽい。試しに電源からのGNDを10Wアンプに近いGNDに直結するよう切り替えてみたら、劇的に改善。

そこで今日、つまり土曜日のハロウィンの日に電源回りを中心に配線の全面見直し。

まぁ、ボリューム最大にすると、ノイズはまだ聞こえますが、なんとか気にならないレベルにまで軽減することができました。

これでまた、この前みたいに太陽電池だけで動かしたら、電流や電圧が見えてもっと面白いかも。ますます日が短い時期になるけど、暖かいうちにまた実験しようかな…?w

太陽光だけでCAmiDionを演奏してみた

土日が雨に見舞われた3連休の最終日、10月12日は快晴。

せっかくの天気なので、余った太陽電池を活用し、太陽エネルギーだけで電子楽器CAmiDionと10Wアンプ(半年前に作ったもの。このブログを10Wアンプで検索するとわかります)を駆動し、演奏できるか実験してきました。

もともとこの太陽電池、ほぼ1年前に秋月電子で買ったソーラーLEDドライバICの余った分を有効活用しようとして使っていたものでしたが、ICに負荷を掛けすぎて壊してしまい、結果として、最大1.15W(5v x 230mA)の全く同じ性能の太陽電池が2枚余ることに。これも、もともとは秋月で買ったもので、性能的にはこの商品とほぼ同じです(大きさは若干違うかも知れません…なんか処分品だったような気がするので)。

5v x 2枚ならば、NiMH x 8 本で駆動していた10Wアンプの電源としてそのまま使えるだろうと思い、NiMHをつながず、太陽電池だけで試してみたら…

ちゃんと動作しました!

さっそく、横浜・山下公園で実験。14:00前にたどり着き、15:00くらいまで演奏して楽しんでいたら太陽が途中でビルの陰に入り、日差しを求めてだんだん赤レンガ倉庫方面へ移動しながら、象の鼻テラスの屋上まで行き、ソーラーパワーだけで16:00頃までCAmiDionを駆動できました。

そのときの様子を動画にしました。
niconico版もあります

ところで、このアドリブギターっぽい演奏のBGMですが、
実はこれもCAmiDionで演奏しています。
まるでギターを弾いてるような感じで演奏できる、そんな方法を編み出したのです。
どうやるかというと…

  • 波形(Waveform)にGuitarを選ぶ(8桁なので Ch1:Guit しか出ませんがこれでOK)
  • エンベロープは a2dBs0rB ぐらいにする。Attackちょっと短め、DecayとReleaseを長めにするのがポイント。(ちなみにこれは Attack=2、Decay=11、Sustain=0、Release=11、という意味。数値は0~15の範囲で、10=A、11=B、のように16進で表示して8桁に収まるようにしています)
  • “Chord”ボタンで和音モードを解除して単音モードにする
  • sus4ボタンとメジャーボタンの同時押しで6音使ってコードを鳴らす。例えばコードCならば、C,G,Eのボタンと、それぞれのsus4ボタンを同時に押す。

これで、ギターの6弦を手で鳴らすような分散和音の雰囲気で、本物のギター演奏にぐっと近い感じになります。Cのコードフォームを左右どちらかに1つだけシフトすれば、FやGのコードも簡単です。AmもC,A,Eのボタンで同じように鳴らせますから、マイナーコードも簡単です。

この方法で弾いていると、まるで本物のギターを弾いている気分になり、なかなかはまる。しかも、山下公園と赤レンガ倉庫のそれぞれで、2歳前後とみられる幼児が不思議そうにこっちを見てるという、そんな光景に出くわしました。
ひょっとしたら、ギターがないのにギターの演奏が聞こえたことに不思議がっていたのでしょうか…?

PWMDAC_Synthの配列生成マクロ

少し前のブログ記事で、PWMDAC_Synthライブラリにおける音階の作り方を解説しましたが、そのためにどんな関数を使っているか、といった計算式の説明が中心でした。

この計算式は、AVRマイコン上でランタイム(実行時)に動かすためではなく、PC上でのコンパイル時にC++のプリプロセッサで字面の置き換えをして配列を生成するためのものです。

従来は

#define F(x) …
PROGMEM const byte table[] = {F(0), F(1), ... , F(127)};

のように0~127まで書き並べる方法で波形テーブルを作っていましたが、新しいPWMDAC_Synthではよりスマートなこの方法に切り替えました。

#define FX2(f,x) f(x), f(x + 1)
#define FX4(f,x) FX2(f,x), FX2(f,x + 2)
#define FX8(f,x) FX4(f,x), FX4(f,x + 4)
#define FX16(f,x) FX8(f,x), FX8(f,x + 8)
#define FX32(f,x) FX16(f,x),FX16(f,x + 16)
#define FX64(f,x) FX32(f,x),FX32(f,x + 32)
#define FX128(f,x) FX64(f,x),FX64(f,x + 64)
#define ARRAY128(f) {FX128(f,0)}
#define ARRAY256(f) {FX128(f,0),FX128(f,128)}
:
#define PWMDAC_CREATE_WAVETABLE(table, function) PROGMEM const byte table[] = ARRAY256(function)

FXn(f,x) で、n/2 個の要素を2倍に増やし、n 個の要素を持つf(x)の並びを作る、ということを7回繰り返します。たったこれだけで 27 = 128 個のカンマ区切り数値の並びができてしまいます。

これを活用して作ったマクロ ARRAY256(function) や ARRAY128(function) を使えば、その引数 function にマクロ関数名を入れるだけで、関数の結果を使った配列が簡単に作れます。

それをさらにマクロにしてまとめれば、ライブラリを呼び出す側でそのマクロを指定するだけで、インスタンス(実体)を重複なく必要な分だけ作れるようになります。タイマー割り込み処理に欠かせない ISR() (Interrupt Service Routine) の定義もインスタンスとみなして一緒に定義することすら可能になります。

最初の頃はExcelのワークシート関数で配列初期化のソースを作らせていたのですが、このような計算を#defineでC++のプリプロセッサに任せることだってできるのです。

ただし、一つだけ注意点が。
この方法を使ってノイズ波形を作るために random() を使った乱数表を作ろうとすると、なぜかうまくいかないようです。コンパイルして実行したら動きませんでした。
random() は呼び出すたびに値が変わるため、実行時に値を作るようにコンパイルされてしまったのでしょうか…?
このような特殊なデータはExcelに作らせるか、エディタを使って手動で作るしかなさそうです。

電子楽器CAmiDion ソフトウェア更新・その2:パフォーマンスチューニング

前回もこのブログでCAmiDionとPWMDAC_Synthの更新をアナウンスしましたが、あのあとまた更新が色々入りました。

チューニングの考え方

今回の主な更新は、リファクタリングはもちろん、それによって判明したパフォーマンスチューニングすべき箇所の修正による、使用RAM領域の節約が中心です。Flashメモリであるプログラム領域については、削減できればいいなと思いつつ、余裕があることや速度を犠牲にしたくないこともあり、それほど重視はしませんでした。

節約の際は、速度を犠牲にしないよう、以下の3つの処理を常に意識しておく必要があります。

  • 割り込み処理ISR()でその瞬間の波形ポイントをPWMのパルス幅にして出力(最高頻度・最優先:できる限り手早く処理しなければならない)
  • エンベロープ音量更新(loop()から定期的に呼ばれるので、あまり遅い処理は禁物だが、頻度が高くないので上記ほどシビアではない)
  • 通常の処理(これもシビアではないが、あまり遅いと全体の処理に影響するかも)

主なリファクタリング/パフォーマンスチューニング項目

PWMDAC_Synth

  • ADSRのenumを外出ししたうえで、エンベロープパラメータをよりコンパクトな形式に変更
  • 音量を16bit/8bit両方持たせていたのを共用体に置き換えた
  • 位相速度(dphase)を構造体にまとめてリファクタリングした。これによりオリジナル値を保持するのが無駄なことが判明したので、保持するのをやめた
  • Voice(発声子)の優先度(「温度」と呼ぶことにした)を見直し、音量をより重視した

CAmiDion

  • ソースを整理し、使えそうな箇所でtemplateを使うようにしたり、ボタンIDのenumを外出しすることで型の明確化に役立てた

エンベロープパラメータ

これまではbyteとunsigned intを両方使っていましたが、16ビット分という細かい値までMIDIチャンネル16個分保持するのはRAMの無駄になることなどから、すべて8ビット(= 1 byte)に統一しました。これに伴って配列化も容易になったので、ADSRのenumを使ったbyte配列として参照や更新が出来るようにしました。これに伴い、値だけでなくメソッドを持たせられるよう、struct から class への「昇格」も行いました。

音量、位相速度のコンパクト化

発声状態(VoiceStatus)には、現在の音量と位相速度(phase speed、音の高さに比例)が常に保持されていますが、これらを保持する変数のサイズ削減も行いました。発声状態はデフォルトで6重和音分あるので、ここのサイズ削減もRAMの節約に役立ちます。

【音量】最終的には高頻度割り込み処理ISR()にて8bitで掛け算されますが、エンベロープ状態の更新はそれに比べて低頻度なので、より細かい16bitで保持しています。今まではこの2つの値をvolume8とvolume16という別々の変数で持たせていたので3バイト占有していました。
しかしよく見ると、8bitで音量を得るには16bitの上位8bitをそのまま参照すればいいことに気づきます。そこで、この部分を共用体(union)で定義し直しました。これにより2バイトの占有で済むようになるだけでなく、8bitシフトしようとして >> 8 とか書かなくても共用体メンバーを参照すればよくなり、ソースが読みやすくなります。

【位相速度】ピッチベンド前の値(dphase original)はNoteOnやピッチベンドのタイミングでその都度wavetableから読み出せばいいことがわかったので、なくしました。位相速度はunsigned longで4バイトも食っているので、これも削減できました。ISRの割り込みに比べて低頻度なので、その都度読み出しても処理速度への影響は少なく、RAMの節約効果が大きいです。

発声優先度の見直しで「温度」の概念を導入

同時発音数いっぱいに発声中でも、受信したNoteOnを無視するわけにはいきません。反応が鈍ったように感じてしまうからです。

これを解決するのが優先度の概念ですが、内部的にはこれを「温度」(temperature)と呼ぶことにしました。ADSRのAttackでは音量が上がるほど下がり、Decay以降は音量が下がるほど下がる、そのような値を「温度」としています。発声が始まったときが最高温度(最も熱い状態)で、ADSRのReleaseが終わって音が止まったときが最低温度(最も寒い状態)になる、という考え方です。こうすると、同時発音数が足りなくなったとき、温度の低い(=寒い)ものを優先的に「横取り」することで、急に音が止まったような感じを抑えることができます。

最初はADSRの状態を優先していましたが、Attackとそれ以外で分け、あとは音量を重視するようにしました。このほうが急に音が止まったような感じがしにくいからです。

templateの活用

CAmiDionのソースでは、ノート番号(byte型)とコード(Chordクラス)が違うだけのメソッドがあるので、この違いを吸収すべくC++のtemplateを初めて活用しました。

同時に、ボタンIDをbyte型からButtonIDというenum型にし、これを広く活用する形にして型を明確化しました。これによりソースが読みやすくなったと思います。

手持ちのCAmiDionハードにも反映しました

以前よりチューニングの効果が出たので、頒布待ちの在庫10セットを含め、ファームウェアを更新しました。基板頒布のリクエストは引き続き受け付けています。欲しいという方はぜひお知らせください。