GAMDX改造:ADPCM変更の効果は?

このところGAMDX改造の話題が続きましたがそろそろ終わりになります。

もとのGAMDXではADPCMの15.6KHzの音源を内部周波数の62.5KHzにアップサンプリング、その際にローパスフィルターを通していますが何故かpcm8.cppおよびX68pcm8.cppという2つのルーチンで2重にローパスフィルターを通っています。ローパスフィルターのパラメータは固定なので、アップサンプリングする周波数が変わってしまうと問題が生じそうです。また、2回LPFを通るので音の歪みなどが必要以上に乗ってしまう可能性はあるかもしれません。追記:LPFの効果を強めたい場合、2つのLPFを直列につなぐのは「あり」のようですので、音質の劣化はあまり心配しなくていいかもしれません。

この処理を少し変えて、GAMDX内部での周波数(可変にしてみました)に合わせてアップサンプリング、そのあとADPCMが15.6KHzなのでその半分の7KHz以上の成分はノイズと考えられるのでアップサンプリングした周波数に合わせてローパスフィルタを通す、という風にしてあります。

フィルタが変わったことで音質の比較のため内蔵の「X68030のテーマ」のボイスパートを使ってみました。ADPCMの比較をするために、FM音源のところはプログラム上でカットしてあります。

1)もとのGAMDX(MDX player 2.0, 62.5KHz出力を使用。したがってダウンサンプラーはAppleのものが使われます)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-48-27

出だし約10秒間を詳しく周波数分析したもの

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-48-42

2)改造版、ダウンサンプラーはAppleの物を使用

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-41-01

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-41-15

3)改造版、ダウンサンプラーはSpeex(Quality=2)を使用

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-43-27

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-43-57

4)改造版、Speexリサンプラー(Quality=4)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-56-30

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-17-56-57

 

SpeexリサンプラーはQuality=2と4でほぼ同じ結果のようです。あとでソースを読んで見る必要あり。AppleよりもSpeexのほうがローパスな傾向があるのと、私が改造したADPCMルーチンは8KHz以上の本来はカットされるべき部分が結構残っているようなのでLPFのパラメータはいじる余地がありそうです。

GAMDXのリサンプラーをいれかえてみる

前回に引き続きGAMDXの改造を継続。

まずADPCMをいじっているときに、内部で使用しているSample型が16bitに設定されていたのでPCMデータがオーバーフローする状況に遭遇。ローパスフィルターを整理するとともに極力データのロスがないように、原則データロスにつながる操作をしないようにしてみた結果なのですが。ということでSample 型は32bitにしてあります。

そしてもともとのダウンサンプラーの性能が良くないと指摘してAppleの(iOS組み込みの)ダウンサンプラーを使っていたのですが、他のOSに移植することもあるかもしれませんし、ダウンサンプラーを差し替えてみようと考えました。

ダウンサンプラーとアップサンプラーをあわせてリサンプラーというのですがそのものを総合的に解説したHPがあります:Digital Audio Resampling Home Page

色々解説が書いてありますがこれを勉強して一から書くのは相当大変というか無理です。ということでこのHPのFree Resampling Softwaresというところでフリーの実装を物色してみます。SoXというのが有名らしいがかなりボリュームが多いしGPLだ・・と思っていたら一番下の方でSpeexライブラリがBSDライセンス(商用利用も可能)となっています。Speex自体はVoIPで音声を圧縮して通話することが主目的のライブラリですが、リサンプラーも半ば独立したものがあるみたい(Speex自体は開発終了していてOpusという新しいプロジェクトに移っているようですが、リサンプラーはあまり変わらないようです)。通話目的のプロジェクトなので、速度も重視されているようです。

ということで秋の夜長、Speexのresampler.cを移植してみました。1.2rc3をダウンロードして、そのものズバリresampler.cというのを入れてみて、コンパイルエラーが起こらないまでにヘッダーを持ち込みます。.cファイルは一つだけというのが好感が持てます。リサンプルの精度は0-10で選べます。ARMのNEON命令セットに対応しているようですが、今はうまく動かないようです。

GAMDXの内部にはMXDRVG_MakeResampler,  MXDRVG_ClearResamplerという2つの関数を追加しただけで、他はほぼ透過的にリサンプルする・しないが選べます。もとのGAMDXではMXDRVG_Startという関数で出力周波数を指定しますが、ここでは内部処理の周波数を指定して、出力周波数を決めるのはResamplerでという風にしたのでフレキシブルになっていると思います。

手元にUSB-DAC/ADCがないのでリサンプラーごとの性能比較が今はできないので後日。

先述の通りSpeexのリサンプラーは精度が0-10で選べます。デフォルトは4になっていて,0では線形補間のレベルと書いてあります(が、ソースの中をちゃんと読んでいないので後の課題にしておきましょう)。5以上のレベルはCPUにかかる負荷が重くなりそうなので、レベル4,2,0について調べてみました。62.5KHzの内部周波数で、48KHzにダウンサンプリングして出力したものです。

レベル4

level4

レベル2

level2

レベル0

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-11-16-26-00

もとのGAMDXリサンプラーと直接比べられるように同じ曲を調べてみました。ノイズは明らかに減っていて、Appleのリサンプラーとあまり違いないレベルでしょうか。レベルごとの違いについて、実際に耳で聞いたところでもあまり変わらないようですが、CPU使用率は結構違います。iPhone5sではレベル4でCPUの使用率30%程度、レベル0で20%程度となります。レベル2くらいでいいのかもしれません。

なお、前回の48KHzの出力で1)GAMDX内部は62500Hz、Appleのリサンプラーで48KHzに変換 3)GAMDX内部から48KHzで処理、を比較してみる話ですが、手元にあったVeyrlen氏のSonic Boom Stage 1 (高校生の時に松山銀天街のセガでやったのを思い出す・・・)はあきらかに1)のほうが良いみたいです。ほかの曲では大きな違いがない様に聞こえるのですが。

ということで現時点での結論としてiOS についてはベストの音質は62.5KHzにApple resamplerをかましたもの、となりver2.0からはあまり大きな改善は見込めなさそうです。他のOSについてはSpeex resamplerがうまくいけば音質向上になると思います。

GAMDX改造の効果は?(続き)

昨日に引き続き、今度はFM音源の出力を本来の62500Hzではなく48000Hzにしたときの比較をしてみます。

1)MDXPlayer for iOS ver2.0 62.5KHz: GAMDX  FM音源62500Hz  – iOS内蔵のダウンサンプラー経由で48KHz出力

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-06-17-07-11

2)MDXPlayer for iOS ver2.0 48KHz: GAMDX  FM音源62500Hz  – GAMDX内蔵のダウンサンプラー経由で48KHz出力

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-06-17-07-57

3)改造版:GAMDX 改造して FM音源48000Hz  -ダウンサンプラー通さず48KHz出力

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-06-17-06-58

ということで、周波数帯の灰色の部分は無音に近いということになりますから、2)は全体的にホワイトノイズが乗ったような感じになっているといえます。ダウンサンプラーを通さないことで余計なノイズは乗らず1)と3)ほぼ互角のように見えます。ただ、FM音源の出力を間引いて出力しているかもしれないので、細かい波形の歪みが出ていないかが気になります。192KHzでキャプチャして、こまかいところまでみてみました。なお、本日使用した曲はVeyrlen氏によるS.D.I.のAn Imminent Warです(FM音源の名曲ですので対決にちょうどよいかと)

前述の1)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-06-17-24-53

2)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-06-17-26-41

3)

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-06-17-24-39

うーん、少なくとも波形の極端な歪みなどはなさそうですが、比較が困難です。すくなくとも、FMgenから48000Hzで出力しても、大きな破綻はないと考えていいでしょうか?

あとは62500Hzの原音と48000Hzの音の波形を比べてみるのが良いのでしょうが、そうなるとまずMacのかんたんなプログラム(コマンドラインから起動?)で62500HzのデータをWAVなどの形でダンプするプログラムを作って、1)のダウンサンプラーを通したほうが良いか、3)のFM音源エミュレータから48000Hzで出したのでかまわないのか、比較する他ないかと思います。あとはご自身の耳でも聴き比べてみてください。

MDX音質向上研究:GAMDX改造

前回の続きでMDX Player for iOSの音質向上を図ってみます。

まず前提として、X68内部ではFM音源は62500Hz, ADPCMは15625Hzで出力されています。

MDX Player for iOSではGAMDXエンジンを使用していて、FM音源とADPCM音源をミックスしています。GAMDXエンジンの内部では

  1. FM音源は62500Hzで生成、
  2. ADPCM音源を62500Hzになるよう4倍化した上でフィルタをかけて高音域に乗ったノイズを除去、
  3. FMとADPCMをミックスした上で目的の周波数(44100Hzとか)にダウンサンプル、

となっています。前回指摘したのは、このダウンサンプルのところでノイズが乗ってしまっているのでは、ということでした(周波数変換自体は非常に奥深い問題で、速度と品質のトレードオフなどもあり一筋縄では行きません。各種の周波数変換ルーチンの比較をするサイトもあります)。

前回、MDX Player for iOS v2.0では62.5KHzの出力にした時は、内部のダウンサンプルルーチンを使わずにAppleのダウンサンプルルーチンを使うようになっていて、その結果音質が向上していることを確認しました。その結果をふまえて、GAMDX 内部をいじってみて音質向上を図ってみます。v2.0にはせっかく22/44/48/62KHzと切り替える機能もついているので、内部的な周波数を変更できるようにしてみました。

まずGAMDXでは内部的に62500Hzに統一していますが、これを可変にしてみます。実はFM音源のエミュレートをしているFmgenは周波数が変わっても出力は可能(周波数が下がると音質も落ちますが)です。また、ADPCM音源のエミュレートはX68pcm8.cpp, pcm8.cppという2段階で行われていますが、何故かローパスフィルタを2回通っています。またローパスフィルタの係数は固定されているので、内部の周波数が変わったときにうまくいかなくなると思われます。ということで、改造したエンジンでは

  1.  FM音源は指定した周波数 xxHzで生成
  2. ADPCMをxxHzにアップサンプリングする。途中までは余計なフィルタは通さず、FM音源とミックスする直前にローパスフィルタ(指定の周波数xxHzによりパラメータを変更するタイプ)を通す
  3. 直接xxHzのデータをiOSに渡して再生してもらう;iOSの機能で必要に応じて44KHzや48KHzに周波数変換してくれるし、もし44KHzで生成すればダウンサンプリング不要となるのでそこでのロスがない

というかたちにしてみました。62KHzでは素のver2.0とほぼ同様です。22, 44, 48KHzの存在意義ですが、再生ルーチンの負担が軽くなるので62KHzに比べて22KHzではCPUにかかる負荷が半分くらいになるようです。なお、MMDSP風の画面更新は周波数に比例した頻度で行うので、22KHzだと画面はスローがかかったようにみえるかもしれません(Frame per secondがひくい)。

再生音質の比較をしたいのですが、わたしはiPhoneを1つしか持っていないので改造版ともとのやつを比較するのにXCodeでプロジェクトを切り替えてインストールして・・・を反復しないといけません。本来なら、GAMDXエンジンで62500Hzを44100Hzにダウンサンプルした場合と、はじめから44100Hzで生成してダウンサンプルしない場合とを比較した方がいいのでしょうが。

もう少し時間のあるときにいろいろ調べてみますが、今はとりあえず新規導入したADPCMのローパスフィルタの性能を評価してみました。ADPCMのみのパート(アサルト2ステージ出だしのドラムだけのところ)を周波数分析してみます:

1)ローパスフィルタを通さずにそのままま出力:15625Hzを中心にエイリアシング(折り返し雑音)が見られます。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-05-16-00-14

2)もとのGAMDXのエンジン:ローパスフィルタが効いています。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-05-16-50-54

3)今回導入した可変ローパスフィルタ:7KHzより上の周波数は本来は雑音ということになるので2)のフィルタよりも少し性能は悪いかもしれませんが、2)のフィルタは前述の通り2回のフィルタ操作を通っているのでもう少し素直な音がしてくれることが期待できるかも・・・??

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-05-16-00-25

 

ということで、ここまでの変更はhttps://github.com/sinn246/MDXPlayer/tree/HiDefにあげておきます。

GAMDXの内部構造としてはローパスフィルタを別ルーチンにしたのでPCM周りは少しスッキリしたと思います。あとは優秀な周波数変換ルーチンがあればAndroidなどでも音質向上できるはずです(と言うか、私が加えた変更よりも周波数変換ルーチンの差し替えだけでも十分かもしれません)。

続きがあります

MDX Player for iOS音質向上研究

MDX Player ver 2.0 から再生周波数の切り替え機能(22,44,48,62Khz)がつきました。

22, 44, 48KHzはわかりますが(CD,DATなどの周波数で一般的によく使われる)、62KHzというのは何かというと、X68000のOPMチップに与えられる4MHzの周波数を64分周したADPCMが15.6KHzなので内部処理を楽にするために?44,48,62KHzのときは15.6×4=62.5KHzがOPMチップの出力周波数となります。ということでGAMDX(MDXドライバ)内部では62.5KHzで処理しており、そのデータをそのままiPhoneのCoreAudioに渡すモードということになります。実際に62KHzで出力されるのではなく、CoreAudio内部で44KHzに変換されて出力されているようです(iPhoneのイヤホン出力をUSB-DAC/ADCに繋いで解析しました)。44, 48KhzのときはGAMDX内部で62KHzから周波数変換されて返されます。なお、22KHzのときだけなぜかGAMDX内部でも22Khzで処理されています(謎)

(追記:上記消去した部分はGAMDXドライバというかMXDRVgを作られたGORRYさんから直接Twitterで指摘されました)

iPhoneは48KHzの出力にも対応しているはずですが、48KHzモードでも44KHzで出力されているようです?と思ってMDX Player for iOSのソースを見てみたらAVAudioSessionのsetPreferredSampleRateが設定されていませんでした。直しておきましょう。

さて本題です。

MDXファイルを再生していると高音域がやけに耳に響くものがあります。もともとFM音源の音はそんなものだという考え方もありますが、とくにADPCMの音が響くと思います。そこで再生周波数の変化でデータがどのように違うか見てみましょう。iPhoneのイヤホン出力をAudacityというソフトで取り込んで(96KHz)、スペクトル表示にしてみました。

62KHz(実際の出力時には44KHzに変換されています)

62KHz

44KHz

44KHz

22KHz

22KHz

特に44KHzは本来の音の周波数以外のところにノイズが乗って全体的に水色がかった感じになっていると思います。62KHzで水色が多いのはADPCMが鳴っているところのようですドラムが鳴っているところで、ノイズを出力しているからこうなるようです。22KHzは44KHzよりもややノイズが少ないといえるでしょうか。ということで、ver 2.0では62.5KHzで聴くのが一番いいと思います。

 

このようなノイズの原因はおそらくGAMDX内蔵の周波数変換ルーチンの性能が良くないからと思われます(62KHzのときはAppleの周波数変換ルーチンが使われるのでノイズが乗らないのでしょう)。また、ADPCMはどうしてもGAMDXの内部で周波数変換されるので、高音域ノイズが乗ってしまうようです。

ということで、しばらくGAMDXを改造してAppleの周波数変換ルーチンを使うようにして(フリーの周波数変換ライブラリはいくつかあるのですが、使うのが面倒なのと巨大です(そして著作権の問題も。GPLみたいですが))、音質向上を図ってみます。まずはADPCMからのアップコンバートでノイズが乗らないことを目標にしてみます。

MDX Player 2.0 XCode8 / Swift3へ移植

iOSのMDX Playerに私が追加したMMDSP風の画面をとりこむかたちで、Ver2.0がダウンロード可能となりました。僅かな貢献ですが楽しんでもらえれば幸いです。

MDX Player 2.0.0 – NagisaWorks Blog

ほかに私の追加した変更もGitHubで公開してあったのですが、Ver2.0は他にも大幅な変更(Swiftへの移植)などあり、うまく変更を取り込むことができない・・・ということで私のバージョンは一旦GitHubからは消して(手元にはありますよ)、あらたにForkしてしまいました。

そしてさてダウンロードしてみると・・XCode7用になっているようです。SSDをケチって128GBにした私のメインマシンではXCode8との両立もきついし、これからはSwift3.0が主流になっていくとも思いましたので変更してみました。

一番大変なのはCocoapodsで、SwiftyDropboxというライブラリを使っているのですが初めはインストールされるのがどうしてもSwift2対応のver3.2.0になってしまいました。

sudo gem install cocoapods –pre

としてプレリリース版にしてみたのと、Podfileの初めの行を

platform :ios, ‘9.0’

としてみたところ(他にも色々やったかもしれないが)、ver4.0がインストールされました。メインのソースは自動変更の上でエラー部分だけちょいと直してとりあえず対応終了。ちゃんと動くようです。

追記:他のマシンにGitHubから持ってきた場合、pod installしてできたファイルは不完全で、設定ファイルのpods>(AlamoFire,SwiftyDropbox)>Build setting のUse Legacy Swift Language Version という項目をNoにしないといけないようです。Podfileの変更でなんとかできそうですが。

これからバグフィックス・機能追加考えてみます。

mxdrvgを64ビット化

MDXPlayer for iOSをいじっていますが、mxdrvgの制約で64ビット化できないとのことです。

mxdrv.h, mxdrv_core.hをみてみると、ポインタはポインタとして宣言されていますし、大体なんとかなりそうですがこんなところがあります:

static void L000788(
  void
) {

// L000788:;
/*
  move.l  a0,(L001e28)
  move.w  (a0),(L001e22)
  movea.l $0002(a0),a1
  move.l  (a1),(L00221c)
  addq.w  #4,a1
  move.w  (a1),d0
  not.w   d0
  move.w  $0002(a1),d1
  not.w   d1
  move.b  d0,(L002230)
  move.b  d1,(L002231)
  move.l  a1,(L002218)
  clr.w   (L001e1c)
  bra     L0007c0
*/
	G.L001e28 = A0;
	G.L001e22 = GETBWORD( A0 );
	A1 = (UBYTE *)GETBLONG( A0+2 );
	G.L00221c = (UBYTE *)GETBLONG( A1 );

最後の2行は完全にアウトです。
またpcm8.cppには

int Pcm8::DmaArrayChainSetNextMtcMar() {
	if ( DmaBtc == 0 ) {
		return 1;
	}
	--DmaBtc;

	int mem0,mem1,mem2,mem3,mem4,mem5;
	mem0 = MemRead((unsigned char *)DmaBar++);
	mem1 = MemRead((unsigned char *)DmaBar++);
	mem2 = MemRead((unsigned char *)DmaBar++);
	mem3 = MemRead((unsigned char *)DmaBar++);
	mem4 = MemRead((unsigned char *)DmaBar++);
	mem5 = MemRead((unsigned char *)DmaBar++);
	if ((mem0|mem1|mem2|mem3|mem4|mem5) == -1) {
		return 1;
	} 
	DmaMar = (volatile unsigned char *)((mem0<<24)|(mem1<<16)|(mem2<<8)|(mem3));  // MAR

最後の1行もダメですね。それでも直せるところだけ直してみて、これらのまずいところにはブレークポイントを設定してその時に考えよう、としてみたところ・・・ブレークポイントには到達することなく曲が再生されます。(・・?

私はMDXファイルの知識が殆ど無いですが、上のケースはMXDRVG_Contコマンドで呼ばれるルーチンですがMDXPlayer for iOSではMXDRVG_Contコマンドは使っていないようです。なので大丈夫なのかもしれません。もう一箇所も同様です。

下のケースはADPCMへのDMAが連鎖するときのようですが・・・やはりそのような使い方は殆ど行われないということで良いのでしょうか。

とりあえずGitHubには変更を上げておきました。コンパイル時にワーニングが出るところは、上記のように本来大問題となるところです。

もう少しいろんな曲を聞いてみて不具合がないか確かめてみます。あと、明らかにおかしいところに来た時には再生を中止するようにしたほうが安全かもしれません。

MDXPlayer for iOS にMMDSP風の画面を追加した あとCocoapodsデビュー

ゴールデンウィーク中にMDXPlayer for iOSの存在に遅ればせながら気づいたあと、ソースがGitHubで公開されているのでいじってみたところが前回まででした。

その後細かいバグ取りや、もともと公開されているソースでXCodeやCocoapodsの新しいバージョンに対応できていないところがあるのでそちらの方も直したりしているうちに3週間ほどが経過。うちかなりの時間MMDSPのソースと格闘。プリントアウトしたアセンブラのソースを久しぶりに読み解きました。
Cocoapodsはちょうど5/16にversion 1.0となってその余波で変更が生じたりしましたが、とりあえず完成したといっていいかなと。

ソースは元がOpenになっているのでこちらもGitHubにアカウントを作って公開してみました

GitHub sinn246/MDXPlayer

Cocoapodsはあえてこれまでは使わずに来ていたのですが(外部ライブラリ的なものは静的にプロジェクトに入れていた)、便利そうです。version1.0になったのを機に次のプロジェクトからは使ってみようかな。

夢の続き:MDXファイルをiPhoneで再生してMMDSP風に?

昔X68000を使っていました。X68000には8音同時発声可能なOPMというFM音源チップが載っていて、1990年頃には音楽の面でも最先端の環境でした。MDXというファイル形式でいろんな音楽データが出回っていたものでした。

大学生時代は友人に貸し出していたX68000ですが、友人宅に遊びに行った時には徹夜で色々話したりゲームをしたりしながら、BGMとしてX68000でMDXファイルを延々と再生していました。再生に使うソフトではMMDSPというのがクールだった記憶があります。その後X68000を手放しました。Windows使っていた時にはMDX再生ソフトやX68エミュレータがありましたが、MacとiPhoneをメインに使うようになってからはMDXファイルを聴く機会もなくなっていました。

こないだ掃除していたら昔のSDカードが出てきて、Windowsで集めたMDXファイルが保存されていました。また聞いてみたくなったのですが、WindowsPCは家にはまともなのがなくて再生できませんでした。iPhoneでMDXが再生できるアプリがあればいいのだがと検索してみると、ちょうど3年前に公開されていました。なぜ気づかなかったのだろう。

MDX Player for iOS

X68000シリーズ由来の音楽データMDXをiPhoneで再生!MDX Player for iOSがリリース

私がiPadで愛用している「i文庫」を作った方が作られたようです。GitHubでソースが公開されているのでダウンロードしてみました。音楽再生は問題ないのですが、画面が寂しいのでMMDSPのソースを参考にしながら鍵盤などを再現してみました。スペクトラムアナライザ風のところは32×32のビットマップを作ってCALayerで拡大表示、その手前にスリット状のビットマップを表示して手抜きで表示しています。

再現度はいまいちかもしれませんが、大学生時代のことをおもいだして昔見た夢の続きを見ているような、不思議な感覚になります。わかるかな?

 

ビル・アトキンソンとQuickDraw リージョンについて思いを巡らせた高3の夏

iOSの開発をしているのでiPadでも買うべきところですが、Kindle4が安かったのでつい買ってしまいました。軽いし文字も読みやすいので、読書にはいい端末です。

さて、Jobsの伝記が発売になったので、早速Kindleで購入して読んでいます。iOSで読むのが本当はいいのでしょうけど。

Lisa/Mac開発のくだりになって、ビル・アトキンソンとQuickDrawとりわけリージョンの話がちょっと出てきました。簡単に言うと、コンピュータの画面上の一部の領域を、矩形に限らず円や任意の形で指定できるという機能です。ドロープログラムで、一部の領域をマウスで選択して動かしたりできますね?あるいは、上のウィンドウで隠された下にあるウインドウの、画面に出ている部分はただの四角ではなくL字型だったりしますね?そういう様々な形を扱うことができるのがリージョンです。

高校生の頃、OS-9/68000を使っていましたが、ウインドウシステムはどうも貧弱で使い勝手の悪いものでした。その頃(1988頃)にはすでにMacでマルチウインドウは高い水準で実現されていました。Macでどういう風になっているのか、MacのToolbox (OSに相当するような、GUIの基本ルーチン)の解説書を、連日地元のK書店に立ち読みに行きました(1万円以上する本で、何巻かに分かれていた記憶が)。

その時、上記の「リージョン」というのがあるのを知りました。今普通にそれを実現しようとすれば、マスクに相当する白黒のビットマップを作るところでしょうか。ただ、当時のマシンのメモリは非常に限られていました。仮に512x512のビットマップをモノクロで作ると、32KBytesになります。また、リージョンは仮想座標系の中で拡大できるので、32000x32000とかになりえます。1MBytes以下のメモリしか積んでいなかった当時のマシンでは、ビットマップで実装するのは無理でした。

しばらくパソコンを封印していましたが、どのようにすればリージョンが実装できるのか、自分なりに考えてみました・・・

不定形のようなリージョンでも、どこかのy座標について見てみると、つまり水平の赤線との交点で見てみると、赤線はリージョンの中に出たり入ったりします。交点の座標を記録しておけば(この場合4個),赤線の上の任意の点について、リージョンの内部にあるか外部にあるかは簡単に判定できます(x座標の値が1つ目の交点以下なら「外」、1つ目と2つ目の交点の座標の間なら「中」、2つ目と3つ目の交点の間なら「外」、・・・)。これだと、交点の座標だけ記憶すればいいので、ランレングス圧縮と似たような効果があって記憶容量はずいぶん少なくできます。

受験生だったので実装したりはしなかったし、これを始めると何ヶ月もかかってしまったでしょう・・・

表題のビル・アトキンソンの話ですが、ふと思い立って検索してみると、QuickDrawのソースコードは公開されているのでした。

Computer History Museum – MacPaint and QuickDraw source code

68000のアセンブリ言語を読むのは15年ぶりでしょうか。68Kのアセンブリ言語は、Cを読むのと殆ど変わりません。なんでこんなに良いCPUが廃れてしまったのか・・・

さて、Regionの実装は、ほぼ高校生の時に予想したとおりでした。アセンブラでこれほど綺麗なソースはすごいです。アップルが、その後PowerPCに移行してもここだけは68Kアセンブラのままだったそうな。

いろんなルーチンがある中でInsetRgnというルーチンがあり、あるリージョンのxピクセル分内側に縮小したリージョンを作るというものですが、これは実装方法がよくわかりませんでした。

ソースコードを読んでみると、まず横方向には、僕の書いた図で点1を右に、点2を左に、点3を右に、点4を左に・・・と動かすことで、領域を縮小するようです。これはわかります。縦方向はどうするのかというと、なんとX/Y座標をスワップして、更にもう一度横方向にInsetしているのでした・・・ビル・アトキンソンすごいな。