落胆がらくた街

Dr.Rootはサポート終了製品です。

JUCEプログラミング::Projucerで作ったLabel/Textの色が反映されない

JUCEのAudioPluginをProjucerのGUIデザイナで作ってると、Labelの色情報が正しく反映されない。

f:id:suzumodoki:20180224094609j:plain f:id:suzumodoki:20180224094611j:plain

色設定のコードおかしくね?みたいな記事を昨日書いたんだけど違ったから消した。

どうも、デフォルトの設定でOK!wとしてると色設定コードを吐いてくれないらしい。
一旦色設定画面↓を開くだけ開いて閉じるとコードがちゃんと生成される。なんじゃそりゃ。
f:id:suzumodoki:20180225103619j:plain

2PIでループする位相変数をUINTに正規化すると美味い

http://www.musicdsp.org/archive.php?classid=1
これの Fast SIN approximation for usage in e.g. additive synthesizers を試してみた。

float fastsin(UINT32 phase)
{
    const float frf3 = -1.0f / 6.0f;
    const float frf5 = 1.0f / 120.0f;
    const float frf7 = -1.0f / 5040.0f;
    const float frf9 = 1.0f / 362880.0f;
    const float f0pi5 = 1.570796327f;
    float x, x2, asin;
    UINT32 tmp = 0x3f800000 | ( phase >> 7 );
    if (phase & 0x40000000)
        tmp ^= 0x007fffff;
    x = ( *( (float*)&tmp ) - 1.0f ) * f0pi5;
    x2 = x * x;
    asin = ( ( ( ( frf9 * x2 + frf7 ) * x2 + frf5 ) * x2 + frf3 ) * x2 + 1.0f ) * x;
    return ( phase & 0x80000000 ) ? -asin : asin;
}

検証コード↓

int main() {
    timerInit();

    float delta_f = 2 * PI * 1000 / SRATE;
    float phase_f = 0;
    UINT32 delta_i = 1000.0 / SRATE * UINT32_MAX;
    UINT32 phase_i = 0;
    

    //特に工夫無く、位相phaseと角速度deltaをfloatにしてmath.hのsinfを呼ぶ
    double t = 0;
    float test = 0;

    for (size_t i = 0; i < SRATE; i++)
    {
        timerStart();

        test += sinf(phase_f);
        phase_f += delta_f;

        t += timerStop();

        if (phase_f >= 2 * PI)phase_f -= 2 * PI; //2PIで位相がループするのはとりあえず時間計測の外とする
    }

    cout << "sinf(float)::" << "total:" << t << "[ms]\n";

    //UINT32正規化版
    t = 0;
    float test2 = 0;

    for (size_t i = 0; i < SRATE; i++)
    {
        timerStart();

        test2 += fastsin(phase_i);
        phase_i += delta_i;

        t += timerStop();
    }

    cout << "sinf(UINT32)::" << "total:" << t << "[ms]\n";

    cout << "test:" << test << ", test2:" << test2 << endl; //何らかの形で変数を参照してやらないと最適化で省かれる

}

結果。

sinf(float)::total:3.06791[ms]
sinf(UINT32)::total:0.292572[ms]
test:0.0168861, test2:6.52373e-05

UINT32、速い(確信)。
ちなみに計測環境はWin8.1、cpu:intel 4690k、DRAM 16GBでコンパイル環境はVisual studio 2015のVisual C++、x64 Releaseビルドで最適化レベル最大。
sinの出力結果の総和が微妙に異なってるのは精度の問題なんだが、別に精度計測のソース書いたら1サンプル毎の精度は十分だったので割愛。


位相をUINT32に正規化するメリットを考えてみる。

  • 検証コードでは時間計測から省いたけど、真面目に2PIでループさせようとする場合1サンプル毎に位相が2PI超えたら戻す処理がいる。UINTならオーバーフローするので不要。
  • 位相を元にテーブルを引きたいみたいな時、位相変数の上何桁と下何桁を分けるみたいな処理が高速で出来る float->intキャストよりずっとはやい!
  • fastsinが速い
  • 位相変数そのものの計算もビット演算が使える分そこそこ速い。特に頻出の2n倍が高速なのは嬉しい

とかこんな感じかな。結構嬉しいと思うんだけど、逆にデメリットはどんなんあるんだろう。誰か教えてください。

デジタル信号処理チートシートと、BLITと殴り合ったメモ

デジタル信号処理でよく出てくる式とか変数とか関係をリストにしてみた
シンセとかプログラミングするのにこんがらがった時はどうぞ


  • int  R:サンプルレート。殆どの場合、44100か48000のどっちか。ほぼ定数とみなして良い。
  • int  F=R/2:ナイキストレート。やはり実質ほぼ定数で、22050または24000。
  • float  f:鳴らしたい周波数[Hz]。↓の f_{min}を用いて、 f_{min} \leq f \leq Fになる。
  • float  f_{min}:鳴らしたい周波数の下限。大抵10[Hz]とかそんなもん。
  • float  T=1/f:周期。単位は秒。
  • float  p:現在の位相。2πでループするんだから、 0 \leq p \leq 2\pi
  • float  {\omega}=2{\pi}f:fにおける角速度。つまりf[Hz]のサイン波は現実世界では sin({\omega})=sin(2{\pi}f)
  • float  d=2{\pi}f/R:1サンプル毎に進む位相差。つまり1サンプル処理する毎にp+=d; if(p>=2*PI) p-=2*PI;をするということ。 \omegaのデジタル世界版。fの条件から、  0 \leq d \leq \piになる。
  • int  H=(int)F/f=(int){\pi}/d:ある鳴らしたい周波数fにおいて、倍音が何個出せるか(F以下に収まる倍音の数)。整数倍音は全部持つ(ノコギリみたいに)と仮定。
  • float  P=R/f:周波数fの信号が1週するのにかかるサンプル数。ぴったり整数個でループするとは限らないので実数。
  • float  sin(2{\pi}\frac{f}{R}n):サンプルレートR、周波数fのサイン波のn番目の値。

あとBLITのメモ
 \displaystyle \begin{align}
Sinc_{M}(x)=\frac{sin(\pi x)}{Msin(\frac{\pi x}{M})}, \\
y(n)=\frac{M}{P}Sinc_M(\frac{M}{P}n) \\
=\frac{1}{P}\frac{sin(M\frac{\pi}{P}n)}{sin(\frac{\pi}{P}n)}
 \end{align}
ここでMはPを超えない最大の奇数(int)。
こいつを日本語訳すると、 「y(n)はナイキストレートまでの周波数で帯域制限したf[Hz]のインパルス列のn番目の値」。
Pは1周するのにかかるサンプル数なので上記チートシートの通りR/f。つまりサンプルレートを鳴らしたい周波数で割ったもの。
この帯域制限されたインパルス列から帯域制限されたSAWやSQUAREを作れる。
詳しくはg200kg氏のページ参照(必読)。

BLITのお話 | g200kg Music & Software


ところで、おもむろに  P=R/f \Rightarrow \frac{1}{P}=f/R をBLITの式に突っ込んでみよう。
 \displaystyle \begin{align}
y(n)=\frac{1}{P}\frac{sin(M\frac{\pi}{P}n)}{sin(\frac{\pi}{P}n)} \\
=\frac{1}{P}\frac{sin(M{\pi}\frac{f}{R}n)}{sin({\pi}\frac{f}{R}n)}
 \end{align}
sinの中身をよく見てみると、f[Hz]のサイン波が sin(2{\pi}\frac{f}{R}n)なんだから、 sin({\pi}\frac{f}{R}n)は鳴らしたい周波数の半分の速度のsinだ。つまりBLITとは「f/2[Hz]のサインでMf/2[Hz]のサインを割ったもの」と言える。ここから、

BLIT(p)=\frac{f}{R}\frac{sin(M\frac{p}{2})}{sin(\frac{p}{2})}
が得られる。
y(n)は「BLITのn番目の値」を引っ張ってくるわけだが、BLIT波形の1周が綺麗に整数個で収まるとは限らない(=Pが整数とは限らない)ので、BLIT(p)=「p[rad]におけるBLITの値」を算出できるようにしておいたほうが都合がいい場合もあると思う。


F[Hz]に帯域制限されたf[Hz]のインパルス列が、なんでこの式で得られるのか。
式の算出過程は理想インパルス列(デルタ関数級数)を理想LPF(時間領域でsinc関数)と畳み込んだものなんだが、得られた式は何がどういう意味を持っているのか。
早速Maximaさんにグラフを描いてもらった。 f:id:suzumodoki:20180121023528j:plain
極限の扱いが上手く出来ておらず、分母が0の時なにやら変な感じになってしまっているが、理想的には滑らかなsincの列なので脳内補完して図を眺める。
まずBLITの式を簡略化すると、振幅を決める \frac{1}{P}はこの際重要じゃないので無視して、意味があるのは \frac{sin(Mx)}{sin(x)}の部分。
ここで x = \pi\frac{f}{R}n だが一旦おいといて、結局こいつは角速度 MxのSinと xのSinの逆数との積だと言ってるわけだ。

まず \frac{1}{sin(x)}はこんな感じ。 f:id:suzumodoki:20180121025414j:plain
赤線が1/sin(x)なんだが、ソフトに書かせると無限の部分を真面目に描いちゃうので、あえて手書きで概略図にしている。
で、こいつにsin(Mx)をかけると… f:id:suzumodoki:20180121030914j:plain
こんな感じ。イマイチ分かりにくいが、大雑把にまとめると

  • 1/sin(x)が発散するタイミングで、 \lim_{x \to \infty} \frac{sin(Ax)}{x}=AよりMになる(インパルスがぴこっと立つ)
  • よって1/sin(x)は「インパルス列そのものの周波数」に対して影響を及ぼしている。
  • よく見るとx=0,π,2πで1/sin(x)が∞になるので、インパルスが立つのはsin(x)の倍の周期。BLITがf/2のsinで割っているのはこういう理由。
  • 分子のsin(Mx)は帯域制限する周波数に対するパラメータ。事実上Mが帯域制限周波数になる*1
  • もしMが奇数だと、インパルスが立つ瞬間の1/sin(x)とsin(Mx)は同じ符号になるので、必ずインパルスはプラス側に立つ。
  • 逆にMが偶数だと、インパルスがプラスとマイナス交互に立つ。これを「バイポーラBLIT」と言う。
  • 矩形波作りたくてバイポーラBLITをやると周波数が半分になる、というのはこの理屈から1/sin(x)だけ倍の周波数にすれば解決する。多分。

といったところ。


現実的な問題と備忘録。
 Sinc_M(0)=1より BLIT(0)=M/Pであるべきだ。黙ってsin/sinをプログラミングすると、sinはともかく割り算が出るので一々0除算じゃないかチェックしなきゃならず辛い。
この間「sinをテーブル引きにすると速い」というネタを自分の環境で試したら速いどころか100倍以上遅くてゲンナリだったんだけど、Sincとか1/sinみたいなのをテーブル化するんならやぶさかでない気もする。というわけで今後は本格的にどんな実装が速いのかとか、数学よりもっとC++とかx64で効率的なやり方を考えようと思います。どっとはらい(あ、あと位相変数は2πをUINT32に正規化するとオーバーフローのお陰で周期性チェックいらなくて便利じゃねっての思ったのでこれも今度試します)

*1:うまいことやればPを整数に決め打ちしても任意のMで帯域制限できそうなんだけど勉強不足で未検証

もうちょっと真面目にパナ改を作った

まえがき

suzublog.hatenablog.com
↑およそ一年前に作ったやつ
こいつは回路の実験という側面が強かったし、単純に出来がよろしくなかったので、「余り物で安く作る」というコンセプトはそのままにもうちょいマトモな代物を組み上げた。
ろくに回路を知らなかった去年よりもう少し知恵も付けたので、多少はマシになった筈だ。採用した回路はいつもの通り
1713 :ファンタム式パナ改マイクの単純化 省電流バージョン|ShinさんのPA工作室
からパクらせていただく。ただし、マジで金が無くて秋月で買い物する送料すら捻出できない貧乏ぶりなので、「コンデンサは容赦なく積層セラミックを使う」という致命的な欠点が。

でざいん

本体及びハウジングだが、これも余り物の流用で、まずXLRはサウンドハウスで一番安かったこれ。
CLASSIC PRO ( クラシックプロ ) >CXL2B | サウンドハウス

マイク本体を収めるのは、ぶち折れたオシロスコープのプローブを流用。
オシロスコープ・プローブ(60MHz): 測定器・計測器関連 秋月電子通商 電子部品 ネット通販
これをバラすとどうなるかはこちらの記事が参考になる…というかまんまだ。
オシロスコープのプローブの修理 ( 修理とリフォーム ) - 金田式アンプ・ファンのささやき - Yahoo!ブログ

さらにそれらを収めるハウジング的な何かとして、余ってたモノプラグのガワを流用。なんでも良いんだけど、これが一番サイズがぴったりだった。
https://www.soundhouse.co.jp/products/detail/item/25715/

f:id:suzumodoki:20171214150549j:plain
右から、「BNCコネクタをバラバラにしたもの」「プローブの持ち手のゴム?の部分」「モノプラグのガワ」「ガワだけ使われていらない子になったモノプラグの本体」。

電子的な部分について。
マイク石はお決まりのこれ↓。拘る人はオリジナルのWM-61Aに拘るらしいが、とりあえず安くて手に入りやすいのでこっちに飛びつく。
エレクトレットコンデンサーマイクロホン(ECM) WM−61A相当品: パーツ一般 秋月電子通商 電子部品 ネット通販

また基盤は、元ネタの「コネクタの中に回路を全て入れる」というアイデアを拝借。必然的に小さい部品が有利なので、J-fetはこれ↓を使う。2つのJ-fetが1チップに収められており、しかもソース同士が内部でくっついている(元の回路図と同じ状態になっている)ので大変好都合だ。
Nch J−FET 2SK2145−BL (2個入): 半導体 秋月電子通商 電子部品 ネット通販

そして基盤は面実装モノをうまい具合に乗せられるこれ↓を採用。こいつは穴がちっこすぎて面実装以外の部品がマトモに乗らないのだが、
64ホールユニバーサル基板(1.16mm): パーツ一般 秋月電子通商 電子部品 ネット通販

こいつの端子部分を引っこ抜くとうまい具合に変換ピンとして流用出来る。↓
シングルピンソケット (低メス) 1×20 (20P): パーツ一般 秋月電子通商 電子部品 ネット通販
f:id:suzumodoki:20171214150536j:plain

ケーブルを受ける側の基盤は同じサイズでDIPピッチの物があるのでこれと繋げばOKだ。
16ホールユニバーサル基板(2.54mm): パーツ一般 秋月電子通商 電子部品 ネット通販


抵抗はebayで中国人が何故か送料無料で送ってくれた大量の面実装抵抗を使う。精度?知らんな!w

かいろ

「超簡略版」との事だが、ファンタム電源を使う以上はいくら簡略化されていても構わない。どうせゲインはオーディオインターフェースでどうとでも出来るからだ。
抵抗4つ、コンデンサ2つ、面実装のJ-fet実質1つを基板上にせっせこ組み上げるだけで出来上がる。今回はあれこれ余り物を使った以上ちょっとヘンな部分があるので回路は本家様にまかせてここではさらっと各部品について触れる。

まず、各J-fetのゲートからGNDに落としている1MΩの抵抗。こいつは十分大きければ何でも良い。今回は在庫事情から750kΩを採用したが、そんなに精度もいらんだろう。
次に、マイクのマイナス、即ちマイク内蔵FETのソースからGNDに落ちている2.2kΩ。こいつはマイク石のデータシートに指定されている数値なので、余り弄るべきじゃないだろう。精度がいるかは謎だが。
で、J-fetのソースからGNDに落としている1.5kΩ、これもなるべくこの通りにした方がいい。まぁ今回は1kΩにしちゃったんだけど。元の回路は2sk-2880というJ-fetを使ってるんだが、今回使ったJ-fetはもうちょっとソース電流がデカいので、それに合わせて少し下げてみた。ここの抵抗がどんな影響を及ぼすかは検証の余地あり。

2つの0.22uFコンデンサ。これは耐圧50V以上が必須で、かつなるべくオーディオ向きのフィルムコンなんかを使うべきだ。在庫無いから安物の積セラ挿したけど(号泣)
ただ、0.22uFという数字は検証の余地があると思う。こいつを絞ると低域がカットされる筈だが、完成品を使ってみた所どうも低域がちと強すぎる感じがあった。好みで上下させていいと思う。

かこう

兎にも角にも苦労するのは加工作業だ。もしこれから初めてこのECM改造をやるのであれば、安いものなので多めに買って練習すると良いと思う。
f:id:suzumodoki:20171214150542j:plain
画像左が無加工の状態。青字で注釈を入れたが、ようはこの黒い点がスルーホールで、内部のマイクの端子にくっついているわけだ。初期状態ではマイクのガワ(GND)とくっついているのが困るので、うまい具合に切り離さなきゃならない。まず予め乗っているハンダが邪魔なので吸い取る。次にスルーホールを潰さないよう気をつけながら、基盤の金属部分、赤点線の所をカッターでせっせこ切って電気的に切断する。マイクのガワはまずハンダが乗らないので、基盤部分をマイク-とGNDに分けなければならないわけだ。マイクは熱に強い部品ではないので、コテを押し当てる時間は長くても3秒程度、それを超えたら一旦冷やす…という感じでやると失敗しにくいと思う。

出来上がったマイクの各端子に配線するのも一苦労だろうが、これはなるべく細い線を使えば(そしてコツさえ掴めば)難しくない。お勧めは物故割れたイヤホンの線だ。こいつは極めて細い上に秋葉原まで出なくても安く簡単に入手出来るというメリットがある。金の話しかしてねぇな…?
メッキ皮膜がなされているので普通に剥がそうとすると大変だが、「ハンダゴテの熱でメッキを溶かして剥がす」というテク(?)を身につければ楽勝だ。まず乗らない事を承知の上ではんだを線材のさきっぽに当ててコテで溶かす。そのまま線材にコテを当て続けると、はんだを通じて熱が伝わり、メッキが黒く焦げて焼け落ちる。黒くなった部分をコテ先で擦るとうまい具合にメッキが剥がれてハンダが乗ってくれるというわけだ。
f:id:suzumodoki:20171214150527j:plain
画像は作業途中の2線繋いだ所。

なお、この配線作業も慣れない内は失敗がつきものだと思う。そんなこと無い?自分だけ?
そんな時、「とりあえずマイクが生きてるかどうか確かめる回路」をブレッドボードかなんかに作ってテストすると便利だ。
f:id:suzumodoki:20171214160047j:plain
ようは本家本元、3線加工する前の回路でマイクテストして、ちゃんと音声が取れればとりあえず配線は成功、という事で。当たり前だけどこの回路はファンタムじゃないので注意。単三電池一個あればテストできるので便利。

くみたて

f:id:suzumodoki:20171214155051j:plain
コンデンサに小さい積セラを使っていることを除いても、面実装部品を遠慮なく使えば余裕を持ってコネクタに収まる事がわかる。
ちなみに手前のケーブル及びコンデンサ部分の基盤と、XLRピンに繋いだ面実装基盤部分は分離できるように作ったので、もし色気づいてお高いコンデンサを買い足しても簡単に改造出来る仕組みだ。今度部品を買いに行く機会があったらやってみよう。

f:id:suzumodoki:20171214155043j:plain
出来上がり。大分端折ったが、どんな部品が手元にあるかで作り方は千差万別だと思う。兎にも角にも、「6ミリ径のマイクがきれいに収まってケーブルを通せる部品」があればなんだって良いのだ。それこそ100均で売ってるチャッカマンの先端の筒部分だって良い。回路部分に関しては、本家のJ-fetや高精度抵抗への拘りも理解できるものの、コストに釣り合わないとか、そもそもS/N比が良ければあとは加工次第でどうにでもなるとか、アマチュアにはアマチュアの事情があるので、個人個人で部品を選べばいいと思う。自分は面実装を容赦なく使用し、確実にコネクタに入れられる方針を採用した(でもコンデンサは自分でも酷いと思う)。もっと一般的な事を言うなら、
・J-fetはNchのものであれば何でも良い。音は好みによる所が大きいので、出してみないとわからない。
・抵抗は高精度であることが売りになる部分(1.5kと2.2k)とそうでもない部分(1MΩ)がある。前者は拘る価値があるかもしれないが、音質にどこまで影響するかは謎。精度より温度特性を意識したほうが良いかも?
コンデンサは拘る価値が大いにある。ただし、容量に関しては検討の余地がある。0.047uF~1uFあたりを色々試してみたい所存。ただし2つのコンデンサが同容量なのは絶対。

おためし

といったところ。肝心の録音に関しては、「A/Iのゲインが最低でも十分過ぎるくらいの音量でびっくり」という結果に。これはこれで不便かもしれないので要検証。
Twitterには録音した音源を挙げたのだが、ネタに走ってクッソ汚い六尺コピペ朗読音声しか挙げなかったので、こっちに挙げるのは後日マトモな奴を正式に録るということで…

一月以上経った今、ふと思い出してようやく録音のテスト音声を上げる事にした。
録音はsteinberg CL-1。廉価なA/Iなので、ここが音質に影響を与えてる説はあるかもしれない…。

とりあえずテキトーにエレキギターを弾いてみた。ソフト的な処理はゲインだけで、ノイズキャンセルの類は皆無。
個人的には十分な音質だと思うんだが、どうもマイクと距離や角度があると急激に音が取れなくなるらしく、例えばウェブカメラのマイクのような部屋全体を録るには向いてない。
楽器や声を録るには良いだろう。

全くのおまけとして、同じギターを同じA/Iにライン録音で録ってみた。

…こ、こいつぁひでぇや!
A/Iがダメなのか、ギター(バッカステレキャス 元値15k ほぼタダで貰った)がダメなのか、いや両方か。
しかし一番ヒドいのは自分のギターの腕前そのものだった。先輩こいつ1年以上やってて未だにF弾けないらしいっすよ?

Split-Radix型大浦FFTの一部をC#に移植した

qiita.com

Qiitaクソ便利だしもうプログラム関連の記事はあっちで書くわ 何がはてなじゃw

~~~

Qiitaでは一切関係ない話題だから書かなかったけど、大浦FFTもFFTWも基本的には「いかに高精度で、次に速く」を原則として作られてる。ので、例えばDTMで使うVSTプラグインみたいな、「まぁ概ねスペクトルの概形が見られればOK」っていうシチュエーション、すなわち「いかに速く軽く、次に高精度で」を原則とした場合まだイジれそうな気がする。

具体的には、FFTはサンプルをN個突っ込んで、サンプルレートの半分を更にNで割った分だけ周波数分解能が得られるわけだが、パラメトリックイコライザなんかでとりあえずローをばっさり切るべーみたいな時に果たして何千という分解能はいるのかという感じ。だから「8192個のサンプルを突っ込むんだけど、求める係数は1つ飛ばし」とかでもいいんじゃね的な。

なんでこんなこと考えてるのかというと、CubaseからStudio Oneに移行したら、Cubaseにあったデフォルト付属のパライコがなくなっちゃったのだ。あのチャンネルストリップに最初から刺さってるやつ。
んでスタ1には一応ProEQって結構高性能なEQが付属しとるんだけど、もっともっと手軽に使えるEQの需要ってあるんじゃないかと思った所。
例えばミックス処理なんかで、大抵のトラックにはローカットを食らわせつつ特定の周波数帯をピークEQでどついたり削ったりするってのがよくある話だと思うんだけど、それやるのにそんな高級なEQいちいち挿したくない。んで探すと意外とフリーでシンプルなパライコ無いんだよな。なんかどれもコレも性能重視というか。性能は雑魚くていいから馬鹿みたいに軽い奴誰か教えてください。

~~~

もいっこ思ったんだけど、FFTというかオーディオのバッファリングでよく使われるのがリングバッファだ。リング状の配列にぐるぐるデータを突っ込んでいく。で、FFTをかけるときは、例えばサイズ8のリングバッファを4つずつFFTかけるみたいな時、最初はa0からa3をFFT、次はa1からa4、次はa2~a5…みたいにちょっとずつずらしながらFFTをかけたい…なんていうのが思いつく。ところが大浦FFTはin-place演算なので、FFTをかけたら元のデータは無くなってしまう。ましてやこの「ずらしながら法」の場合、ハミング窓も一緒になってずれていくので、こうなると一々FFTをかけたい部分だけリングバッファからコピーしないといけない計算になる。こりゃー無駄だ。

っていうのを何とかしたかったんだけど、素のFFTアルゴリズムを自前で書いてリングバッファと窓関数を対応させたやつよりも、一々バッファをコピーしてから大浦FFTかけるほうが速度出たからもう寝るわw