2011年12月15日木曜日

FLACファイルのReplayGain情報を利用して音量正規化(ノーマライズ)

前提となる環境は以下のようなものです。

  • Windows7 Home Premium 64bit
  • 音楽ファイルをPCではFLAC、携帯プレーヤー(COWON S9)ではOgg Vorbisで管理
  • FLACファイルに付与したReplay Gain情報を利用して、Replay Gain非対応のS9でも音量を正規化したい

音源によって異なる音量を一定に保って再生する方法として、Replay Gainがあります。これは音楽ファイルにタグとして音量を記録しておくもので、再生側が対応している必要があります。PCであれば対応ソフトウェアがけっこうありますが、携帯機器となると話は別。私の使っているCOWON S9は未対応…。

もちろんReplay Gain以外の方法で音量正規化をかけることもできます。有名なのはMP3Gainでしょうか。ただこれは対応ファイル形式が.mp3限定。AACGainなるものもあったりしますが、私の使ってるFLACとOgg Vorbisに対応してるものは少ないです。ないことはないんですが。

で、ふと「音量正規化するソフトがあるんだったら、エンコーダに正規化機能が付いてるんじゃないか」と思って調べてみました。

2011年11月24日木曜日

アセンブラを作る

既存のプロセッサならアセンブラは用意されていると思います.自分で仕様を決めたりして独自命令セットになっている場合の話.方法は色々ありますが,とりあえず自分のやった方針だけ.スマートではないです.

方針

  1. アセンブリコードを1行ずつ読み込む.
  2. 読み込んだコードからコメントと空白行を除去し,中間ファイルに出力.
  3. 中間ファイルから1行ずつ読み込んでラベルを探し,出現する行数とセットで記録する.
  4. もう一度中間ファイルから1行ずつ読み込んで機械語へ変換する.分岐命令では3.で記録したラベル情報を参照する.

出来上がったあとで思ったこと

アセンブラを書いたあとの講義で,パイプラインプロセッサの命令スケジューリングを行う課題が出ました.そのとき先生の用意したコードを見て思ったことです.

  • 中間ファイルに出力する必要はない.命令を格納する構造体を作っておけばよい.
  • 最初の読み込み段階でラベルのリストアップも同時に行うべきだった.
  • ラベルの記録に連想配列が使えたらもっと楽だった.

余談

  • 先生から「スクリプト言語を習得したほうが後々便利.C系とスクリプト系を一つずつ使えれば,ちょこっとプログラム組むぐらいのことにはほとんど対応できる.」と言われました.
  • テキストエディタの置換機能を使って,アセンブラとして動作するマクロを作っている人もいました.その手があったか….一本取られた感じ.

2011年11月22日火曜日

ラプラス変換を使って一般解を求める

2階の微分方程式を想定.考え方は単純で,A, Bを任意定数として y'(0)=A, y(0)=B と設定するだけ.あとは普通にラプラス変換を使って解けばよい.

10進から2進への変換

※ 私の発想ではありません.どこかで見かけただけです.

考え方

  1. 変換したい10進数と比較用の数値1,カウンタ変数を用意する.カウンタは0で初期化.
  2. 10進数と比較用数値のANDをとる.結果が0でなければカウンタの示すビットに1を立てる.
  3. 比較用数値を1ビット左へシフト.カウンタをインクリメントする.
  4. すべてのビットが確定するまで2, 3を繰り返す.

10進から2進へ変換できる理由

  • 10進といってもコンピュータ内部では2進表現であり,これを文字として書き出したいだけ.つまり各ビットごとに1か0かを判定すればよい.
  • あるビットを調べたければ,そこだけ1, 残りを0で埋めたものとのANDをとる.(他のビットをマスクする)
  • 条件判定式は0かそうでないかを見る.
    ex. while(1)でも,while(100)でも同じこと.

変換指定子に%bとかあればいいのに.

改行コードについて

改行コードは歴史的経緯からCR, LF, CR+LFの3種類ある.ASCIIではCRに0x0D(\r), LFに0x0A(\n)が割り当てられている.3つのうちどれが出てくるかは処理系によるので,予想できないとして扱うべきかと.

注意事項とか

  • シリアル通信などで改行を扱うときは注意.万全を期すならバイナリモードで出力するべきらしい.でも"\r\n"で動くんだからそれでもいいと思う.Linuxでしか使わないプログラムだったりとかすると割とどうでもいいのかもしれない.
  • Cの文字列探索で改行を調べるとき,"\n"だけを調べると処理系によって結果が変わる.無限ループが発生したりする."\r", "\n", "\r\n"のすべてを調べること.

termios.hについて

termios.hはLinux標準のシリアル通信用Cライブラリ.シリアル通信用の構造体termiosが定義してある.termios構造体にはメンバとして通信速度やキャラクタ長,ストップビットなどの通信設定が格納される.シリアルポートに相当するファイルは "/dev/ttyS0" とか,USB-シリアル変換を使ってると "/dev/ttyUSB0" になったりする.変換はだいたいPL-2303チップで,Linuxだと標準でドライバを持ってることが多い.要するに刺すだけでudevが対応するファイルを生成してくれて,使えるようになる.

簡単な使用手順

  1. シリアルポートに相当するファイルをopen()で開く.fopen()じゃなく,低水準入出力を使う.そのためファイルポインタじゃなく,ファイルディスクリプタが返ってくる.こいつはint型.エラーのときは負の値になってる.
    ※ この時点で失敗するときはだいたいパーミッションが原因.上記仮想ファイルのパーミッションを666とかに設定しとくこと.パーミッションは再起動とか変換アダプタの抜き差しでリセットされる(ファイルが再生成されるから)ので,その都度設定するか,udevの設定ファイルをいじくっておく.
  2. tcgetattr()を使って現在の通信設定を適当に宣言したtermios構造体に退避する.
  3. 2.で使ったやつとは別のtermios構造体に必要な設定を行う.メンバは単に値を代入するものと,各ビットがフラグとなっているものがある.詳細は"termios 設定"とかで調べる.
  4. tcflush()でポートをクリアしてから,tcsetattr()で設定を適用する.
  5. write()で出力する.
  6. read()で応答を読み込む.接続機器の設定によるけど,応答の末尾1, 2文字は多分改行コード.これは'\0'に置き換えた方が幸せだと思う.
  7. tcsetattr()で退避しておいた設定を復帰させてから,close()でファイルを閉じる.

その他注意事項とかメモとか

  • カノニカルモードと非カノニカルモードがあるけど,カノニカルモードの動作がよく分からなかった.というか,カノニカルモードで安定して動作させることができなかった….
  • パーミッション周りは自分のようなLinux初心者がよくはまると思う.まずは権限を疑うこと.
  • termios構造体のメンバのビットを操作して設定する,というのがちょっと分かりにくかった.マイコンのプログラミングやってるとすぐに把握できるんじゃないかと思う.
  • 通信するだけならすでにツールがいろいろあるので,そっちを使った方が幸せ.