*庭の状況メモ

園芸 20120513 174847
imaege 2 : DSC06207.JPG
前回3月下旬 、収穫されるか心配されたスナップエンドウですが、なんかその後花咲きまくり、繁茂しまくり、結実しまくりで、少なくとも200個、いや300個くらい収穫。で、まだ100個くらいは残ってる。いやー素晴らしい。
そして、スナップエンドウは採って即1.5分塩茹でが一番美味い!どうやら、枝豆もそうだけど、豆類は採った直後から甘みが落ちていくようなので、採れたてが最強。1日経つとかなり味が落ちる。自分で育てない限り採れたては味わえないという点で、スナップエンドウは家庭菜園向きと思った。

imaege 1 : DSC06199.JPG
ただ、茹で過ぎるとシャキシャキした瑞々しさがなくなるので注意が必要。栄養価的にもビタミンCは熱に弱いので茹で過ぎはだめ。この写真のスナップは間違えて4分ほど茹でられてしまったので、若干残念。

imaege 6 : DSC06221.JPG
家庭菜園向けと言えば、ハーブ類もそうかも。特にパクチーはなかなか売ってないし、売ってても高いし、鮮度が落ちてる。そもそも日本で食べるパクチーはどうもパンチが弱い。ベトナムやタイで食べたあの鮮烈な、どぎつい香りと味を、日本の料理店で味わったことが無い。
こうなったら自分で作るしかない、って事で種を買って空いてるプランターにバラ撒いたところ、発芽率は悪いけどちょびっと生えてきた。

imaege 10 : DSC06237.JPG
こうなると我慢できない。新芽を刈り取って辛ラーメンに入れてみた。ついでに庭の浅葱とスナップエンドウも入れてみた。
これは…なんということでしょう。一瞬で韓国ラーメンがタイラーメンに変貌した。すぐにでも酢と海老を入れて、トムヤンクン化したい衝動に駆られる。パクチーとは恐ろしい奴だ。全く人を引き立てるとか、調和するということをしない。徹底的に料理の中心的存在を自分に書き換えてしまう。

しかし、匂いなどの強烈さは本場と比べるとこれでも薄い。確かにパクチーなのだけど、何だかフルーティーさも兼ね備えちゃってる。パクチーの種、コリアンダーは柑橘系の香りがするのだけど、それが混ざっている感じ。これはこれで美味いけど、やっぱなんか違う。もっと強烈な日差しを浴びせて成長させてから食べてみるか…

imaege 5 : DSC06215.JPG
ジャガイモが繁茂しだした。てか、花も咲き出した。
またもや一切脇芽を刈らず放置していたので、密集して分けわかんないことになってきた。挿し芽で増やせるらしいので、駄目もとで適当に刈って、空地に挿してみる。

imaege 3 : DSC06210.JPG imaege 4 : DSC06214.JPG imaege 7 : DSC06226.JPG imaege 8 : DSC06228.JPG imaege 9 : DSC06230.JPG
再生長ネギ(スーパーのネギの切れ端を植えて放置)、浅葱、ワケギのネギ系は育てすぎて消費が追いつかず、刈って冷凍にしたりしてたんだけど、それでも消費が追いつかず、結局葱坊主できまくり。折角だから花も咲かせて種も採ってみますが、これ以上ネギは要らないよ…

imaege 11 : DSC06238.JPG imaege 0 : DSC06241.JPG
一番増えまくったのがワケギ。去年8月に2球ずつ植えたのだけど、育ちがいいとそれが50球くらいに増えてる。球根もさっと湯通しして、酢味噌和えで食べると美味しいんだけど、食べ過ぎて飽きたし、もう旬過ぎてるし、秋前にまた植えるために掘り起こすしかない。こんな沢山植えるところないんだけど、ネギ類は防虫効果や連作障害を防ぐ効果もあるみたいなので、空いたところにゲリラ的に植えていこうかな。

という落ちも何も無い雑記でした。

*メガデモを語る

demo 20120503 222901
歯ブラシ解読会楽しかった。
  • ronan解説+js化とか
  • exe packerのdisasmしてoprandごとに並べ変えて圧縮率UPとか
  • なぜか高校生がC64とAMIGAについて熱く語るセッションとか
  • werkkzeugのscript周りの話とか
  • werkkzeugのGUIは何故かwindow周り自作してる話とか
  • 自作デモツールの話とか
  • revisionの生な話とか
他にも色々ありました。

これも含めTokyo Demo Fest(行ったことないんですが)を組織している、kiokuさんたちはホントすごいな。

適当に振り返ってみる。

とりあえず参加者に知り合い誰も居ないし、ここ数年デモシーンに全く深入りしてないので知ってる人も居ないだろうし、ぼっちになりそーだなー、と思いつつヒカリエ到着。

ヒカリエとはどんなもんじゃい、と思いつつエスカレーターで11Fで上がっていったけど、女子やらカップルやら母親系家族連れやら、オサレな人種の坩堝と化した人たちが所狭しと集まって、それでやってることは密集して行列作って飯食うために何十分も待つということだけということに対し、実にここは高度に発達した文明都市の矛盾と功罪を集積した、現代のバベルの塔だな、と思いつつも、アート系の展示とかも多くて男一人でぶらぶらしてても意外と楽しい感じ。

さて、そのバベルの塔の上層階に陣取るは、今を時めくDeNA社である。何人かDeNAの社員の方がいらっしゃり、その手引きというか御好意で部外者の僕らもその内部に侵入して集会が開けるというわけだ。

その集合場所11Fオフィス入り口に辿り着くと、一目で判る怪しげな集団が!もう遠目でも簡単に分る、ヒカリエには相応しくない(自分も含め)野郎の集団!これが歯ブラシ解読会に集う人々に違いありません。事実近づくと、黒地に白で「歯ブラシ解読会」と描かれた無骨な画像を表示したiPadを高々と掲げる怪しげな人が居ます。やはりそうだったか、と奇妙な安心感と連帯感を覚えました。

そこから21FのDeNAのロビーを通り、会議室へ。11Fの時点で眺めが素晴らしく、物見遊山に訪れた観光客が写真を撮りまくるという構図があったのですが、21Fは完全に渋谷の街を眼下に収めることができ、普通の会社では体験することのできない、圧倒的優越感、社会的成功感が味わえます。

それはともかく、会議室に通るとほぼ満席、入ったすぐのところが空いていたので、座ってみると、どうも隣の人の様子がおかしい。Mac Book Airで猛烈にプログラムを組んでおられる。そして、猛烈に能力の高そうなオーラを発しておられる。このとき、自分の中の検索会社が「もしかして:hamaji.shinichiro先生」とアラートを出しました。
そして、その前に座っている長髪の方も只ならぬ雰囲気を醸し出しております。これは、僕はとんでもない席に迷い込んでしまったのではないか。

で、隣の方が気になって仕方ないのでチラ見してたら、どうやらぷよぷよの対戦アルゴリズムを作っておられる雰囲気。もうhamaji先生ほぼ確定やん。と思って確認してみたらマジでそうでした。会社にぷよぷよ日本一の人が遊びに来るのでAIを作って戦ったけど、歯が立たず、悔しいのでアルゴリズムを改良しようとしている、とのこと。憧れの人とその作業風景に出会えて感動いたしました。

そして長髪の方が301zさんで、休憩中にpackerの話で盛り上がったのだけど、その時点で以前2chのメガデモスレでhello, worldみたいなのを如何に小さくするかという話題で、僕の限界を突破して感服されられた方が301zさんと判明した。まじかー。

もはや、いつのどんな話だったか、自分が何をしたかも良く覚えてなかったので、家に帰って調べたら
メガデモを語る
270( ´∀`)さん :2001/02/06(火) 05:39
>>267 
>AAコンポ作品がまだないっすね・・・・ 

  Λ_Λ   / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ 
 ( ´∀`) <  モナーを256バイトで描写してみな! 
 (    )   \___________ 
 | | | 
 (__)_) 

Windowsアプリ限定. 
コンソール不可.
これだ!
もう11年以上前じゃん。
しかもこれ書き込んだのDonだっけ?

299デフォルトの名無しさん :2001/02/08(木) 02:38
>>270 
俺は533bytesが限界だな。 
512が一つの壁のような気がする。 

300 270age :2001/02/08(木) 03:28
>>299 
256は無理?あげ

301 デフォルトの名無しさん :2001/02/08(木) 03:33
>>299 
533bytesとは…NASMですか? 
自分は現在876bytes,精進します.

302 301 :2001/02/08(木) 05:09
ムキになってまだやってます (藁 
MZ/PEヘッダにリテラル逃がして現在 596bytes. 
ふー. もう寝よう…

326 301 :2001/02/10(土) 05:20
512バイトきっかり達成, もう少しいじってみます.

328 301 :2001/02/10(土) 10:06
疲れたからもういいや…ということでアプしました. 
ああ、この299が僕で、301が301zさんなのか。
今頃、青春の点と今の点が繋がって、線になった。感無量すぎる。
というか、気づいてなかったの僕だけ?

当時のバイナリもまだ手に入りますね。
http://2chparty.net/2chparty01


というわけで、非常に楽しく感慨深い会でした。
2次会も楽しかったですね。同じ店に明るいうちから22時まで居たんだっけ。
holeさんのライブコーディングとか始まるし、高校生がゲームボーイ始めるし、二人でセッションしだすし、濃かったです。てか、何でもありなあの店素晴らしい。

imaege 1 : DSC06153.JPG imaege 2 : DSC06158.JPG

*farbrausch V2 synthesizerをちょっと読んでみた

tech , demo 20120503 222806
歯ブラシ解読会があるということで、僕も(発表には間に合わないけど)何か読んでみるかと思った。

farbrauschと言えば、そのintroは圧倒的な物量で見るものを唖然とさせてくれるのだけど、中でも64kbとは思えないリッチな音楽も当時は驚愕ものだった。特にfarbrausch衝撃の出世作であった2000年のfr-08は、64kbでありながら、本編前のローディング中にも専用の曲が流れるという意味不明な豪華さ。正直、当時はどうなってんじゃこりゃ?と笑うしかなかったわけだけど、あれからもう早12年近くが経つのだ。当時岐阜の高専生だった僕も、今や上京し社畜8年目。時の流れは無情である。

そんなfarbrauschが彼らのintro/demoのコアのコードを先日公開した。当時僕らを熱狂させてくれた魔術的コードが白日の下に晒されたのだから、これは何か読むしかないってわけで、とりあえずサウンド部分を見ることにした。最近全然デモシーンにもグラフィック周りにもご無沙汰だったので、たぶん他は読んでも解らんからね(汗)

V2 synthesizerとは何か

その摩訶不思議なfarbrauschのintroに使われているシンセサイザがV2 synthesizerというシステム。farbrauschのkb(Tammo Hinrichs)作。
3オペ16チャンネル 64音同時発声のシンセサイザ。内、スピーチ合成用に1ch。
楽曲作成用にVSTiプラグインやBuzzプラグインが提供されている。

バイナリは1.0が2004年に公開されて、1.5が2008年に公開されている。
http://1337haxorz.de/products.html

ソースは先日から
https://github.com/farbrausch/fr_public/tree/master/v2
で手に入るようになった。

基本スペック

基本スペック自体は普通のシンセかな、という気がする。ただ、この普通といえるレベルのシンセを当時の環境でリアルタイムにデモの裏で動かすという速度面の最適化をすると同時に、64kbの片隅に押し込めるサイズ面での最適化を行っていたというのが凄いのだ。

  • チャンネル
    • 3オシレータ
      • 正弦波、鋸波、三角波、正弦波の(前段オシレータによる)周波数変調、ノイズ、等
    • LFO*2
    • EG(Envelope Generator)*2
    • VCF*2
      • LPF,BPF,HPF,Notch,MoogLPF,MoogHPF
    • VelocityやNote、LFOやEGの出力は殆どあらゆるパラメータに結線可能。
    • 16ch(最終ch)はスピーチシンセサイザに割り振られている。
  • グローバル
    • リバーブ
    • ディレイ
    • コンプレッサ
    • バスブースト

YM2151 好きとしては4オペ(OSC)で複雑なFM変調がかけられるアルゴリズムを持っていると二重丸なんだけどね。kbさんはどうやらFM変調はあまり使わず、VCFやLFO等を駆使したテクノっぽい音作りがメインのようなので、3オペで割り切った構成なんだと思われる。

V2を使ったintro/demo

farbrauschのwerkkzeug3を使ったintroは全てと思われる。てことで、幾つか紹介。個人的にはfr08は当然として、MS2001 inv.の曲が何故か印象に残ってたり。

kbが作曲したintro

kbは作曲していないがv2を使った有名なintro

あと、まだV2はない時代だけどkbが作曲した有名なdemoだと
elitegroup - Kasparov
がある(当時はこれ、賛否両論あったよなあ)。V2のソースコードを見るとelitegroupの文字が見受けられるので、当時からのコードを使いまわしているようだ。

曲の作り方から再生まで

  • VSTiプラグインとして起動
 VSTiプラグインとして提供されているので、適当なVSTホストに読み込ませる。
 例えば、OpenMPTを使う。
 InstrumentにVSTiプラグインを読み込ませると、下記のような画面が出てくる。ソフトシンセに詳しければ、これで大体何ができるかは分る…のかな?DTMやらない僕は最初はよく分りませんでした。大まかには左側が各種モジュールで、右側が各種入力やそれらのモジュール、あるいはモジュール間の結線となります。EGやLFOはここで結線しないと意味ないです。
 ということで、これを弄り回して音色を作っていきます。

imaege 0 : v2_vsti.png

  • 音ができたらVSTホストで適当に曲を作る
  • 曲が完成したら、recordボタンを押してから曲を再生すると、V2 synthesizerに投げ込まれたコマンドがダンプされてv2mファイルが出来上がり
  • v2mファイルをlibv2で読み込ませて再生

これで、君のintroにも立派な曲が付けれるぞ、っと。

ファイル構成

v2/以下のファイル構成を洗い出してみた。
これ以外にもファイルは多数あるけど、読まなくても本質的には問題ない。たぶん。

synth.h
synth.asm
 実際のレンダリングを行う中核。
 フルアセンブラ。

v2mplayer.h
v2mplayer.cpp
 v2mのロード、レンダリング。
 werkkzeug3にはこれのコピーが_viruz2.hpp,.cppとして入っている。

dsio.h
dsio.asm
 DirectSound制御モジュール
 何故かフルアセンブラ。

ronan.h
ronan.cpp
 スピーチシンセサイザ。

v2mから再生までの流れ

実際のコードを深堀りする前に、全体の流れをざっくり抑えておこう。

v2m形式

まずはv2mがどんな形式なのかを説明する。
ざっくり言えば、MIDIメッセージのProgram Change,Control Change,Pitch Bend,Note On/Offを記録したもの。
ただし、圧縮率を高めるためにSMF(Standard Midi File)とは格納方式がかなり異なる。
SMFの場合、(Format0,1,2で異なる面もあるが)基本的にはデルタタイム(可変バイト長)+MIDIメッセージを順番に並べるだけである。

v2mでもデルタタイム(ただし固定長)+MIDIメッセージを並べるという点では同じ。ただ、並べ方に工夫がある。
v2mの場合はチャンネル毎、メッセージ種類毎、パラメータのバイト単位で並べなおす。例えば、最初はch.1のProgram Changeのパラメータだけが並び、次にそのデルタタイムの先頭バイトだけが並び、次にそのデルタタイムの2バイト目が並ぶといった具合。SMFがAoSで、v2mはSoAと言えば近いのかな。
これによる利点は2つあって、ひとつは、チャンネルやメッセージの種類を表すMIDIメッセージの最初の1バイトがメッセージ毎に省略できること。次に似た値が並びやすいので圧縮が期待できる点。

このあたりの概略はsoundsys.cppの頭のコメントや、vsti/v2mrecoder.cppのExport関数をみると分りやすい。

v2mのデコード

v2m形式の概要は分ったので、デコードからシンセサイザ本体までの流れを見てみる。このあたりは、v2mplayer.cppを見れば分る。
v2mを読み込むと最初に従いパッチのセットアップやグローバルな設定を行う。
そしてtick毎に、V2MPlayer::Tick()によってv2mに従い、MIDIメッセージを生成へ渡す。具体的には、メッセージを1Tick分書き溜めてから、synth.asmのsynthProcessMIDIを呼ぶ。

生成するMIDIメッセージは以下のみ。
Cn pp Program Change
Bn cc vv Control Change
En ll mm Pitch Wheel Change
9n kk vv Note On
synth.asmはNote Off(1000nnnn)は受け付けない。代わりにNote Onのvelocityを0としてNote Off扱いとする。

MIDIメッセージの詳細は
http://www.midi.org/techspecs/midimessages.php
を参照。

synth.asmのインタフェイスがMIDIメッセージなのは、VSTi対応のためと思われ。

synthでレンダリング

では、このMIDIメッセージからどう波形をレンダリングしているのか?

その答えがsyhth.asmにある。ここが肝心要なのであるが、FPU命令だらけのフルアセンブラであり、高校生のころx86逆アセンブラを自作していたような自分でもなかなか読むのが辛い。はっきりいってCで書いてあれば、なんてことはないコードのはずなんだが…

データ構造を大まかに捉える

まずはどんなデータ構造があるのかを大雑把に掴んでおく。
SYNが基点となるsynthesizer本体の構造体となる。ポイントとなるプロパティだけを見てみる。

struc SYN
.patchmap resd 1
 .chanmap    resd POLY
 .chans    resb 16*CHANINFO.size
 .voicesv  resb POLY*syVV2.size
 .voicesw  resb POLY*syWV2.size
 .chansv       resb 16*syVChan.size     
 .chansw       resb 16*syWChan.size

patchmapはpatch(v2soundで定義される構造体。いわゆるinstrument)への配列ポインタ。

voicesとchans(チャンネル)は見れば分るが、末尾のvとwはなんだろう?
コードを読むと分るが、vがv2sound(値をBYTEで持っている)と互換を持っていて最初にv2soundから展開される構造体で、値をfloatで持つ。wは実際に発声するときに使うworkspaceで、Vが持っているプロパティに加え、各種テンポラリ変数を持っている。
v2soundとVとWの間でどのようにコピーされるかはstoreChanvaluesやsyV2Set、syChanSet等を読むと分る。

syVV2,syWV2の中にあるsyVOsc,syWOscや、syVLFO,syWLFOなども、同様の関係。

chanmapはどのボイスをどのチャンネルが使っているかを示してる。

大まかに各構造体の関係をまとめると、
  • SYN has Patches(v2sound), Voices(syVV2), Channels(syVChan), channel map(voiceとchannelの関係)
  • Voice has 3 OSCs(syVOsc), 2 VCFs(syVFlt), 2 LFOs(syVLFO), 2 Envelope Generators(syVEnv), 1 Distorter(syVDist)
  • Channel has 1 BassBoost(syVBoost), 1 Distorter(syVDist), 1 Chorus(syVModDel)
  • syVxxxが最初にv2soundを元にパラメータ設定される構造体で、syWxxxが実際に発生する際に使われる構造体(workspace)

ノートONを追う

ではそろそろv2mplayer.cppとの繋ぎから見ていこう。
v2mplayerで作られたMIDIメッセージは
_synthProcessMIDI
を経由してシンセサイザ本体に取り込まれる。ここでは代表的なProcessNoteOnの挙動を見てみる。
ざっくり見ていくと、空いてるvoiceを探したら(.foundfv)、storeV2Valuesでpatchをvoiceへ設定して、syV2NoteOnでOSCやEGの初期化を行う。先で述べたVとWがややこしいけど、意味が分っていれば読めるはず。
他のMIDIメッセージの処理も同じ感じで追える。と思う。

レンダリングを追う

チャンネルのレンダリング自体は大まかに2つのパートに分かれる。
EGやLFOの更新を行うsyV2Tick(EGやLFOからOSC等へのパラメータ反映はstoreChanValuesのmodmatrixで行う)と実際にレンダリングを行うsyV2Renderだ。

前者はEGとLFOの更新だから、特殊なことはしていないので解説は省く。
後者もそういう意味ではそこまで特殊なことはしていないと思うが、OSCとVCF(Filter)の代表的な実装だけを見ていこう。

OSC(Oscillator)

まず、音の基本となるOSCから。これはsyOscRenderでレンダリングしている。
modeによってtri/saw(三角波、鋸波),pulse(矩形波),sin,noise,FM変調sin,auxa,auxbが切り替わる。
auxa/auxbは外部入力を想定しているのだけど、v2mでは関係ないっぽい。

鋸波/三角波

てことで、まずはシンセサイザの基本、鋸波から見ていく。
これは
syOscRender.mode0
にコードがある。

.m0casetab   dd syOscRender.m0c2,     ; ...
             dd syOscRender.m0c1,     ; ..n , INVALID!
             dd syOscRender.m0c212,   ; .c.
             dd syOscRender.m0c21,    ; .cn
             dd syOscRender.m0c12,    ; o..
             dd syOscRender.m0c1,     ; o.n
             dd syOscRender.m0c2,     ; oc. , INVALID!
             dd syOscRender.m0c121    ; ocn
まず、ジャンプテーブルの定義から。
なぜ、たかが鋸波で8種(実質6種)も分岐があるのか疑問に思うかもしれないけど、この時点ではよく分らないので次へ。

section .text

.mode0     ; tri/saw
        mov             eax, [ebp + syWOsc.cnt]
    mov   esi, [ebp + syWOsc.freq]

    ; calc float helper values
        mov   ebx, esi
        shr   ebx, 9
        or    ebx, 0x3f800000
        mov   [ebp + syWOsc.tmp], ebx
    fld   dword [ebp + syWOsc.gain]     ; <g>
    fld1                                ; 1 <g>
    fsubr dword [ebp + syWOsc.tmp]      ; <f> <g>
    fld1                                ; <1> <f> <g>
    fdiv  st0, st1                      ; <1/f> <f> <g>
    fld   st2                           ; <g> <1/f> <f> <g>
ここから実際に波形をレンダリングするコードが始まる。
まずebpにsyWOsc構造体へのポインタが入っている前提。

はじめに定数値などをFPUのスタックに積んでいく。
ここで分りづらいのは、calc float helper valuesの部分だろう。
syWOsc.cntは毎ステップfreqの値が加算される小数部32bitの固定小数点だ。
この上位23ビットをfloatの仮数部に入れて、符号と指数は決めうちにして、1.0から2.0までのfloatを作っている。
その後、そこから1を引いて0.0から1.0にしている。FPUで割り算とかが発生するのを嫌ってこうしてるのかな。

あと、コメントにFPUのスタックの状況が書いてあるが、この<f>がそうやって作った値となる。
なお、スタックは左からst0 st1 st2 st3... という順で書いてある。正直がこれがないとマジでFPUのコードは解読不能。あっても辛いけど。

        mov   ebx, [ebp + syWOsc.brpt]
        shr   ebx, 9
        or    ebx, 0x3f800000
        mov   [ebp + syWOsc.tmp], ebx
    fld   dword [ebp + syWOsc.tmp]     ; <b> <g> <1/f> <f> <g>
    fld1                               ; <1> <b> <g> <1/f> <f> <g>
    fsubr  st0, st1                    ; <col> <b> <g> <1/f> <f> <g>

    ; m1=2/col
    ; m2=-2/(1-col)
    ; c1=gain/2*m1 = gain/col
    ; c2=gain/2*m2 = -gain/(1-col)
    fld    st0                         ; <col> <col> <b> <g> <1/f> <f> <g>
        fdivr  st0, st3                    ; <c1> <col> <b> <g> <1/f> <f> <g>
    fld1                               ; <1> <c1> <col> <b> <g> <1/f> <f> <g>
    fsubrp st2, st0                    ; <c1> <1-col> <b> <g> <1/f> <f> <g>
    fxch   st1                         ; <1-col> <c1> <b> <g> <1/f> <f> <g>

    fdivp  st3, st0                    ; <c1> <b> <g/(1-col)> <1/f> <f> <g>
    fxch   st2                         ; <g/(1-col)> <b> <c1> <1/f> <f> <g>
    fchs                               ; <c2> <b> <c1> <1/f> <f> <g>
次に、syWOsc.brptという謎のプロパティが出てくるが、これはVSTiプラグイン上ではcolorとして設定できる値だ。
おそらく、breakpointの略ではないかと思うが、これが鋸波、三角波の頂点の位置を決めている。つまり、colorの値を変えると鋸波~三角波~逆鋸波へと変化していく。mode1の矩形波ではduty比になる。
<b>が先ほどと同様の方法で1.0から2.0へfloatへcastしたもので、<col>がそれから1.0引いたものとなる。この値とgainを使って鋸波・三角波の上りと下りの係数を決める。

    ; calc state
    mov   ebx, eax
    sub   ebx, esi                 ; ................................  c
    rcr   edx, 1                   ; c...............................  .
    cmp   ebx, [ebp + syWOsc.brpt] ; c...............................  n
    rcl   edx, 2                   ; ..............................nc  .

.m0loop
        mov   ebx, eax
        shr   ebx, 9
        or    ebx, 0x3f800000
        mov   [ebp + syWOsc.tmp], ebx

      fld   dword [ebp + syWOsc.tmp] ; <p+b> <c2> <b> <c1> <1/f> <f> <g>
      fsub  st0, st2                 ; <p> <c2> <b> <c1> <1/f> <f> <g>
      cmp   eax, [ebp + syWOsc.brpt] ; ..............................oc  n
      rcl   edx, 1                   ; .............................ocn  .
      and   edx, 7                   ; 00000000000000000000000000000ocn
        jmp             dword [cs: .m0casetab + 4*edx]
次にcntとbrptから3bitの状態を作り、最初に定義したジャンプテーブルで分岐する。
3bitをocnとすると、oは1個前のループのcntがbrpt未満か、cが前回cntが1周したか(overflowしたか)、nが現在のcntがbrpt未満か、を表す。
三角波の前半の上り坂をphase1として下り坂をphase2とすると、ようやく最初のジャンプテーブルの意味が分ってくる。
syOscRender.m0c2は前回も今回もphase2、
syOscRender.m0c121は前回phase1で、cntが1周してまたphase1になったことを表す。
しかしなぜこんなに場合分けが必要なのかよく分らんな。普通だったら2パターン、多くても4パターンで実装すればいいんじゃね?と素人の僕は思うが、ここまで境界条件に拘る理由は何だろうね。

場合分け後の計算は、コメントに書いてある式の通りなので飛ばす。
.m0pl
      fadd  st0, st6                 ; <out> <c2> <b> <c1> <1/f> <f> <g>
      add   eax, esi                 ; ...............................n  c
      rcl   edx, 1                   ; ..............................nc  .
      test  byte [ebp + syWOsc.ring], 1
      jz    .m0noring
      fmul      dword [edi + 4*ecx]      ; <out'> <c2> <b> <c1> <1/f> <f> <g>
      jmp   .m0store
.m0noring
          fadd  dword [edi + 4*ecx]      ; <out'> <c2> <b> <c1> <1/f> <f> <g>
.m0store
          fstp  dword [edi + 4*ecx]      ; <c2> <b> <c1> <1/f> <f> <g>
          inc           ecx
        jz   .m0end
    jmp  .m0loop
条件分岐後に.m0plに合流する。最後にgainを足して(丸めのため?)、次のジャンプテーブルのために3bitの状態を更新する。出力先はedi+4*ecxの指すアドレスとなる。ring modulationが有効なら、既に書き込まれた値と乗算、そうでなければ加算する。

sin波/fm sin波

鋸波がかなり難解な作りになっているので、これが分ればあとは簡単。sin波はfsinを使わず7次の多項式で近似してるのがちょっとしたポイント。

http://www.agner.org/optimize/instruction_tables.pdf
によると最近のCPUでもfsinは100clk程度は見ておく必要があるようなので、今でもこれは有効だと思う。fsinだと倍精度で計算しちゃうだろうから、オーバースペックだしね。
ちなみに、さらに高速化・高精度にしたいなら、区間分割して(cntの上位ビットで振り分ける)それごとに係数を変える、さらに係数をテイラー展開ではなくミニマックス近似を行うのが定石。

fm sin波では、前段までのOSCの出力をcntに足す、いわゆるFM変調を行う。

あとnoiseとか矩形波があるけど省略。OSCはそんな感じ。

VCF(Filter)

次にフィルタを見ていく。
チャンネル単位で掛けられるLPFなどのフィルタはsyFltRenderに書かれている。
ここには普通のLPF/HPF/BPF/NotchとMoog LPF/HPFがある。
LPF/HPF/BPF/Notchは基本の構成っぽいので説明を割愛。アナログ回路でもこれらのフィル対は単純化して言えば、RLC回路のどこの電圧を測るかだけの問題なので、プログラム上も同じ処理をしてどの数値を拾うだけかの問題。

フィルタ自体の原理は僕が大昔書いた
http://kmkz.jp/mtm/mag/lab/digitalfilter.htm
と似たようなもんです。今見ると数式が多いな。難しくないはずだけど、数式があると難しく感じる不思議。

moog filterが気になるけど、moogは全然分らないので説明できないな。資料をまず探さなきゃ。スピーチシンセのronanも見てないし(これはCだし読むのは容易いけど)。ということで、時間がなくなってきたのでまた続く!かも。

*PogoPlug+MPDで音楽サーバ構築

20120415 191028

前回までのあらすじ。

SH7262とか使ってちっちゃな音楽サーバでも作ろうかと思ったけど、SheevaPlug系+MPDが最強と気づいてしまった私。

SheevaPlug/PogoPlug

SheevaPlug とは何か。
小さくて消費電力が小さくLinuxが動く、EtherとUSBの口がついているだけのコンピュータだ。待機時2.3Wだから、1ヶ月動かしても50円くらいか?
玄人志向から玄芝として以前発売されたりしていたらしいが、現在は発売されていない。手軽に手に入るものはないかなー、と思ったら、PogoplugがSheevaPlugベースだと知る。

というわけで、POGO-E02(\5,500)を購入。






デュアルコアになったが、クロックが落ちてメモリが半減したPOGO-P25(\6,500)と迷ったけど、安さ優先にした。ただ、こっちのほうが改造すればSATAが繋がるので、NASとして使うならこっち。

DDC

DDCとしてFiiO D5 USB Digital Audio Decoder(\2,850)も購入。

Music Player Daemon

僕が以前から求めていたサウンドサーバ的なものが実はMusic Player Daemonという、そのものスバリな名前で既にあった。

起動するとTCPのポートが開くので、そこに色々命令を送り込める。これを演奏しろとか、これをプレイリストに追加とか、クロスフェードしろとか、シャッフルしろとか、そういった命令を送り込める。
既にクライアントが山ほど開発されていて、Web用UIもあればiPhone用アプリ(MPoD)もある。

しかも対応フォーマットがアツイ。mp3やFLAC,AACは当たり前として、libmodplugに対応、さらにMikModにも対応!って両方対応するとは開発者は相当なmod狂だ。さらに、libgmeにも対応。gymとかnsfとかspcが標準で対応とは!残念ながらmdxには対応してないので、これは後で勝手に改造しようと思う。

セットアップ記録

まずはPogoPlugを普通に設定して、SSH接続できるようにする。
適当なUSBメモリ(4Gは欲しい)を用意する。

その後は、
http://archlinuxarm.org/platforms/armv5/pogoplug-v2-pinkgray
をそのまま実行してarchlinuxをinstall。
archlinuxってのはコンパクトなlinuxのディストロ。Ubuntu入れても良いけど、メモリも少ないしarchlinuxが無難と思われる。

再起動後、sshでroot:rootでログインできれば、archlinuxが入ってる。
パッケージを最新にして音楽サーバから引っ張ってくるためのNFSと肝心のMPDを入れよう。

その前に、自分の環境ではmirrorサーバにうまく繋がらなかったので、
/etc/pacman.d/mirrorlist/mirrorlist

Server = http://mirror.archlinuxarm.org/arm/$repo
をコメントアウトして、
Server = http://us.mirror.archlinuxarm.org/arm/$repo
を有効にした。

pacman -Syu
pacman -S rpcbind nfs-utils nfs-common ntp mpd mpc
modprobe nfs

/etc/rc.conf
にインストールしたデーモンを追加。
MODULES=(... nfs)
DAEMONS=(... rpcbind nfs-common ntpd mpd)
/etc/fstabにマウントしたいファイルサーバのパスを追加。
ファイルサーバ側の/etc/exports等は別途編集しておく。

/etc/mpd.confを作る。
cp /usr/share/mpd/mpd.conf.example /etc/mpd.conf
mpd.confのmusic_directoryをマウントした音楽置き場とする。
sync && /sbin/reboot

これで再起動したらmpdが起動してるはず。
コマンドラインで
mpc update
としてDBを更新する。これは裏でしばらく動き続ける。
mpc status
とするとDBを更新中か分かる。曲数が多いとしばらくかかる。

mpc ls
とするとDBに登録した曲が見える。
mpdが扱うファイルは、OSのファイルシステムではなく、mpd.confで指定したmusic_directoryが基準となる点に注意。後は、
mpc add ukrock/oasis/1st/liveforever.flac
等とやれば音が鳴る。

ノイズ発生

これでまずは一件落着のはずが、自分の場合はノイズ混じりで聴くに耐えないことになってしまった。

topと叩くとmpdが異常にCPUを使っている。
試しにalsaplayerで再生したら、全く問題なし。5%くらいしかCPU使わん。そりゃそうだよな。

調べたらresamplerが遅いとのこと。下記を参考に設定を変更した。
http://mpd.wikia.com/wiki/Tuning#Samplerate_converting_-_or_why_does_ALSA_consume_much_more_CPU_than_OSS_does.3F

これでほぼ解消したけど、sshでアクセスしてファイル編集などでdisk ioが発生すると
ときたまプチプチ言うので、
audio_buffer_size               "2048"

audio_buffer_size               "8192"
とした。

以上でほぼ問題なくなった。

これで、iPhoneならMPoD等のアプリを入れれば、
遠隔で操作できるようになる。

けど、アンプのON/OFFやボリューム調整はこれではできないから、クライアントは後で自作する。

libgmeを有効にする

標準だとlibgmeが有効になっていなかったので、自分でmpdをビルドしてみた。
pacman -S base-devel
pacman -S libgme
wget 'http://downloads.sourceforge.net/project/musicpd/mpd/0.16.8/mpd-0.16.8.tar.bz2'
tar jxf mpd-0.16.8.tar.bz2
cd mpd-0.16.8
./configure --enable-gme
make
make install

338 results << 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 >>
<--