RSS Twitter Facebook


« 2020年06月 | 2020年07月のアーカイブ | 2020年08月 »

2020/07/29

Appleがハードウェア寄りなAPIの実装を拒否している件


Web MIDI、Web Bluetooth、Web USB、Web Serial...

と近年ハード寄りなブラウザ API がどんどん出てきていて、そこそこのものなら(ハードウェアの能力を限界まで絞り出すようなネイティブアプリがある以上完全に、とは言わないけど) Web アプリで済む世界は近いと思ってはいるのだけど、その一方でこういう話もあるのだよね。

元記事は6月末の頃のもののようだ。

厳しいなあ。
Gigazine : 「Appleが「プライバシー上の懸念あり」としてSafariへの一部ウェブAPIの実装を拒否」

面白そうな API が軒並み実装拒否対象になっている。まあ Web USB なんかが出てきた時に Web アプリからそんな所を触って大丈夫なのか? と多少は思ったというのも確かにあるけどね。Web MIDI は許して欲しいな、勝手な希望なのは承知しているけど。

対象の Web API は以下の通り :

・Web Bluetooth
・Web MIDI API
・Magnetometer API
・Web NFC
・Navigator API: deviceMemory
・NetworkInformation API
・Battery Status API
・Web Bluetooth Scanning
・AmbientLightSensor API
・EME Extension: HDCP Policy Check
・Proximity API
・WebHID API
・Serial API
・WebUSB
・Idle Detection

posted by g200kg : 11:13 AM : PermaLink

2020/07/26

ノブのラベルはどう配置するべきか


特に気にしていない人には本当にどうでも良い話だとは思うのだけど、GUI画面のツマミの周辺の配置をどうするべきかちよっと迷ったりする事が多いんですよね。つまり、下の図のラベルとリードアウトをノブに対してどこに配置するかという話です。

幾つか考えるべき点はあって、まず実際のハードウェア機器ではリードアウトは普通存在しないので、ノブとラベルの位置関係だけになります。これがどうなるかというと、世の中の趨勢としても個人的な嗜好としても、家庭用オーディオやラックマウント機器のようなものならラベルはノブの上、小型のガジェットや鍵盤付きシンセのような平置きして使うものなら下、かなあ。絶対ではなくてあくまで傾向ですけどね。

というのも実際のハードウェア機器だと視線の問題があって機器の形によって見下ろす視線で使うものと見上げる視線で使うものでノブの陰にならないようにしないと使いづらいという事情があります。

ソフトウェアGUIになるとどこに置いても陰にはならないのでこういう呪縛がなくなるわけですが、ハードウェア機器へのリスペクトの高さによって、傾向としては残るんでしょうね。ハードウェアエミュレート系のソフトウェアならなおさらです。

そして現在のパラメータを表示するリードアウトですが、エミュレーション系ならそんなもの付いてないのが当たり前なんですが、せっかくのソフトウェアのメリットでもあるので個人的には付けたくはなりますね。

という事で身の回りの音楽系プラグイン等を数十程度ざっと見てみたのですが、まずラベルがノブの上に来るのか下に来るのかという事で言えば、下派がやや優勢で、上40%、下60%程度かと思います。やはり家庭用オーディオ機器よりもシンセやストンプエフェクターなんかの方が近い存在なんですかね。

そしてやや意外だったのが、リードアウト付きの比率が結構低いようです。リードアウト付きは全体の20%~30%程度でしょうか。付けても機能的にマイナスになる事は基本的に無いと思いますが、これはやはり楽器系ソフトウェア特有のハードウェアリスペクトの高さによるものなのでしょうか。

それで全体の2~3割の選択として、リードアウトを付けるとすると、ラベルとの配置の兼ね合いが割と厳しくなります。上か下ははともかく、ラベルとリードアウトを同じ方向に付けると下の図のようになるわけですが、やっぱりちょっとごちゃごちゃした感じになってしまいます。実際こういうパターンのもそれなりに存在するし駄目ではないんですが。

という事で結局ラベルとリードアウトをそれぞれノブの上と下に振り分けるというパターンが無難な感じにはなるんですが、それで上下にどう振り分けるかとなると、どっちを重視するかみたいな話で、どうするのが良いかもうわからない。ただ個人的にはラベルを上、リードアウトを下という方向でなんとなく収束しつつある気はしています。

大して変わらんと言えば変わらんのだけど、ただ、1画面内でラベルが上にあったり下にあったり混在しているのは流石に避けるべきかなとは思います。

posted by g200kg : 2:31 AM : PermaLink

2020/07/20

webaudio-controlsのフロントページを作りました


今まで GitHub にそのまま置いていただけなのですが、ちょっとそれらしい形にしました。

そしてアプリケーションノート的なドキュメントも書こうとして色々と実装が不完全な所が露呈して結構本体にも手を入れる事に。@atsushieno さんのKnobのサイズの件とかも。まだ完全に理想通りではないかもだけど。

ついでに npm パッケージ (g200kg/webaudio-controls) にしてあります。なお npmjs ではなくて GitHub Packages なので npmjs しか使っていない人は少しだけ npm の設定が必要になります。

https://g200kg.github.io/webaudio-controls/docs/index.html

posted by g200kg : 2:03 PM : PermaLink

2020/07/08

ADSR がチョットワカルようになる記事



これまでに ADSR 関係の記事を幾つか書いてきたのだけど、ADSR についてはまだ言いたい事があるのだ。あなたの ADSR の実装、間違ってないだろうか? と。ADSR はシンセを構成するモジュールとしては簡単な方だし、ソフトウェアで実現するのもそれほど難しくはない。が、ここで説明する点は特に勘違いしやすいので注意して欲しい。

ADSR はざっくりと下の図のような説明がされている事が多い。ADSR の概要を説明するにはこれで問題はないのだけど、この図を見て、「完全に理解した。アタックのピークを過ぎた後は、減衰の曲線が Key On の間は Decay で Key Off になると Release に切り替わるんだね」と言う風に理解してしまうとまずい事になる。


次のような設定にした時を考えてみる。
Decay ⇒ 小、Release ⇒ 大、Sustain ⇒ 小。


音の出始めにガツンと強いピークがあり、そのまま消えるのではなく、その後も薄く鳴り続けリリースの消え際も長い、という感じの音だ。これでアタックのピーク近くの瞬間にキーを離してしまったらどうなるか...。

「Key Off になるとディケイからリリースに切り替わる」だと、緑の線のようになりますよね。これに違和感があるのは明らかでしょう。アタックの一瞬だけのはずだった音量でしばらく鳴り続けるのだから。
正解は次の図

紫の線のように KeyOff の後、Sustain レベルまでは Decay の速さ + Release の速さで減衰しないといけない (減衰は漸近線なので、Decay の減衰カーブだけの場合は Sustain レベルに辿り着けないのはわかると思う)。

また Attack のピークに至る以前に Key Off が発生した場合も同様で、次の図のように Key Off 時点の現在値が Sustain レベルより高い間は Decay の減衰 + Release の減衰、現在値が Sustain レベル以下ならば Release の減衰が適用される。

つまり Decay の減衰の発動条件は、
 「Attack フェーズ以外で、現在の値が Sustain レベルより高い時」であり、
Release の減衰の発動条件は
 「Attack フェーズ以外で、現在のキーの状態が Off の時」なのだ。
そしてこの二つの減衰は重なり合う事もある。
また Attack フェーズとは、
 「Key On でトリガーされてから現在値を増加させて行き、ピーク値に到達する、または Key Off になるまで」である。



if( detectKeyPositiveEdge )
    AttackPhase = true
if( CurrentValue >= PeakLevel or Key == OFF )
    AttackPhase = false
if( AttackPhase ){
    increase CurrentValue by AttackRate
}
else{
    if( CurrentValue > SustainLevel )
        decrease CurrentValue by DecayRate
    if( Key == OFF )
        decrease CurrentValue by ReleaseRate
}


これだけの事で以前書いた リトリガーの問題 も解決する (そして残念な事に、このように現在の値を基に曲線をトリガーしたり、複数の減衰曲線を重ねたりしようとすると Web Audio API のオートメーション関数ではとても面倒な事になるのだ)。

ハードウェアで作る時は大体参考にする公開された回路図通りに作る事から始めるケースが多いので間違える事は少ないのだが、ソフトウェアでは最初の ADSR の波形を見ながらスクラッチから自分で実装しようとして失敗する場合があるので気を付けて欲しい。

posted by g200kg : 9:31 PM : PermaLink

新しいWebSynthを作ってみた (WebGrowler)


先日 AudioWorklet で ADSR を出力するノードを作ったのですが、これまでに他にも楽器系に使える各種ライブラリを作ってきたのでそのあたりを全部まとめて使ってみようか、という事で作ってみました。

WebGrowler という名前です。コンセプトとしては、「適当にツマミをいじってもそれなりに派手な音がする」です。audioworklet-adsrnode は ADSR をノードの出力として出せますので、音信号だけでなく制御系信号をノード間の connect() で処理できるのが特長です。これを活かすためにエンベロープと LFO をソースとする大き目のモジュレーションマトリックスを付けてあります。

使用しているライブラリは次のとおりで、どれも GitHub に置いてあります。

GitHub Repository : https://github.com/g200kg/webgrowler

デモページ : https://g200kg.github.io/webgrowler/demo.html

posted by g200kg : 2:15 PM : PermaLink

2020/07/04

デノーマル問題は今どうなっているのか



もう今から 10 年近く前の話になりますが、ソフトシンセ等の楽器系ソフトウェアではデノーマル問題というのがちらほらと起こっていました。これは扱うデータが通常の浮動小数点では表現できないほど小さい ( 0 に近い) 状態になると、単に 0 に切り捨てるのではなくて CPU がデノーマルモードという特殊なモードで近似的に扱う代わりにパフォーマンスが極端に悪い状態になる事を指します。

ソフトウェアのジャンルによってはこれでも特に問題はなかったのですが、楽器系ソフトウェアはリアルタイム処理が命で処理が間に合わないとノイズが発生する等の結果になったりしますので特に敏感だったわけです。そしてこの問題はネイティブアプリだけではなく、Javascript 上でも同じように存在していて Web アプリでシンセを作ろうとするとやはり気にしないといけなった、というのが大体 9 年前。↓の記事でブラウザ上のデノーマル問題について少し検証しています。

「Javascriptでもデノーマル問題はあるんだよ!」

その後、Chrome のバグトラッカーにこの問題があがって対策されたのが 5、6年前くらい。これでもう Web アプリでデノーマルに悩まされる事は無くなるのかなー、やったね。
という事があって、もうデノーマル問題の存在すら忘れかけていたのですが、今になってちょっと引っかかった問題をきっかけにこのあたりを掘って見ると、見つけてしまいました。デノーマル再来。

問題は TypedArray です。

倍精度の場合は
\( 2.22 \times 10 ^{-308} \)、

単精度の場合は
\( 1.17 \times 10 ^{-38} \)

あたりよりも 0 に近くなるとデノーマルモードに入ります。Javascript の通常の変数の場合はすべて倍精度浮動小数点として扱われ、10のマイナス308乗あたりよりも小さくなるとデノーマル状態になりますが、どうやら何らかの対策が行われていて特にパフォーマンスが落ちるという事はないように見えます。しかし、これが TypedArray、Float32Array や Float64Array の場合は、昔のようにデノーマル状態でパフォーマンスが 5~6 倍程度落ちるようです (昔よりはパフォーマンスのペナルティは低くなっているようですが)

下に検証用のコードがあります。javascript の通常の変数、Float32Array、Float64Array についてそれぞれ変数の値に 0.1 を掛けて行き、時間を測定しています。
これをそのまま Javascript コンソールに貼り付ければ実際に結果を見る事ができます。


for (f = 0.1; f != 0; f *= 0.1) {
  Time1 = performance.now();
  for (var i = 0; i < 1000000; ++i) {
      result = f * 0.1;
  }
  Time2 = performance.now();
  document.write((Time2 - Time1)+"mSec , "+result+"<br/>");
}
document.write("Loop Exit!! (Normal Variable) <hr/>");

a=new Float32Array(3);
for (a[0] = 0.1,a[1] = 0.1; a[0] != 0; a[0] *= a[1]) {
Time1 = performance.now();
for (var i = 0; i < 1000000; ++i) {
a[2] = a[0] * a[1];
}
Time2 = performance.now();
document.write((Time2 - Time1)+"mSec , "+a[2]+"<br/>");
}
document.write("Loop Exit!! (Float32Array) <hr/>");

b=new Float64Array(3);
for (b[0] = 0.1, b[1]=0.1; b[0] != 0; b[0] *= b[1]) {
Time1 = performance.now();
for (var i = 0; i < 1000000; ++i) {
b[2] = b[0] * b[1];
}
Time2 = performance.now();
document.write((Time2 - Time1)+"mSec , "+b[2]+"<br/>");
}
document.write("Loop Exit!! (Float64Array) <hr/>");

さて、この結果は下の図のようになりました。左側が通常の変数の場合です。デノーマル領域を超えて 0 になるまで特に処理時間に変わりはないようです。しかし、

右側上 Float32Array の場合は \(1.17 \times 10^{-38}\)
右側下 Float64Array の場合は \(2.22 \times 10^{-308}\)

よりも小さいデノーマル領域では処理時間が落ちているのがわかります。

昔みたいに何十倍も遅くなるという程ではないので致命的な問題にはなりにくそうではありますが、逆に、パフォーマンスいまいちだなーとか思いながら気が付かずに使ってしまうという事がありそうですね。 TypedArray を使う時には気を付けた方が良さそうです。

-----(2020/07/04) さっそく追記ですが-----

昔よりデノーマルのペナルティが低くて 5~6 倍というのは勘違いだったようです。事態はもっと深刻とも言えます。
という事で検証用の式と結果を訂正します。

検証で使用した計算式が元は、 通常の変数 = Float32Array * 通常の変数 になっていました。
この場合、通常の変数にデノーマル数をロードするのが遅いという事はわかりますが演算自体はデノーマルのペナルティが発生しない通常変数で行われています。

Float32Array = Float32Array * Float32Array のように TypedArray のままで演算自体が完結する場合、デノーマル数が発生すると通常の場合よりも 20 倍程度時間がかかるようです。

ただし、TypedArray の演算は通常の変数よりも高速ですのでそれに比べて、という話になります。高速なはずの TypedArray なのにデノーマルを発生させてしまうと通常の変数よりも遅くなってしまうという事でもあります。TypedArray を使う場合は十分に注意する必要がありますね。
なお、この件は Chrome、Firefox 共同じ現象になるようです。


posted by g200kg : 11:47 AM : PermaLink

« 2020年06月 | 2020年07月のアーカイブ | 2020年08月 »


g200kg