[VGMPlayer] WAVデータの生成

(実処理の内容を書けるほど理解できるか不明だが、チップのWAV生成処理周りが判ったら書く。)

 ストリームバッファの充填にも書いた通り “対応チップオブジェクト->StreamUpdate関数” がWAVデータ生成処理。
 実際はChips_GeneralActions関数(Mode=0x1)の処理時に CAA->StreamUpdate に設定される関数内で行われる。(例:sn764xx_stream_update関数)

音、波形、PCMデータとは。
また、基礎的なPCMデータ生成の方法。

 音と波形…基礎の基礎。
音と波形

 波形の量子化(イメージ)。
量子化イメージ

量子化イメージ_2

PCMデータはデジタル化した波形を、以下のような情報として記録する。
・音程について、一定時間毎の周期的な変化量で音の高さを保持。
・音量について、一定単位毎の音の大きさを保持。

量子化波形を、数値で表す
・中央の横線を基準に、上下に±幾つ という形で音の大きさを計る。

 上の図では、時間軸(水平)方向に13本の縦線が引かれている。
 13本の縦線は、それぞれある時点の音の大きさを表している。そして13本の縦線で1周期分の波を形作っている。
 仮に1周期が 1/1000秒だった場合は1秒間に1000周期なので、1kHz(=1000Hz)の音になる。
 波の振幅が大きいほど大きな音量になる。

 それから、記録時の周波数(サンプリング・レート)が仮に48kHzの場合、再生可能な最高周波数は半分の24kHzとなる。これは音が波であり、波には高い部分と低い部分が必ず必要になる(平坦だと波のない状態)。差があってこそ波が発生する。それには最低2つのデータがなくては、音が鳴らない。
 48kHzの場合、データ2個をサンプリングするのに掛かる時間は半分の24kHz分の期間が必要になるので、理論上の最高出力周波数も24kHzということになる。

シンプルな波形(ja.wikipedia.org/wiki/波形より)

Waveforms

PCM関連の情報や解説ページ

>PCMの基礎知識
http://www.hikari-ongaku.com/study/pcm.html

>波形を見る
http://aidiary.hatenablog.com/entry/20110519/1305808715

>JavaScriptで波をつくろう。リアルタイム波形生成&再生
http://d.hatena.ne.jp/yanagia/20100323/1269334226
https://github.com/yanagia/jsaudio

WindowsがPCM再生をどう行っているのか。

 WAVデータ&出音の原理の方を先に把握しないと、処理の意味が読み取れない..

>Waveフォーマット・PCM・サンプリング周波数・ビット数・チャンネル数とは
http://soundengine.jp/wordpress/tips/glossary/113/
>WAVとは
http://soundengine.jp/wordpress/tips/glossary/1944/

>WindowsPCが音を出す仕組み
http://www.spatiality.jp/pcaudio-research/pc-transport/software/windows-audio

>WAVEデータの作成と再生
http://www13.plala.or.jp/kymats/study/MULTIMEDIA/waveOut_create.html

>PCM の基本
http://wisdom.sakura.ne.jp/system/winapi/media/mm5.html

[VGMPlayer] ストリームバッファの充填を行う処理

 VGMPlayerはそもそも、以下のような動作イメージになる。

処理のイメージ

 で、VGMPlayerのキモの部分は、図のWAV生成の部分。
 ストリームバッファの充填は、以下の処理でWAV生成を行うことで実現される。

  • VGMデータを解釈しチップにデータを設定:InterpretVGM
  • チップ出力のストリーム情報を取得:ResampleChipStream
InterpretVGM関数

 VGMデータを先頭から順に解釈して対応するチップにデータをセットしていく。(VGMデータ仕様はここを参照)

 基本的に、並んでいるデータを順番にチップへ設定していくものなので、早送・巻戻などを無視すれば特に複雑な部分はないと思われる。
 ただVGMの対応チップ数が結構多いので、関数内の処理の把握をしようとした場合はそれなりに手間。

ResampleChipStream関数

 前段のInterpretVGM関数で設定されたデータによるチップの出音結果(32bit Stereo WAVデータ)を受け取る関数。
 処理的には “対応チップオブジェクト->StreamUpdate関数” を呼び出して、得たストリームデータの音量計算と出力バッファへの転送を行う。
 また、出力バッファへの転送については単なるコピー処理以外に幾つかのモード(OLD-resample, Up-sample, Down-sample)によるリサンプリング機能持つ。

[VGMPlayer] 処理の大雑把なところ

コードを準備する

 ソースコードをDLして適当な場所に展開後、VGMPlay.dswを開けばソリューションを開くことができる。

 Visual Studio Express 2013 for Windows Desktopでも可能だった。

 おためしで実行したければ、「ビルド > ソリューションのビルド」でDebugフォルダ下に VGMPlayer.exe が作成される。

コードの中身

 コードはC++で書かれているので、main関数から追っていけば大雑把なことは判る。

UIを使わないで…って考えた場合は、
最後の関数は PlayVGM(); でもよさげ。
ReadOptions関数も、初期設定でよければ不要。
バッファへのデータ充填は後述。

 PlayVGM_UI 関数は名前通りユーザー入力を受けて再生操作(一時停止, 早送, 巻戻..)できる作りになっているので、対応するストリーム生成処理にも初見だと若干判りにくい部分を感じる。
 ただ、やっていることはそんなに多くないので、順を追えば把握できると思う。

  • キー受付
    • キー受付に付随する各種操作
  • サンプル時間の計算
  • ストリームバッファの充填
ストリームバッファの充填

 出音については「サンプル時間の計算~ストリームバッファの充填」が判らないとどうにもならないので、UI処理は基本忘れてストリームバッファの充填をいかにして行っているかの理解に努めたい。
 ストリームバッファの充填に必要なパラメータとしてUI処理を理解しなければならない場合は、追々解析を行えば良いかと思う。

 そしてストリームバッファの充填は、その名の通り FillBuffer 関数が行っている。
 当該関数の内部動作を中心に理解を進めれば、大雑把に動作を把握できるのではないかと思われる。(当該関数が引数に取るものは、16bit Stereo Buffer 及び Buffer Size)
 ただしコードはオブジェクト指向なスタイルではなく、ちょくちょくグローバル定義の変数にアクセスするので、よく使われる変数の正体を逐次類推しながらの作業になる。
 関数・変数・定数各々の機能については、把握できている事柄を順次コメントに追記しながら進めていった方が良いが、extern定義の場合には参照元の定義にコメントを集約しておきたい。

[android] JNIとは

自分はiPhone持っていないのもありandroidの開発環境を調べた。
その際のめも。

JNIはJava Native Interfaceの略称で、Javaとネイティブコード(C/C++)を連携するための仕組みです。共有ライブラリからの動的読み込みをサポートしています。

>Android開発者のためのJNI入門
http://techbooster.org/android/application/7264/
http://developer.android.com/training/articles/perf-jni.html

>Android NDKを使用してJava言語とC言語で速度比較をする
http://labs.techfirm.co.jp/android/iguchi/1782

andoidはJavaVM上でアプリが実行されるのが基本。開発にはJDKを用いる。
Javaは専用のVM上で動くことを前提にしているので、CPUに依存せず様々なCPUを使った端末が用意できる。というのがウリ。(最近のandroidはARM系CPU一色だが…)

一方JNIは、特定のCPU向けに生成したネイティブコード(.soファイル)をJavaから呼び出せるようにする仕組み。ネイティブコード開発にはNDKが必要。
特定のCPUに依存する代わりに、実行速度を稼ぎやすいのがメリット。

但しJavaの場合は気にしなくて済むいろいろな些末事を自分で処理しないといけない…という面倒さはある。導入時、いろいろ覚えることが多そう。

オブジェクト指向プログラムのスタイル

教育大学でやっていた。
こういう説明もあるか~と思ったので、めも。

プログラムは、
“オブジェクトとのメッセージのやりとり” で記述される

 …という思想が採られる。

 オブジェクトは “クラス” により定義され。
 クラス定義には、オブジェクトの保有する(メンバとなる)変数やメソッドが列挙される。

 クラス定義の後に、メインルーチンとしてプログラム本体が記述される。

メインルーチン内では
“オブジェクトに対するメッセージ” という形で、命令が記述される。

 メッセージの基本的な書式は、
 ”オブジェクト名.メソッド名()” という形を採る。


後日、こんなページを見つけたのでめも。
メッセージ脳の作り方
メッセージ指向のイロハ

VBSでWIN32APIを利用する

VBS単体ではセキュリティリスクの都合から簡単にWIN32APIが利用できない。
そのへんを解消するフリーのライブラリを見つけたので、めも。

SFCmini

>こっちはWin32API定義がざっくり全部入りの SFCmini ラッパー
JavascriptでWin32APIが利用しやすくなる『win32api.js』

>その他
コマンドプロンプトから,Win32 APIや任意のDLLを呼び出して実行しよう (コマンドプロンプトから画面キャプチャする方法の仕組みを理解)


イケてない問題が発覚..ちょっと使いづらいな~orz

ΔΣ変調って、なんぞ?

ずいぶん前にDSDを調べていた時に出てきた、ΔΣ変調のことを思い出した。
そもそも、これはどんな技術だったのか。。

低ビットレートのPCMだと、歪んだモニョモニョ・ピンピンと変な響きでくぐもった音に変わる。
あの変な風に音が変わってしまうのが誤差の影響で、ビットレートを極端に高くすると、それが人間の耳では判別できないようなレベルになる…という理屈が元にあって。。

最近のデジタル機器の高速化に伴って、量子化ノイズを極限まで小さくできる目処が立ってきた。
…ってことで、DSD技術が成立しているって感じか。。

デジタル機器の技術発展は本当に凄いな…デジタル・フロンティアって感じ。

ヒラギノを少しスマートに表示

Macだとゴシック体(ヒラギノ角ゴ)がちょっとゴツく表示される..
と思ったら単純に設定があったので、めも。

http://blog.nakatanigo.net/archives/50768904.html

ついでに下記サイトに倣って、もっとゆるく丸ゴシックにしてみた。。

http://ez-sparrow.com/windowsとmac両方で丸ゴシックを表示させる/


Win7では大丈夫だったのに、Win8.1で見ると横棒が縦に伸びてて汚い表示になっていた..
結局最後はターゲット用意して確認しないとダメか..ってことで、角ゴに戻した..

角ゴに関しては、最終的に下記を参照

http://loconet.web2.jp/blog/archives/2007/02/cssfontfamily.html

モバゲーblog…勉強になります。

はじめて読んだけど、アークタンジェントで角度出すとかFIRフィルタとかさらっと具体的な用途とか書かれてたりして結構おもしろげ、めも。

http://developers.mobage.jp/blog/2015/using-the-accelerometer

ほかの会社にも似たようなblogはいろいろあるんですね。

>gree
http://labs.gree.jp/blog/

>msdn
http://blogs.msdn.com/
>MSDN Blogs > Windows Multimedia Hacks > 音声再生速度変換 : もっとゆっくりしゃべって!
http://blogs.msdn.com/b/windows_multimedia_jp/archive/2009/03/04/9458418.aspx
>MSDN Blogs > ひにけにGD > クォータニオンの使いどころ
http://blogs.msdn.com/b/ito/archive/2009/05/01/more-bones-04.aspx

出力される電流の量を求める


 ネットを眺めていて、以下のような回路図で出力される電流を回答する設問を見つけて、計算を勉強するのによさげだったのでやってみた。

question

 最初は、公式も忘れていて以下のページを見たりしながら考えていたんだけど、そもそもどうにも理解できてなかったので、計算方法が全く出てこなかった。

 http://www.max.hi-ho.ne.jp/lylle/denryu4.html
 http://www.max.hi-ho.ne.jp/lylle/omu4.html

 ただ電圧を判り易く説明したこの図を見て、ようやく少し理解。
 http://www.max.hi-ho.ne.jp/lylle/denryu5.html

 電圧は..
・直列なら分け合う。
・並列だと同じに掛かる。

 各抵抗に掛かる電圧を計算できれば、電流も計算できるはずなので、各抵抗の電圧を計算してみたのだが..

 この時「電圧の分配を位置エネルギーでイメージする(参考)」の項に書かれている図が、これまた非常に判り易かった。

 その図に倣って整理すると、こうなる。

answer1

 これで、直列・並列の関係がある程度判別しやすくなりました。
 順番に繋がりを挙げてみると、

・R1とR2 が直列。
・R1とR3以下(R4,R5) が直列。

・R2とR3以下 が並列。
・R4,R5 が並列。

 以上のようになりました。
 並列部分は同じ電圧が掛かるので良いとして、直列部分では部品同士で電圧を分け合うので、部品ごとに実際の電圧を求めます。

・R1&R2, R1&R3が直列なので16Vが半々に掛かり、各8V
・R3&R4, R3&R5が直列なので、8Vが半々に掛かり、各4V

 これにより、各部品に以下の電圧が掛かることに。

answer2

 電圧が判れば、電流の計算は $$I = \frac{V}{R}$$ で求まります。

 設問は、電源からの電流の値なので、直近の R1 へ流れる電圧から計算できます。

$$I = \frac{V}{R}\\
I = \frac{8}{2}\\
I = 4$$

 電流の値は 4A


 …と、ここまで書いてみて考えてみる。

 R2とR3以下って抵抗値が違うんですよね。。これ同じ電圧が掛かるの??

 で、調べてみると、こんな電気工作のページを発見。
 http://www.kotaden.com/stage1_12_index.html

 どうやら、抵抗値に関係なく部品の接続が完全に並列な部分には電圧が等しく掛かるようです。(←これは早とちりだった..)
 でも流れる電流量は異なるので、これによって作動したり作動しなくなったりする、と。

 動的に制御できる回路を組めば、いろいろ動かせるってことですな。


 でも…R2とR3以下では抵抗の値が違うから、上の計算間違ってる気がする。。

 調べてみると、説明を見つけた。
 http://www.eonet.ne.jp/~hidarite/me2/denki01.html

・直列回路内では、電流は一定。
・並列回路内では、抵抗の大きさに反比例して分流。

・直列回路内では、抵抗の大きさに比例して電圧を分配。
・並列回路内では、電圧は一定。

 ということは、やっぱり繋がっている抵抗値を考慮に入れないと正確な数値が出ないんですね。。

 R1の電圧を V1 とすると、以下の式で計算される模様。

$$V1 = \frac{R1}{R1 \times RS} \times E$$

 RSの合成抵抗は、以下の通り。

$$\frac{1}{RS2} = \frac{1}{4}\\
\frac{1}{RS3} = \frac{1}{2}\\
\frac{1}{RS45} = \frac{1}{4} + \frac{1}{4}$$
※↑ 「1/RS」 のように逆数なので、直列計算時は再度逆数にする。

$$RS345 = RS3 + RS45\\
\frac{1}{RS} = \frac{1}{RS2} + \frac{1}{RS345}$$

 で、値を計算。。

$$\frac{1}{RS3} = \frac{1}{2}\\
RS3 = 2$$

$$\frac{1}{RS45} = \frac{1}{4} + \frac{1}{4}$$

$$\frac{1}{R0} = \frac{1}{R1} + \frac{1}{R2}\\
\frac{1}{R0} = \frac{R1+R2}{R1*R2}\\
R0 = \frac{R1*R2}{R1+R2} より..$$

$$\frac{1}{RS45} = \frac{4 + 4}{4 \times 4}\\
\frac{1}{RS45} = \frac{8}{16}\\
RS45 = \frac{16}{8}\\
RS45 = 2$$

$$RS345 = RS3 + RS45\\
RS345 = 2 + 2\\
RS345 = 4$$

$$\frac{1}{RS2} = \frac{1}{4}\\
RS2 = 4$$

$$\frac{1}{RS} = \frac{1}{RS2} + \frac{1}{RS345}\\
\frac{1}{RS} = \frac{1}{4} + \frac{1}{4}\\
RS = \frac{4*4}{4+4}\\
RS = \frac{16}{8}\\
RS = 2$$

下記の式より、
$$V1 = \frac{R1}{R1 \times RS} \times E$$
※Eは、部品に掛かる電圧の合計。

$$V1 = \frac{2}{2 \times 2} \times 16\\
V1 = \frac{16}{2}\\
V1 = 8$$

 R1 と RS の抵抗値は共に 2Ω なので、直列で繋がったこの部品には半々に電圧が掛かる。
 したがって、V1 = 8V であることは間違いなかった。

 でも、RS部分の電圧の計算は(結果的に合うように回路が組んであっただけで..)そもそも計算できていたわけではなかった..勉強が足らんですね..orz


並列で繋がっている抵抗について、基本的な捉え方は下記のような感じで良い。

抵抗が2個並列で並ぶ場合で考えると、管2本があるのと同じ…という考え方で良い。
管一本が一定時間に1リットル流れるところ、2本ならば2リットル流れる。
2本の場合の抵抗は、1本の場合の半分と考えることが出来る。

抵抗も考え方は同じ。

仮に、4Ωが並列に2個並ぶ場合、2本合わせて1つと考える。
その際の抵抗値は、半分の2Ωとなる。


直列の場合は、単純に足し合わせるという考え方で良い。

バイナリツリーと探索手順

 バイナリツリーを使った通りがけ順の探索を利用すると、「ソートされた値を拾うことができる」という特徴があるそうなので、豆知識的に書いておく。

 前に書いた 探索木 を読み返していて、この間放送大学でバイナリツリーと探索順の話をしていたなぁ..と思い出した。

探索木については、こちらも参照のこと。

探索順とは..?

 探索順というのは、ツリーの情報を巡回する際の優先度のことを言います。
 また、巡回とは、データを順に見て回ることです。

 下図でいえば、「10」から順番に線を辿りながらデータを参照することを巡回と言い、辿り方の優先度のことを探索順と呼んでいます。
2分探索木

 バイナリツリーは、2分木と呼ばれるツリー構造を持ちます。
 データの構造は、最低限以下のような情報を持っています。

  • データの値
  • 自分より小さい値の位置
  • 自分より大きい値の位置
位置として保持する情報によって、関連するデータ(ツリーの分岐先の情報)を紐付けています。
探索時は、この紐付けを辿っていきます。

いろいろな探索順序

 探索順には幾つか種類があって、それぞれ名前があります。

  • 行きがけ順
  • 通りがけ順
  • 帰りがけ順

 それぞれは以下のタイミングで保持する値を集めるのですが、通りがけ順を用いるとソートされた順序を得ることができます。

  • 行きがけ順は、データを取得する毎に値を参照。
  • 通りがけ順は、小さいデータを取得した後に値を参照。
  • 帰りがけ順は、小大両方のデータを取得した後に値を参照。

 前述の図の場合だと、各手順では以下のような順序が得られます。

  • 行きがけ順:10,6,2,13,11,21
  • 通りがけ順:2,6,10,11,13,21
  • 帰りがけ順:2,6,10,11,21,13
バイナリツリー構造とは..?

 これだけの説明だと、なんのことやら..という感じだと思います。
 少し詳しく書いていくと、まずツリーは葉(leaf または node)枝(branch)という要素で構成されます。
 前述の図で四角で囲った箱が、青い線が枝(分岐)です。
 一般的には、葉はノードと呼び、1個分のデータを指します。

tree_node

 2分木ツリーは、このノードを連結させて作成します。
 イメージは、以下のようなものになります。

tree_connection

 青い線の続いている場所は関連があり、なにも続きがない場合は関連情報が入っていません。

行きがけ順での動き ~ 括り

 行きがけ順で追った場合、以下の順序で処理します。

  • “10”のノードを取得
    • 値(10)を取得
    • 小さいノード(6)を取得
      • 値(6)を取得
      • 小さいノード(2)を取得
        • 値(2)を取得
        • 小さいノード取得(なし)
        • 大きいノード取得(なし)
      • <ノード(6)に戻ってくる>
      • 大きいノード取得(なし)
    • <ノード(10)に戻ってくる>
    • 大きいノード(13)を取得
      • 値(13)を取得
      • 小さいノード(11)を取得
        • 値(11)を取得
        • 小さいノード取得(なし)
        • 大きいノード取得(なし)
      • <ノード(13)に戻ってくる>
      • 大きいノード(21)を取得
        • 値(21)を取得
        • 小さいノード取得(なし)
        • 大きいノード取得(なし)

 同じ要領でも値の取得タイミングが異なれば、取得される値の順列が変わる訳ですね。

  • 行きがけ順:小さいノード取得の前
  • 通りがけ順:小さいノード取得と大きいノード取得の間
  • 帰りがけ順:大きいノード取得の後

 そして通りがけ順の特性として、ソートした結果を得られる..と。

 ちなみに、ちゃんとツリー構築時に値の大小を正しく格納出来ていなければ、ちゃんとソートされた結果にはなりません。。

 また、再帰呼び出しやデータ・スタックを行うメモリが少ない環境の場合には、各ノードに親ノードを記憶させる領域を取り、元のノードに戻れるようにしておく必要があります。

カリー化

 A Short Practical Guide to Blocksで判らないでいたカリー化。

 モヤっとしていたので、もうちょっと調べてみた。(というか同じページに書いてあったんだけど..)
 ⇒カリー化

 結局のところ、以下のように書かれている通りなのだろう。

カリー化を利用すると、複数の引数をとる関数を、一つの引数のみを取る複数の関数のラムダ計算などの単純な理論的モデルと見なして研究できるようになる。

 問題を単純な形に切り分けて処理する為の技術って感じ。
 逆にいえば、問題を単純な形に切り分ける力も必要..と。

 例えば wikipediaに以下のように書かれているように、第1引数と第2引数を渡す関数をそれぞれ別に定義・関数化することで、引数を1つしか取らない関数2つに処理を分けている。
 ※関数:cdiv(x)と 無名関数:function(y) の2つ。

 また、カリー化する意味も “inv”変数関数を作って見せることで証明している。
 ※併せて説明されている理論的モデルは wiki参照。

 事象を単純化した数学モデルに置き換える作業が必要な場合には、威力を発揮すると思われるが、そういうことをしない人間なので正直便利さが全く理解できないでいる..


 JavaScriptで複数キーでソートする処理って..とか思ってたら、早速関数を引数に取る必要が出てきた。まぁ関数を引数に取るだけで、カリー化とは関係ないか..
 とはいえ、こういうのをさらっと1から書けないなぁ。
 ⇒http://stackoverflow.com/questions/1129216/sort-array-of-objects-by-property-value-in-javascript