電子楽器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セットを含め、ファームウェアを更新しました。基板頒布のリクエストは引き続き受け付けています。欲しいという方はぜひお知らせください。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です