doubledepth

#xxx02:0

小節長の値として0を指定している作例を、私はひとつ思い出しました。#RANDOM BMS listで頁内検索すればhitします。小節長0をどう解釈するべきなのか私には判断できません。

  • Delight2, nBMplay, nazo, LR2, nanasi, Angolmois(C), Bemuse, PMSee-V, BMSC, GDAC2, BMSEおよびuBMplayは、小節長0を小節長1として解釈します。
  • BM98k, BM98de, ruvit, fgt++, beatoraja 0.4.1, Pulsus 0.5.1, raindrop 0.500, iBMSC, IIDXv, BMSV, BMEV, WAviewおよびBMxImageは、小節長0をそのまま解釈します。対象小節の内容を反映する機種もあれば、内容無しとみなす機種もあります。
  • BMX2WAVで「Errorを無視して変換を続行」すると、小節長0を小節長1として解釈します。
  • BGAEncAdvanceおよびTechnicalGrooveは、object.Error: BigInt division by zeroで終了。

かくいう私の小節長test0による除算を実行していました。値が数値として妥当か否かはcheckしていましたが、値0を食わされる可能性を完全に失念しており、結果としてNaNが出力されます。

先日の日記の補足

Comment氏の実装は最小公倍数を求めないそうで、自分の不明を恥じました(氏の厚意で詳細を一時的な記事にまとめていただきましたLinkしても問題なさそうなら後日参照予定ですRhythm座標の分数表現を前提として、全notesのrhythm座標を通分した分母を、私は「分解能」と呼んでいました。後述するような目的がないなら、分母の最小公倍数を求める必要はなさそうで、そこは私にとって盲点でした。

最小公倍数を求める目的のひとつとして、BMS関連apps間でnotesの情報を共有できるようになる点が挙げられます。たとえばBMSEのclipboard情報において、notesのrhythm座標は「通分分母を192としたときの分子」として表現されます。iBMSC Clipboard Data xNT共有例があります。一部の図表著者たちは、plaintextを加工することによって、これらのclipboard dataを賢く悪用しています。

他方では、「分解能」はBMS以外の形式に変換する目的にも役立ちます。
#TITLE lcm sample
#BPM 140
#WAV11 kick.wav
#00111:11111111
#00211:1111111100000000

各notesのrhythmの大域座標は[8/8, 10/8, 12/8, 14/8, 16/8, 17/8, 18/8, 19/8]と表現できます。通分済み分母8は「4/4拍子小節単位の分解能」です。これを最小のBMSON形式に変換すると、

{
  "version": "1.0.0",
  "info": {
    "init_bpm": 140,
    "resolution": 2,
    "title": "lcm sample"
  },
  "lines": [
    {"y":  8},
    {"y": 16},
    {"y": 24}
  ],
  "sound_channels": [
    {
      "name": "kick.wav",
      "notes": [
        {"x": 1, "y":  8},
        {"x": 1, "y": 10},
        {"x": 1, "y": 12},
        {"x": 1, "y": 14},
        {"x": 1, "y": 16},
        {"x": 1, "y": 17},
        {"x": 1, "y": 18},
        {"x": 1, "y": 19}
      ]
    }
  ]
}

通分済みのrhythm座標分子をそのまま流用できます。BMSONの"resolution"は「拍単位(4分音符単位)の分解能」です。

BMS書式が優秀であることに異論はありませんが、小節長やencodingなどにの幅が生じうる難しさがあります。「標準化されたJSON parserに任せれば一発で構文解析が終わるBMSON形式」は、非開発者であり車輪の再発明を愉しむ余裕がない私にとってはありがたい存在です。

いただいたcommentsについて考える

ありがとうございました。返信不要とのことだったので引用は控えますが、とても有益な内容のように思えますし、機会があれば一記事としてどこかで読めると嬉しいかもしれません。以下は返信ではなく、投稿いただいた内容を私が理解するために書きながら考えているものとお受け取りください。

私の理解が間違っていないなら、私の方法において許容誤差を0に設定すれば、構文解析結果は氏の実装のそれと同じくなると思われます。たとえば先日のMINECRAFT INSANE図表の例でいうと、

#50802:
10.0885416666667
小節長(俺):
1937/192
小節長(氏):
100885416666667/10000000000000
#508xx:
Divide into [1,10,121,194,968,1831,1834,1921,1930,1936,1937,3072]
座標分母(俺):
lcm(192,1920,…,371712) = 112225273924207825920
座標分母(氏):
lcm(10000000000000,…) = 2173803555911905588070400000000000000

この小節内におけるnotesの位置を分数で表現する場合、座標分母は前述の値になります。通分用の最小公倍数lcm()が「安全でない精度」やoverflowに達しうるので、私は常にJavaScript用任意精度演算librariesのひとつを通して座標を算出します。BMSON checkerも前述のlibraryを用いています。

脳筋演算を尊ぶ方針や、BMSが自然に分数を導出する優れた記法である点については、おそらく私も氏も見解が一致しています。「作者の意図」を小数値から察することが原理的に不可能であることについては、まあそうなんだろうなとは思いつつ、(BMSが音楽を記述するための記法であること、そしてBMSが小節を#999までしか表現できないことを踏まえると、)そうかなあとも思います。

「許容誤差は甘え」と指摘されればそこは頷くしかありませんが、現実的な問題として、BMSON仕様において"y""resolution"の上限値は4294967295なので、三連符系列の小節長を(誤差を許さずに)分数化すると、まともに変換できないという事情もありまして。

もう少し続けたくありますが、数日前から猛烈に体調が悪いので、今日はこの辺で失礼します。考え方が近い箇所も異なる箇所もあり、とても刺激的なcommentsでした。ありがとうございました。

MINECRAFTADVENTUREMEDLEY =C-H-I-L-L-M-I-X=

私の計算が間違っていないなら(間違っていそうですが)、追加された図表の音価分解能は:

SPA:
22210974396337942114099200
DPA:
29496904255958646153482035200
PMA:
551306634255933608669628211200

[追記] 私は計算を間違えたうえ、INSANE図表とSPA図表を取り違えました。分解能は正しくは:

SPA:
2909637645920270416946995200
DPA:
3864094457530582646106146611200
PMA:
72221169087527302735721295667200
INS:
2218785055204183628988997153382400

たった一小節、#508だけで音価分解能がNumber.MAX_SAFE_INTEGERを上回ります。

BMSON的な座標への変換に徹底的に抗うこのような図表をこそ私は変換したいので頑張ります。

私は利き手をもはやまともに動かせないので、おもに低難度図表を楽しませていただきました。高難度図表は打鍵感がよくわからない箇所が多かったけれど、notesの並べ方そのものは楽しかったです。

構造体 is 何

float型の演算では円周率の連分数展開が4回で収束することを、C言語で確かめました。

C言語を書けないなりに、“Do Repeat Yourself”で検算しました(誇らしげ)

#include <stdio.h>
int main(void) {
    float x = 3.14159265358979323846264338327950288419716939937510f;

    float i = (float) 0;
    float l1 = (int) x - i;  //   3.000000
    float r1 = x - l1;       //   0.141593
    float x1 = 1 / r1;       //   7.062509

    float l2 = (int) x1 - i; //   7.000000
    float r2 = x1 - l2;      //   0.062509
    float x2 = 1 / r2;       //  15.997681

    float l3 = (int) x2 - i; //  15.000000
    float r3 = x2 - l3;      //   0.997681
    float x3 = 1 / r3;       //   1.002325

    float l4 = (int) x3 - i; //   1.000000
    float r4 = x3 - l4;      //   0.002325
    float x4 = 1 / r4;       // 430.162964

    float l5 = (int) x4 - i; // 430.000000

    i += 1;
    float a0 = l1;
    float a1 = l1 + (i / l2);
    float a2 = l1 + (i / (l2 + (i / l3)));
    float a3 = l1 + (i / (l2 + (i / (l3 + (i / l4)))));
    float a4 = l1 + (i / (l2 + (i / (l3 + (i / (l4 + (i / l5)))))));

    printf("%s", (a3 == x) ? "a3 == x\n" : "a3 != x\n"); // a3 != x
    printf("%s", (a4 == x) ? "a4 == x\n" : "a4 != x\n"); // a4 == x

    return 0;
}
varapprox(float)correctly
a03 / 13.0000003
a122 / 7 3.1428573.142857074737549
a2333 / 1063.1415103.141509532928467
a3355 / 1133.1415933.1415929794311523
a4152983 / 48696 3.1415933.1415927410125732
第4次正則連分数第4次主近似分数
double[3; 7, 15, 1, 292]103993 / 33102
float[3; 7, 15, 1, 430]152983 / 48696

float型の152983÷48696は、float型の円周率と等価でした。そんなー


つまり実装や言語次第でどうとでもなってしまうと

これはもしや「bmsplayer data」や「bms benchmark」の領域!!

信じられる小節長は1/(2^n)の倍数値……!! 大正義っ……BMSEッ……!! 通常は楽譜としての妥当性を必要以上に追い求める理由がないので、一連の話題は氏が仰る通り、ごく一部のedge casesをどの程度まともに取り扱うのか、という話に留まりそうです。BMSC時代は変拍子図表がedge cases扱いだったはずだし、それと同じ掻痒を私は分解能3千億などに感じていますが、う〜ん。

先日の日記の訂正

BMIIDXView2015の説明書をよく見たところ、HDX/IIDXvの小数値はすべてfloat型のようでした。double型ではないようです。思い込みから誤りを書いてしまい、申し訳ございません。

  • 数値型のパラメータ(整数値、小数値)の場合は値の範囲が指定可能

    値の範囲は%からd、またはfの間に記載します。

    値はマイナス値も指定可能で、範囲の他に指定の値以上、以下といった定義も可能です。

    ※整数値はint型、小数値はfloat型の範囲で指定してください

    例)
    %-100~100d-100+100までの整数値であればOK
    %100~d+100以上の整数値ならOK
    %~-100d-100以下の整数値ならOK
    %0.000001~f0より大きい小数値ならOK(以上でしか定義出来ないため最小値で疑似的に表現)

先日の引用におけるddd.ddは、decimalを指しているものと思われます。

有理数近似によってBMSの小節長を解釈する試み(2)

匿名氏から誤差について再び返信不要で教えていただきました(ありがとうございました)。手元では従来版を変更済みでしたが、修正版をuploadする予定はないので、変更点だけ挙げてみます。

従来版は指定値と近似値の相対誤差を見て、0.0001 %以内の距離に収まった最初の結果を採用していました。ふわっとした条件だったので案の定、いくつかの状況で困りました。

  • 100000000.015625を指定すると、従来版は近似値として100000000を採用します。私は小数部と整数部を分離しました。修正版は近似値として6400000001/64を採用します。
  • 以下の状況(ii)の相対誤差0.00010000009999742445 %は、許容誤差0.0001 % Toleranceの範囲外ですが、近すぎます。

    1. 入力値0.33333のとき、仮定値1/3との絶対誤差は0.00000333… 採用値: 33333/100000
    2. 入力値0.333333のとき、仮定値1/3との絶対誤差は0.000000333… 採用値: 333332/999997
    3. 入力値0.3333333のとき、仮定値1/3との絶対誤差は0.0000000333… 採用値: 1/3

    (ii)について、仮定値1/3を許容範囲外として却下すると、BMSの小節長としてありそうにない仮定値333332/999997が採用されます。私は許容誤差を0.00010001 %に広げてad hocに対応しました。循環小数の大雑把すぎる指定が根本的な原因なので、0.0104166などに関しては諦めることにしました。0.01041666まで書いてくれれば普通にsafeなので……あーでももうすこし頑張るかあー

  • 「JavaScriptにはfloat型がない」と私は書きましたが、誤りでした(匿名氏、すみません)。せっかくなので、手元の修正版では単精度浮動小数点数演算もsimulateして、多倍長・倍精度・単精度の各演算結果を比較できるようにしました。float型は円周率の連分数展開が僅か4回で収束して私を驚愕させました。この結果を訝しんだ私は、JavaScript版をC言語で書き直して検算を試みます。

    私がfloat型を試す理由は、HDX/IIDXvの拡張構文#nnn02:ddd.ddfを思い出したからです。

    • コマンド以外のオブジェ定義は内部で処理されるため、PROTOTYPEタグには宣言不要

      現在対応しているオブジェ定義は以下のとおりです。
      #nnn02:ddd.dd(小節倍率)
      #nnn03:xxxxxxxx(テンポチェンジ)
      #nnnzz:zzzzzzzz(02、03ch以外のオブジェ定義)

      nnnは小節番号(000999)

      ddd.ddは小数値(C言語のように123.45fと最後にfが付いていてもOK)

      xxは16進数の羅列(00FF)

      zzは36進数の羅列(00ZZ)

    ここでddd.dddouble型を指しているものと思われます。接尾辞fの効果は不明です。C言語のように値がfloat型に変換される可能性も仮定して、BMSの小節長解析器を書き直す予定でした。円周率の連分数展開をC言語で検算してからの話ですが……C言語が書けないすぎる……[訂正記事]

    JavaScriptでfloat型を模する選択肢として、asm.jsなる低級言語もどきも見つかりました。しかしこれは、英数字とbitwise operatorsに拒絶反応を示す私にとってまさに天敵であり、またInternet Explorerではpolyfillしようがないので却下しました。C/C++を知る人にとっては、素のJavaScriptよりもむしろ馴染みやすいものなのかもしれません。

LZH書庫の解凍・圧縮DLLUNLHA32.DLL」が7年ぶりの更新、DLL読み込みの脆弱性を修正

匿名氏から返信不要でnewsを教えていただきました(ありがとうございました)。

LR2はdropされた書庫を自動的に展開することができます。この機能を活用するusersの環境には、LR2files\caldix\caldix.exeによって、古いUNLHA32.DLLがinstallされているかもしれません。endless music for LR(HQOGG)\tool\cldx\Unlha32などからも関連文書を参照できます。

32-bit版Explzhなども、古いUNLHA32.DLLを使っているかもしれません。64-bit版Explzhは、私家版TAR64.DLL/7-ZIP64.DLLを自動更新してくれません(どちらも2017年に更新されているのですが)。

Computersに疎い私のようなusersは、これら「環境のためのDLL群」を普段は触らないので、これらを思い出し見つけ出すために大変に苦労するでしょう。しかし最近のnewsは、脆弱性を塞がないと危険な目に遭うということを否応なく認めさせます。弱さは罪なのでしょうか。WannaCry.

有理数近似によってBMSの小節長を解釈する試み(1)

以下の文書を参考に、BMSの小節長解析器をJavaScriptで実装しようとしていました。主近似分数を総当たりで探索していた頃よりは賢くなりました(私が)。

試行錯誤中に見つけた『ε3(仮) 正則連分数展開』が解答そのものでした。あとは許容誤差Toleranceを適切に設定すれば私の期待通りに働くはずなのですが、0.3333330.0104166などの微妙な値をうまく判定できません。対象小節のBPMやrhythm等の情報も、併せて考慮する必要があるかもしれません。

1÷3 vs. 33 %

#TITLE 1/3
#BPM 120
#wav01 kick.wav
#00102:0.3333333333333333
#00111:010101
#00202:0.3333333333333333
#00211:010101
#00302:0.3333333333333333
#00311:010101
#00411:010101010101010101
#TITLE 33 %
#BPM 120
#wav01 kick.wav
#00102:0.33
#00111:010101
#00202:0.33
#00211:010101
#00302:0.33
#00311:010101
#00411:010101010101010101

前述のBMS二例の差異が顕著に現れるBMX2WAVのlogによれば、前者は「分解能: 18」、後者は「分解能: 900」です。解釈の違いは、たとえばBMSONへの変換時などに影響を及ぼすでしょう。

人間可読な値1/3として小節長を解釈した場合の、最小のBMSON。
{
  "version": "1.0.0",
  "info": {
    "title": "1/3",
    "init_bpm": 120,
    "resolution": 9
  },
  "lines": [
    {"y":  36},
    {"y":  48},
    {"y":  60},
    {"y":  72},
    {"y": 108}
  ],
  "sound_channels": [
    {
      "name": "kick.wav",
      "notes": [
        {"x": 1, "y":  36},
        {"x": 1, "y":  40},
        {"x": 1, "y":  44},
        {"x": 1, "y":  48},
        {"x": 1, "y":  52},
        {"x": 1, "y":  56},
        {"x": 1, "y":  60},
        {"x": 1, "y":  64},
        {"x": 1, "y":  68},
        {"x": 1, "y":  72},
        {"x": 1, "y":  76},
        {"x": 1, "y":  80},
        {"x": 1, "y":  84},
        {"x": 1, "y":  88},
        {"x": 1, "y":  92},
        {"x": 1, "y":  96},
        {"x": 1, "y": 100},
        {"x": 1, "y": 104}
      ]
    }
  ]
}
前述のBMSONを、BmsONE以外のBMSON実装にも解釈できるように修正した版。
{
  "version": "1.0.0",
  "info": {
    "title": "1/3",
    "init_bpm": 120,
    "resolution": 360
  },
  "lines": [
    {"y":    0},
    {"y": 1440},
    {"y": 1920},
    {"y": 2400},
    {"y": 2880},
    {"y": 4320}
  ],
  "sound_channels": [
    {
      "name": "kick.wav",
      "notes": [
        {"c": false, "l": 0, "x": 1, "y": 1440},
        {"c": false, "l": 0, "x": 1, "y": 1600},
        {"c": false, "l": 0, "x": 1, "y": 1760},
        {"c": false, "l": 0, "x": 1, "y": 1920},
        {"c": false, "l": 0, "x": 1, "y": 2080},
        {"c": false, "l": 0, "x": 1, "y": 2240},
        {"c": false, "l": 0, "x": 1, "y": 2400},
        {"c": false, "l": 0, "x": 1, "y": 2560},
        {"c": false, "l": 0, "x": 1, "y": 2720},
        {"c": false, "l": 0, "x": 1, "y": 2880},
        {"c": false, "l": 0, "x": 1, "y": 3040},
        {"c": false, "l": 0, "x": 1, "y": 3200},
        {"c": false, "l": 0, "x": 1, "y": 3360},
        {"c": false, "l": 0, "x": 1, "y": 3520},
        {"c": false, "l": 0, "x": 1, "y": 3680},
        {"c": false, "l": 0, "x": 1, "y": 3840},
        {"c": false, "l": 0, "x": 1, "y": 4000},
        {"c": false, "l": 0, "x": 1, "y": 4160}
      ]
    }
  ]
}

「『4/4拍子小節の三等分の長さ』を三等分するrhythm」を、精密に等間隔となる座標へ換算した例が、前述のBMSON codesです。一方、百分率として解釈した結果は以下のようになるでしょう。

小節長0.33を字義通りに解釈した場合の、最小のBMSON。
{
  "version": "1.0.0",
  "info": {
    "title": "33 %",
    "init_bpm": 120,
    "resolution": 225
  },
  "lines": [
    {"y":  900},
    {"y": 1197},
    {"y": 1494},
    {"y": 1791},
    {"y": 2691}
  ],
  "sound_channels": [
    {
      "name": "kick.wav",
      "notes": [
        {"x": 1, "y":  900},
        {"x": 1, "y":  999},
        {"x": 1, "y": 1098},
        {"x": 1, "y": 1197},
        {"x": 1, "y": 1296},
        {"x": 1, "y": 1395},
        {"x": 1, "y": 1494},
        {"x": 1, "y": 1593},
        {"x": 1, "y": 1692},
        {"x": 1, "y": 1791},
        {"x": 1, "y": 1891},
        {"x": 1, "y": 1991},
        {"x": 1, "y": 2091},
        {"x": 1, "y": 2191},
        {"x": 1, "y": 2291},
        {"x": 1, "y": 2391},
        {"x": 1, "y": 2491},
        {"x": 1, "y": 2591}
      ]
    }
  ]
}

元々のBMSの小節長合計が2.99(暗黙の小節長として、#00002:1および#00402:1が加算される)なので、変換後の座標にも「4/4拍子小節における1小節の長さ」の1 %に相当する不足が反映されます。#BPM 120における小節長0.01は、時間に換算すると0.02秒に相当します。つまり、計算上は1÷3解釈よりも0.02秒ほど再生時間が短くなります。

小節長の値として0.33が与えられたとき、これを1÷3と解釈するべきなのか、はたまた33 %とみなすべきなのか、構文解析器だけでは判断しきれないように思います。これを解決する方法を考えていますが、私の頭では変換器に挙動制御用optionsを用意する程度の発想しか出せていません。

0.1+0.20.3

浮動小数点数における小数計算の闇について、匿名氏が非公開comment経由で詳しく教えてくださいました(ありがとうございました)。以下はそれについて自分なりに噛み砕いた雑記です。

単精度浮動小数点数0.0104166666666667の、computer内部における表現:
  • 0.0104166666666667が、IEEE 754 binary32形式に変換される。C言語の場合 3C2AAAABH
  • 3C2AAAABHを二進数で表すと 0011 1100 0010 1010 1010 1010 1010 10112
  • 前述の「二進数で扱えるように丸められた」を再び10進数で表すと 0.010416666977107525

以下のC言語codeを実行すると、trueが表示されます(すくなくともEasyIDEC(ZIP版)では)。

#include <stdio.h>
int main(void) {
    float i = 0.0104166666666667;
    float j = 0.010416666977107525;
    if (i == j) {
        printf("true\n");
    } else {
        printf("false\n");
    }
    return 0;
}

float型が存在しないJavaScriptなどの環境では異なる結果になりますが、([追記] 最近のJavaScriptではMath.fround()などを利用可能です)言語が何であれ、computer上の小数値は浮動小数点数としてevaluateされた時点で丸められます。ここまでが前置きです。

ここまでは私にも理解できましたが、BMSの小節長が「実際の値」を考慮する必要性についてはいまひとつ理解できませんでした。食い違いの原因は、「『図表内の最小の音価』を座標単位とする、中間的なmodel(ちょうどBMSONのような)」を私が勝手に仮定していたからでした。

BMSのdata channel文は、おおむね「4/4拍子1小節のx倍の長さの小節を、音符や休符によってn等分する」という書式です。小節長0.5を指定する図表著者は、浮動小数点数の内部表現として割り切れる値だから0.5を指定しているわけではなく4/4拍子小節を1/2の長さにするために1÷2の値を指定しているはずです。「図表を音楽的に解釈するうえで問われるのは、実際の長さではなく長さの抽象構文解析段階では0.1+0.2=0.3であるべきだ」と私は考えました。(註:私が考える方法では)

しかしながら、根本的に異なる方法もいくつもあるに違いないことに気づかされました。たとえば小節単位で時間軸上にnotesをmappingしていくような方法なら、「図表全体の音価分解能」を算出する必要はありません。音声と映像のthreadを分離するなら、両者の分解能を合わせる必要もありません。方法次第では、BMSの小節長が「小数計算の闇」と向き合う状況もあるのだろうと思います。

日記

BMS関連

拙作BMS
bubble / hitkey
二次配布BMS
ノイズの海と鯨 / moka
PARTY TIME IN MY DREAM / HAIJI
BMSE非公式ヘルプ
Lite
Lite-online
Full
Full-online
buglist
iBMSC
Web (Japanese version)
issues
BMS差分
a­nal­gam
boléro
Ketch­up
quovadis
SELF
yellows
Do not use non-ascii filenames
雑多なメモ
bmsplayer data
bms benchmark
Secrets - Feeling Pomu 2nd
grid2sec
bmx2xxx
BMx Outliner
BMS command memo
BMS command memo (Japanese version)
BMS EVENT LITE
#RANDOM BMS list
BMS #OPTION command
BMS Bitmap test
Extended BPM
STOP Sequence
BMS Edge Cases
BMS extensions proposed by Sonorous (unofficial Japanese version)
BMS 2.0 (unofficial Japanese version)
BMS Editors
Do not use non-ascii filenames
BM98 Kikuchan Version 3.30 Revision #4.2
BMSON Checker

その他

HTML関連メモ
Dakuten on HTML
nest1000
EVS
Nervous Cascading
Source Han Sans test
User-Agent String
CSS Logical Properties