diff --git a/README.md b/README.md index eed8180..b59d125 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#build--distribution) [![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/) [![Downloads](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=downloads)](https://github.com/CaYatur/SoundVisualizer/releases) -[![Tests](https://img.shields.io/badge/tests-2184%20passing-2ea043.svg)](#tests) +[![Tests](https://img.shields.io/badge/tests-2187%20passing-2ea043.svg)](#tests) [![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com) @@ -253,8 +253,11 @@ that asserts the bar profile has no step in it. on); echo orientation is `(int)x % 4`; gamma below 1 is ignored while echo is on; and when two presets whose echoes point different ways blend, the echo fades out and back in instead of flipping. A preset that leaves out its decay or gamma gets MilkDrop's 0.98 and 2.0 instead of 0, - and so do most other values a preset leaves out; a rotation centre of 0 stays in the corner. - Rendered through the engine, every case matches MilkDrop's formula within 2/255. + and so do the other values it leaves out — down to MilkDrop's quirk of reading a missing wave + colour as 0; a rotation centre of 0 stays in the corner. The values MilkDrop keeps out of the + equations (wave scale and smoothing, the volume fade, warp speed and scale) come from the file as + they do there, so a wave scale of 0 flattens the wave (102 presets of the corpus). Rendered + through the engine, every case matches MilkDrop's formula within 2/255. - **Composite shaders get the hue colour MilkDrop gives them.** MilkDrop passes every composite shader four slowly drifting corner colours as `hue_shader`, whatever the preset's `fShader` says; `fShader` only scales them on the fixed pipeline. The engine applied `fShader` to shaders @@ -1305,7 +1308,7 @@ npm test npm start -- --smoke ``` -**2184 unit tests, all passing.** They are written to check answers, not to exercise lines: +**2187 unit tests, all passing.** They are written to check answers, not to exercise lines: - **Formulas** are checked against values derived by hand from their definitions — Viviani's curve staying on its sphere, the torus tube radius, Chladni's m↔n antisymmetry, every attractor diff --git a/README.tr.md b/README.tr.md index 9d41ef1..ce08d63 100644 --- a/README.tr.md +++ b/README.tr.md @@ -12,7 +12,7 @@ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#paketleme--dağıtım) [![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/) [![İndirme](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=indirme)](https://github.com/CaYatur/SoundVisualizer/releases) -[![Test](https://img.shields.io/badge/test-2184%20geçiyor-2ea043.svg)](#testler) +[![Test](https://img.shields.io/badge/test-2187%20geçiyor-2ea043.svg)](#testler) [![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com) @@ -246,8 +246,11 @@ profilinde basamak olmadığını doğruluyor. solarize'ı açıyor); yankı yönü `(int)x % 4`; yankı açıkken 1'in altındaki gama uygulanmıyor; yankıları farklı yöne bakan iki preset geçişte karışırken yankı bir anda dönmüyor, sönüp yeniden beliriyor. Sönmesini ya da gamasını yazmayan bir preset 0 yerine MilkDrop'un 0,98 ve - 2,0'ını alıyor, yazmadığı öteki değerlerin çoğu da öyle; dönme merkezi 0 ise köşede kalıyor. - Motordan geçirilerek çizildi: her durum MilkDrop'un formülüyle 2/255 içinde aynı. + 2,0'ını alıyor, yazmadığı öteki değerler de öyle — MilkDrop'un eksik dalga rengini 0 okuyan + tuhaflığına kadar; dönme merkezi 0 ise köşede kalıyor. MilkDrop'un denklemlere açmadığı değerler + (dalga ölçeği ve yumuşatması, sesle sönme, warp hızı ve ölçeği) oradaki gibi dosyadan geliyor, yani + 0 dalga ölçeği dalgayı düzleştiriyor (korpusta 102 preset). Motordan geçirilerek çizildi: her + durum MilkDrop'un formülüyle 2/255 içinde aynı. - **Birleştirme shader'ları MilkDrop'un verdiği ton rengini alıyor.** MilkDrop her birleştirme shader'ına `hue_shader` olarak dört köşede yavaşça gezinen renkler veriyor, presetin `fShader`ı ne derse desin; `fShader` onları yalnız sabit yolda ölçekliyor. Motor `fShader`ı shader'lara da @@ -1303,7 +1306,7 @@ npm test npm start -- --smoke ``` -**2184 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar: +**2187 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar: - **Formüller**, tanımlarından elle türetilmiş değerlerle sınanıyor — Viviani eğrisinin küre üzerinde kalması, simidin boru yarıçapı, Chladni'nin m↔n antisimetrisi, her çekicinin sınırlı diff --git a/ROADMAP.md b/ROADMAP.md index 65e7b6a..69dfafa 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -129,9 +129,9 @@ npm test npm start -- --smoke ``` -- **2184 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0; +- **2187 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0; 105 came with v3.1.1; 163 came with v3.1.2; 157 came with v3.1.3 — 1128 at - that tag — 469 more with v3.1.4, most of them from the MilkDrop work, and 587 + that tag — 469 more with v3.1.4, most of them from the MilkDrop work, and 590 on `main` since. Formulas are checked against values derived by hand from their definitions — Viviani's curve staying on its sphere, the @@ -2032,8 +2032,8 @@ rest after. No version number yet. the old ring nouns, the " · " in mash-up names or removing the module's script tag each fails a test. - **Fidelity follow-ups (#580)** · the fixed composite, the stage rule, - MilkDrop's defaults and the hue colour are done; the remaining defaults, - the other audits and the file-reading differences are next. + MilkDrop's defaults, the hue colour and the values that stay in the file + are done; the other audits and the file-reading differences are next. - **Checked against the source.** Nullsoft's own code (jecassis/foo_vis_milk2 5b44cea) and, for the fixed pipeline's blend passes, the D3D9 code the D3D11 fork was ported from (BeatDrop @@ -2146,15 +2146,34 @@ rest after. No version number yet. byte-identical; presets that read `hue_shader` differ in at most 0.1% of pixels by one level, the same four equal corner colours summed in another order. - - **Next: keys that fall back to other values.** MilkDrop reads a - missing `wave_r`, `wave_g`, `wave_b`, `wave_x` or `wave_y` as `rot`'s - value at that point, which is 0, where the engine has 1, 1, 1, 0.5 and - 0.5. Missing wave smoothing is 0.75, and the volume fade runs from 0.75 - to 0.95. Custom shapes default to red inside and green outside. MilkDrop - also blends its other file-only values linearly through a transition - (wave scale and smoothing, the fade range, warp scale), where the engine - reads them from per-frame values. No corpus preset leaves any of these - keys out, and neither do ours. + - **Keys that fall back to other values.** MilkDrop reads a missing + `wave_r`, `wave_g`, `wave_b`, `wave_x` or `wave_y` with `rot`'s value as + the default (state.cpp:1389-1393, the same in the D3D9 code), and `rot` + is still Default's 0 at that point — so a file without them draws a + black wave in the corner, where the engine had white and centred. + Custom shapes that leave out their colours are red inside and green + outside (state.cpp:619-626), where the engine had white and black. With + fidelity on the engine now does both. No corpus preset leaves any of + these keys out, and neither do ours. + - **Values that stay in the file.** Wave scale and smoothing, the volume + fade's switch and range, warp speed and warp scale are not per-frame + variables in MilkDrop; the engine read them from per-frame values, with + `|| 1` fallbacks. With fidelity on they now come from the file: missing + smoothing is 0.75 and the fade range 0.75 to 0.95, as in MilkDrop; a + wave scale of 0 flattens the wave (102 presets) and a warp speed of 0 + stops the warp pattern (4 presets), where `|| 1` turned both into 1. + Through a transition wave scale and smoothing, the fade range and warp + scale blend linearly between the two files, and the warp's speed and + scale are the new preset's for both meshes, since MilkDrop computes the + warp frequencies once a frame from the new state (milkdropfs.cpp: + 1591-1597); a custom wave takes the wave scale of its own preset, + unblended (2429). + - **Measured** against `main`, the last ten of 60 frames: 120 presets + that write none of these zeros are identical; of the 102 with wave + scale 0, 12 change and 7 by more than 1% (up to 23%), their waves now + flat; of the 4 with warp speed 0, one changes, slightly. With fidelity + off all of them are identical. The 900-preset sample sorts into the + same classes, preset for preset. - **Not done yet:** the fixed warp path, the blur chain, borders and centre darkening, and the rest of the blend snap points; the file-reading differences found while building the mash-ups (a @@ -2190,7 +2209,14 @@ rest after. No version number yet. `fShader`; the fixed amount's threshold, partial mix and no clamp; the draw weights (pass counts, the 1.0001 cut, the edge below −0.999, wraparound, per-corner order); the echo fade with no echo on the new - side; and `hueAt` not flipping y. 18 of 18 mutations are caught. + side; and `hueAt` not flipping y. 18 of 18 mutations are caught. The + values that stay in the file add three: missing wave keys at 0 and + shape colours red and green with fidelity on and the old base with it + off; a wave scale of 0 flattening the main wave with fidelity on and + counting as 1 with it off; and the volume fade's switch and range from + the file with MilkDrop's defaults, a negative switch counting as on. + Five test files follow the new reads, one of them with a flat custom + wave at scale 0. 16 of 16 mutations are caught. ## v3.1.6 — Comprehensive video export diff --git a/src/shared/milkdrop.js b/src/shared/milkdrop.js index 97b2555..f53f843 100644 --- a/src/shared/milkdrop.js +++ b/src/shared/milkdrop.js @@ -970,11 +970,20 @@ yazdığı için ad hep "var" görünüyordu. `mv_a` MilkDrop'ta da sonunda 0: `bMotionVectorsOn` yoksa 0'a - çevriliyor (state.cpp:1402), ancak sonra `mv_a` okunuyor. Uyum açıkken - bu tablo, kapalıyken eski taban geçerli. */ + çevriliyor (state.cpp:1402), ancak sonra `mv_a` okunuyor. + + `wave_r/g/b/x/y` yazılmamışsa 0, CState::Default'un 1 ve 0,5'i DEĞİL: + Import onları varsayılan olarak `rot`un o anki değeriyle okuyor + (`GetFastFloat("wave_r", m_fRot.eval(-1), f)`, state.cpp:1389-1393; + BeatDrop'un D3D9 hâli 1368-1372 aynı) ve `rot` henüz okunmadığı için + o değer Default'un 0'ı. Yani bu anahtarları yazmayan bir dosyanın + dalgası MilkDrop'ta siyah ve köşede. Korpusta hiçbir dosya onları + atlamıyor, yerleşiklerimiz ve üreticimiz de yazıyor (#580). + + Uyum açıkken bu tablo, kapalıyken eski taban geçerli. */ const MD2_PF_DEFAULTS = { zoom: 1, zoomexp: 1, rot: 0, warp: 1, cx: 0.5, cy: 0.5, dx: 0, dy: 0, sx: 1, sy: 1, - decay: 0.98, wave_a: 0.8, wave_r: 1, wave_g: 1, wave_b: 1, wave_x: 0.5, wave_y: 0.5, + decay: 0.98, wave_a: 0.8, wave_r: 0, wave_g: 0, wave_b: 0, wave_x: 0, wave_y: 0, wave_mystery: 0, wave_mode: 0, ob_size: 0.01, ob_r: 0, ob_g: 0, ob_b: 0, ob_a: 0, ib_size: 0.01, ib_r: 0.25, ib_g: 0.25, ib_b: 0.25, ib_a: 0, @@ -1283,6 +1292,12 @@ pool, initialised: false, }; + /* MilkDrop'un şekil renkleri, dosya yazmamışsa (state.cpp:619-626): + iç renk KIRMIZI (1,0,0), dış renk YEŞİL ve saydam (0,1,0,0). + Eski tabanımız beyaz ve siyahtı. Fark yalnız dosyanın atladığı + renklerde — korpustaki 15.982 açık şeklin hiçbiri atlamıyor. Uyum + açıkken bu taban geçerli (shapeFrame; #580). */ + shape.baseMd2 = Object.assign({}, shape.base, { g: g('g', 0), b: g('b', 0), g2: g('g2', 1) }); shape.cInit = compile(s.init || '', pool, { seed: o.seed, loopBudget: 65536 }); shape.cFrame = compile(s.per_frame || '', pool, { seed: o.seed, loopBudget: 65536 }); for (const c of [shape.cInit, shape.cFrame]) { @@ -1381,7 +1396,7 @@ if (!s || !s.enabled) return null; const P = s.pool; this._shareInto(P); - const b = s.base; + const b = this.accurate !== false && s.baseMd2 ? s.baseMd2 : s.base; for (const k in b) P.set(k, b[k]); P.set('instance', instance); P.set('num_inst', s.instances); diff --git a/src/visualizer/modes/milkdrop.js b/src/visualizer/modes/milkdrop.js index 03cc166..08457d7 100644 --- a/src/visualizer/modes/milkdrop.js +++ b/src/visualizer/modes/milkdrop.js @@ -2368,14 +2368,10 @@ void main(){ let orient = M.echoFlipBits(Pp.get('echo_orient')); const O = this.oldPreset; if (O && this.blendProg < 1) { - const fp = (P, k, d) => { - const v = P && P.file && P.file.params ? P.file.params[k] : undefined; - return typeof v === 'number' && isFinite(v) ? v : d; - }; const na = this._fileVal('fvideoechoalpha', 0); - const oa = fp(O, 'fvideoechoalpha', 0); - const no = Math.trunc(fp(Pp, 'nvideoechoorientation', 0)); - const oo = Math.trunc(fp(O, 'nvideoechoorientation', 0)); + const oa = this._fileOf(O, 'fvideoechoalpha', 0); + const no = Math.trunc(this._fileOf(Pp, 'nvideoechoorientation', 0)); + const oo = Math.trunc(this._fileOf(O, 'nvideoechoorientation', 0)); if (na > 0.01 && oa > 0.01 && no !== oo) { const ci = this._cosMix(); if (this.blendProg < this._snapPoint()) { @@ -2390,23 +2386,32 @@ void main(){ return { alpha, zoom, orient, gamma: Pp.get('gamma') }; } - /* DOSYADAN OKUNAN, KARE DEĞİŞKENİ OLMAYAN ayarlar — `fShader` gibi. - MilkDrop bunları denklemlere açmıyor: kodun aynı adla yazdığı değer - bir kullanıcı değişkeni olarak kalıyor. Geçişte eski ve yeni presetin - dosyadaki değeri arasında DOĞRUSAL, ham ilerlemeyle karışıyorlar - (`CBlendableFloat::eval`, state.cpp:1933-1953) — kosinüs eğrisi yok. - `key` dosyanın küçük harfli anahtarı; yazılmamışsa `dflt`, yani - MilkDrop'un varsayılanı. */ + /* DOSYADAN OKUNAN, KARE DEĞİŞKENİ OLMAYAN ayarlar — `fShader`, + `fWaveScale`, `fWaveSmoothing`, `fModWaveAlphaStart/End`, + `fWarpScale`, `fWarpAnimSpeed`, `bModWaveAlphaByVolume`. MilkDrop + bunları denklemlere açmıyor: kodun aynı adla yazdığı değer bir + kullanıcı değişkeni olarak kalıyor. `key` dosyanın küçük harfli + anahtarı; yazılmamışsa `dflt`, yani MilkDrop'un varsayılanı. 0 da + bir değer: burada `|| 1` yok. + + `_fileOf` tek presetinkini veriyor. `_fileVal` geçişte eski ve yeni + presetin değeri arasında DOĞRUSAL, ham ilerlemeyle karışanı — + MilkDrop'un karışabilen sayıları böyle (`CBlendableFloat::eval`, + state.cpp:1933-1953), kosinüs eğrisi yok. Karışmayanlar + (`fWarpAnimSpeed`, mantıksal ayarlar) geçişin başından yeni + presetin: MilkDrop onları anında değiştiriyor (state.cpp + StartBlendFrom). */ + _fileOf(P, key, dflt) { + const v = P && P.file && P.file.params ? P.file.params[key] : undefined; + return typeof v === 'number' && isFinite(v) ? v : dflt; + } + _fileVal(key, dflt) { - const get = (P) => { - const v = P && P.file && P.file.params ? P.file.params[key] : undefined; - return typeof v === 'number' && isFinite(v) ? v : dflt; - }; - const cur = get(this.preset); + const cur = this._fileOf(this.preset, key, dflt); const O = this.oldPreset; if (!O || !(this.blendProg < 1)) return cur; const p = Math.max(0, Math.min(1, this.blendProg)); - return get(O) * (1 - p) + cur * p; + return this._fileOf(O, key, dflt) * (1 - p) + cur * p; } /* MilkDrop'un dönme matrisleri: rot_s/d/f/vf/uf/rand 1..4. @@ -3346,8 +3351,14 @@ void main(){ /* Dalga örnekleri kare başına BİR KEZ, çizimlerden önce: özel dalgalar geçişte iki preset için iki kez çiziliyor. Hizalamanın kendisi karenin başında, bantlardan önce ilerletildi. */ - this._waveSamples(audio, this.preset.get('wave_scale'), - this.preset.get('wave_smoothing')); + /* Ölçek ve yumuşatma uyum açıkken DOSYADAN, geçişte doğrusal karışarak + (milkdropfs.cpp:909-912): MilkDrop onları denklemlere açmıyor ve + yazılmamışsa 1 ile 0,75 kullanıyor. Havuzdan okumak yumuşatmayı + yazmayan dosyada 0 veriyordu. */ + const accW = this._wantAcc !== false; + this._waveSamples(audio, + accW ? this._fileVal('fwavescale', 1) : this.preset.get('wave_scale'), + accW ? this._fileVal('fwavesmoothing', 0.75) : this.preset.get('wave_smoothing')); /* Dokulu şekiller ÖNCEKİ kareyi örnekliyor. Şu an yazdığımız hedefi okumak tanımsız davranış: aynı dokudan okurken aynı dokuya yazmak sürücüye göre değişen çöp verir. MilkDrop da şekli sampler_main @@ -3863,9 +3874,16 @@ void main(){ kural. Sifira bolme korunuyor: `fWarpScale = 0` yazan bir preset var - olabilir ve sonsuz bir frekans butun agi katlardi. */ - const wSpeed = acc ? (P.get('warpanimspeed') || 1) : 1; - const wScaleRaw = acc ? (P.get('warpscale') || 1) : 1; + olabilir ve sonsuz bir frekans butun agi katlardi. + + İKİ DEĞER DE DOSYADAN ve YENİ presetten, geçişte eski presetin ağı + için de (#580): MilkDrop frekansları karede bir kez, yeni durumdan + hesaplıyor ve iki ağa da uyguluyor (milkdropfs.cpp:1591-1597). Hız + karışmıyor (düz bir sayı), ölçek doğrusal karışıyor. 0 bir hız: + korpusta 4 preset `fWarpAnimSpeed=0` yazıyor ve deseni durduruyor; + `|| 1` onu yine oynatıyordu. */ + const wSpeed = acc ? this._fileOf(this.preset, 'fwarpanimspeed', 1) : 1; + const wScaleRaw = acc ? this._fileVal('fwarpscale', 1) : 1; const wScale = Math.abs(wScaleRaw) < 1e-4 ? 1e-4 : wScaleRaw; const warpTime = clock * wSpeed; const wsi = 1 / wScale; @@ -4643,8 +4661,12 @@ void main(){ ornekler ±128 biriminde. Motor bir ara 1 kullaniyordu — ozel dalgalarin hepsi olmasi gerekenin iki kati buyuklukteydi ve `wave_scale` onlara hic ulasmiyordu. Artik iki yol da MilkDrop'un - carpanini KENDI biriminde kullaniyor. */ - const ws = acc ? (WP.get('wave_scale') || 1) : 1; + carpanini KENDI biriminde kullaniyor. + + `wave_scale` dalganın KENDİ presetinin dosyasından, karışmadan + (`pState->m_fWaveScale.eval(-1)`, milkdropfs.cpp:2429); 0 dalgayı + düzleştiriyor. `|| 1` 102 presette 0'ı 1'e çeviriyordu (#580). */ + const ws = acc ? this._fileOf(WP, 'fwavescale', 1) : 1; const sc = acc ? (fq ? 0.15 : 0.004) * w.scaling * ws : w.scaling / 128; for (let i = 0; i < N; i++) { a[i] *= sc; b[i] *= sc; } } @@ -4708,13 +4730,18 @@ void main(){ return a; } + /* Ses yüksekliğiyle sönen dalga. Anahtar ve aralık DOSYADAN (#580): + MilkDrop üçünü de denklemlere açmıyor; aralık yazılmamışsa 0,75 ile + 0,95 ve geçişte doğrusal karışıyor, anahtar geçişin başından yeni + presetin (milkdropfs.cpp:2693-2694, state.cpp:570-572). Havuzdan + okumak eksik aralığı 0 yapıyordu. */ _waveVolAlpha(a) { let alpha = isFinite(a) ? a : 1; const P = this.preset; - if (this._wantAcc !== false && P && P.get('wave_modalpha') > 0) { + if (this._wantAcc !== false && P && this._fileOf(P, 'bmodwavealphabyvolume', 0) !== 0) { const vol = ((P.get('bass') || 0) + (P.get('mid') || 0) + (P.get('treb') || 0)) / 3; - const a0 = P.get('wave_modalpha_start') || 0; - const a1 = P.get('wave_modalpha_end') || 0; + const a0 = this._fileVal('fmodwavealphastart', 0.75); + const a1 = this._fileVal('fmodwavealphaend', 0.95); const d = a1 - a0; if (Math.abs(d) > 1e-6) alpha *= (vol - a0) / d; } @@ -4761,7 +4788,10 @@ void main(){ if (!this._fL) { this._fL = new Float32Array(576); this._fR = new Float32Array(576); } const L = this._fL, R = this._fR; const n = tb.length; - const s = isFinite(scale) && scale !== 0 ? scale : 1; + /* Uyum açıkken 0 da bir ölçek: dalga düzleşiyor, MilkDrop'ta olduğu + gibi (korpusta 102 preset `fWaveScale=0` yazıyor). Eski yol 0'ı + "yok" sayıp 1'e çeviriyordu. */ + const s = isFinite(scale) && (scale !== 0 || this._wantAcc !== false) ? scale : 1; let sm = this._wantAcc !== false && isFinite(smoothing) ? smoothing : 0; if (sm < 0) sm = 0; else if (sm > 1) sm = 1; // Uyum açıkken hizalanmış iki kanal, aynı süzgeçle diff --git a/tests/milkdrop-fidelity-comp.test.js b/tests/milkdrop-fidelity-comp.test.js index e6a2788..eaf921a 100644 --- a/tests/milkdrop-fidelity-comp.test.js +++ b/tests/milkdrop-fidelity-comp.test.js @@ -165,6 +165,32 @@ test('dosyanın yazdığı değer varsayılanın önünde; mv_a bMotionVectorsOn assert.strictEqual(p.get('wave_a'), 0.3, 'başlık adı fWaveAlpha kazanmalı'); }); +/* `wave_r/g/b/x/y` yazılmamışsa MilkDrop'ta 0: Import onları varsayılan + olarak `rot`un o anki değeriyle okuyor ve `rot` henüz Default'un 0'ı + (state.cpp:1389-1393). Şekillerin eksik renkleri iç kırmızı, dış yeşil + (state.cpp:619-626). Uyum kapalıyken eski beyaz, orta ve siyah. */ +test('eksik dalga rengi ve konumu 0; şeklin eksik renkleri kırmızı ve yeşil', () => { + const src = 'fDecay=0.9\nshapecode_0_enabled=1\nshapecode_0_r=1\n'; + const acc = new M.Preset(src, { seed: 1 }); + acc.frame({ time: 0, frame: 0 }); + for (const k of ['wave_r', 'wave_g', 'wave_b', 'wave_x', 'wave_y']) assert.strictEqual(acc.get(k), 0, k); + const s = acc.shapeFrame(acc.shapes[0], 0); + assert.deepStrictEqual([s.r, s.g, s.b, s.a, s.r2, s.g2, s.b2, s.a2], [1, 0, 0, 1, 0, 1, 0, 0]); + // Dosyanın yazdığı değer kazanıyor + const w = new M.Preset('wave_r=0.3\nwave_x=0.7\nshapecode_0_enabled=1\nshapecode_0_g=0.4\n', { seed: 1 }); + w.frame({ time: 0, frame: 0 }); + assert.strictEqual(w.get('wave_r'), 0.3); + assert.strictEqual(w.get('wave_x'), 0.7); + assert.strictEqual(w.shapeFrame(w.shapes[0], 0).g, 0.4); + // Uyum kapalıyken eski taban + const legacy = new M.Preset(src, { seed: 1, accurate: false }); + legacy.frame({ time: 0, frame: 0 }); + assert.strictEqual(legacy.get('wave_r'), 1); + assert.strictEqual(legacy.get('wave_x'), 0.5); + const ls = legacy.shapeFrame(legacy.shapes[0], 0); + assert.deepStrictEqual([ls.g, ls.b, ls.g2], [1, 1, 0]); +}); + test('init de MilkDrop\'un varsayılanlarını görüyor', () => { // MilkDrop init'ten önce yerleşik adları yüklüyor (LoadPerFrameEvallibVars) const p = new M.Preset('per_frame_init_1=q1 = decay + gamma;', { seed: 1 }); diff --git a/tests/milkdrop-hue-shader.test.js b/tests/milkdrop-hue-shader.test.js index 3cbcc1b..1bc7431 100644 --- a/tests/milkdrop-hue-shader.test.js +++ b/tests/milkdrop-hue-shader.test.js @@ -35,7 +35,10 @@ const body = (src) => src.slice(src.indexOf('{') + 1, src.lastIndexOf('}')); // Köşe rengi yöntemi kaynaktan const hueCorners = new Function('amt', 't', 'rand', body(method('_hueCorners(amt, t, rand)'))); -const fileVal = new Function('key', 'dflt', body(method('_fileVal(key, dflt)'))); +const fileOf = new Function('P', 'key', 'dflt', body(method('_fileOf(P, key, dflt)'))); +const fileValRaw = new Function('key', 'dflt', body(method('_fileVal(key, dflt)'))); +// `_fileVal` tek presetin değerini `_fileOf`tan alıyor +const fileVal = { call: (self, key, dflt) => fileValRaw.call(Object.assign({ _fileOf: fileOf }, self), key, dflt) }; /* Motorun kendi uniform yükleyicisini koşturur ve `hue_corner`a yazılan on iki sayıyı döndürür. Yalnız o uniform'un konumu verildiği için diff --git a/tests/milkdrop-warp.test.js b/tests/milkdrop-warp.test.js index f1f823a..07c942b 100644 --- a/tests/milkdrop-warp.test.js +++ b/tests/milkdrop-warp.test.js @@ -27,10 +27,15 @@ const CODE = fs.readFileSync( const BODY = CODE.replace(/\/\*[\s\S]*?\*\//g, ' ').replace(/(^|[^:])\/\/[^\n]*/g, '$1 '); const MESH = /_warpMeshPass\(P, clock, rep\) \{[\s\S]*?\n \}/.exec(BODY); +/* İkisi de DOSYADAN ve YENİ presetten, geçişte eski presetin ağı için de + (#580): MilkDrop frekansları karede bir kez yeni durumdan hesaplıyor + (milkdropfs.cpp:1591-1597). Hız karışmıyor, ölçek doğrusal karışıyor; + 0 bir değer. */ test('warp: iki preset ayarı okunuyor', () => { assert.ok(MESH, '_warpMeshPass bulunamadı'); - assert.match(MESH[0], /P\.get\('warpanimspeed'\)/); - assert.match(MESH[0], /P\.get\('warpscale'\)/); + assert.match(MESH[0], /this\._fileOf\(this\.preset, 'fwarpanimspeed', 1\)/); + assert.match(MESH[0], /this\._fileVal\('fwarpscale', 1\)/); + assert.doesNotMatch(MESH[0], /P\.get\('warp(animspeed|scale)'\)/, 'denklemlerin yazdığı değer MilkDrop\'a ulaşmıyor'); // Hız zamanı çarpıyor, ölçek tersiyle giriyor assert.match(MESH[0], /const warpTime = clock \* wSpeed;/); assert.match(MESH[0], /const wsi = 1 \/ wScale;/); @@ -100,8 +105,8 @@ test('warp: genlik çarpanı 0.0035 değişmedi', () => { test('warp: anahtar kapalıyken eski desen ve eski hız duruyor', () => { /* Eski davranış bir uyum değil ama kullanıcıların izlediği görüntü. Kapalıyken hız ve ölçek 1, terimler de eski sabitleriyle. */ - assert.match(MESH[0], /const wSpeed = acc \? \(P\.get\('warpanimspeed'\) \|\| 1\) : 1;/); - assert.match(MESH[0], /const wScaleRaw = acc \? \(P\.get\('warpscale'\) \|\| 1\) : 1;/); + assert.match(MESH[0], /const wSpeed = acc \? this\._fileOf\(this\.preset, 'fwarpanimspeed', 1\) : 1;/); + assert.match(MESH[0], /const wScaleRaw = acc \? this\._fileVal\('fwarpscale', 1\) : 1;/); /* Eski desen artık ayrı bir kod yolunda duruyor (dönüşümün sırası da farklı olduğu için tek bir `if` ile ayrılamıyordu). */ assert.match(MESH[0], /su \+= wr \* Math\.sin\(warpTime \* 0\.333 \+ cx0 \* 5 \+ cy0 \* 3\);/); diff --git a/tests/milkdrop-wave-align.test.js b/tests/milkdrop-wave-align.test.js index cdab3f5..06ee1ef 100644 --- a/tests/milkdrop-wave-align.test.js +++ b/tests/milkdrop-wave-align.test.js @@ -402,15 +402,24 @@ test('motor: özel dalga örnekleri iki yolda', () => { const fn = new Function('tb', 'N', 'w', 'audio', 'preset', inner); const tb = bytes(2048, (t) => smooth(t * 5)); const waves = new A.MilkdropWaves().update(tb, bytes(2048, (t) => smooth(t * 4 + 3))); - const preset = { get: (k) => (k === 'wave_scale' ? 1 : 0) }; + // wave_scale dalganın presetinin DOSYASINDAN (#580); yazılmamışsa 1 + const fo = method('_fileOf(P, key, dflt)'); + const fileOf = new Function('P', 'key', 'dflt', fo.slice(fo.indexOf('{') + 1, fo.lastIndexOf('}'))); + const preset = { get: () => 0, file: { params: {} } }; const wave = { sep: 0, smoothing: 0, scaling: 1, spectrum: 0 }; - const acc = { _wantAcc: true, _waves: waves, _specData: null, preset }; + const acc = { _wantAcc: true, _waves: waves, _specData: null, preset, _fileOf: fileOf }; fn.call(acc, tb, 512, wave, {}, preset); for (let i = 0; i < 512; i++) { assert.strictEqual(acc._cw1[i], Math.fround(waves.at(0, i - 16) * 0.004), 'a[' + i + ']'); assert.strictEqual(acc._cw2[i], Math.fround(waves.at(1, i - 16) * 0.004), 'b[' + i + ']'); } - const old = { _wantAcc: false, _waves: null, _specData: null, preset }; + // Dosyadaki 0 dalgayı düzleştiriyor; havuzun yazdığı 5 MilkDrop'a ulaşmıyor + const zero = { get: (k) => (k === 'wave_scale' ? 5 : 0), file: { params: { fwavescale: 0 } } }; + const acc0 = { _wantAcc: true, _waves: waves, _specData: null, preset: zero, _fileOf: fileOf }; + fn.call(acc0, tb, 512, wave, {}, zero); + // −0 da sıfır: strictEqual onu ayırıyor, `===` ayırmıyor + for (let i = 0; i < 512; i++) assert.ok(acc0._cw1[i] === 0, 'ölçek 0: a[' + i + '] = ' + acc0._cw1[i]); + const old = { _wantAcc: false, _waves: null, _specData: null, preset, _fileOf: fileOf }; fn.call(old, tb, 512, wave, {}, preset); for (let i = 0; i < 512; i++) { assert.strictEqual(old._cw1[i], Math.fround((tb[i] - 128) / 128), 'eski a[' + i + ']'); diff --git a/tests/milkdrop-wave-modes.test.js b/tests/milkdrop-wave-modes.test.js index 3e73600..ce053c9 100644 --- a/tests/milkdrop-wave-modes.test.js +++ b/tests/milkdrop-wave-modes.test.js @@ -47,16 +47,23 @@ function drawWave(opts) { const fn = new Function('gl', 'GW', 'GH', 'window', 'smoothWave', body(method('_drawWaveModes(gl, GW, GH)'))); const vol = new Function('a', body(method('_waveVolAlpha(a)'))); const modeAlpha = new Function('a', 'mode', 'GW', body(method('_waveModeAlpha(a, mode, GW)'))); + // Sesle sönmenin anahtarı ve aralığı dosyadan okunuyor (#580) + const fileOf = new Function('P', 'key', 'dflt', body(method('_fileOf(P, key, dflt)'))); + const fileVal = new Function('key', 'dflt', body(method('_fileVal(key, dflt)'))); const vals = Object.assign({ wave_mode: 0, wave_a: 1, wave_r: 1, wave_g: 1, wave_b: 1, wave_x: 0.5, wave_y: 0.5, wave_mystery: 0, wave_brighten: 0, wave_usedots: 0, wave_thick: 0, wave_additive: 0, - wave_modalpha: 0, wave_modalpha_start: 0, wave_modalpha_end: 1, bass: 1, mid: 1, treb: 1, + bass: 1, mid: 1, treb: 1, }, opts.vals || {}); - const preset = { get: (k) => vals[k] }; + const preset = { get: (k) => vals[k], file: { params: opts.file || {} } }; let got = null; const self = { _wantAcc: opts.acc !== false, preset, + oldPreset: null, + blendProg: 1, + _fileOf: fileOf, + _fileVal: fileVal, time: opts.time || 0, _fL: opts.L, _fR: opts.R, diff --git a/tests/milkdrop-wave-smoothing.test.js b/tests/milkdrop-wave-smoothing.test.js index 4f39320..403d2c4 100644 --- a/tests/milkdrop-wave-smoothing.test.js +++ b/tests/milkdrop-wave-smoothing.test.js @@ -30,9 +30,27 @@ const ALPHA = /_waveVolAlpha\(a\) \{[\s\S]*?\n \}/.exec(BODY); // ------------------------------------------------------------ kare dalgası +/* Uyum açıkken ölçek ve yumuşatma DOSYADAN, yazılmamışsa 1 ve 0,75, + geçişte doğrusal karışarak (milkdropfs.cpp:909-912; #580); kapalıyken + havuzdan. */ test('kare dalgası: yumuşatma okunuyor ve çizime bağlanıyor', () => { assert.ok(BASIC, '_waveSamples bulunamadı'); - assert.match(BODY, /this\._waveSamples\(audio, this\.preset\.get\('wave_scale'\),\s*this\.preset\.get\('wave_smoothing'\)\)/); + assert.match(BODY, /accW \? this\._fileVal\('fwavescale', 1\) : this\.preset\.get\('wave_scale'\)/); + assert.match(BODY, /accW \? this\._fileVal\('fwavesmoothing', 0\.75\) : this\.preset\.get\('wave_smoothing'\)/); +}); + +/* 0 da bir ölçek: MilkDrop'ta dalga düzleşiyor (korpusta 102 preset + `fWaveScale=0` yazıyor). Eski yol 0'ı "yok" sayıp 1'e çeviriyordu. */ +test('kare dalgası: uyum açıkken 0 ölçek dalgayı düzleştiriyor, kapalıyken 1 sayılıyor', () => { + const fn = new Function('audio', 'scale', 'smoothing', + BASIC[0].slice(BASIC[0].indexOf('{') + 1, BASIC[0].lastIndexOf('}'))); + const tb = new Uint8Array(1024).map((_, i) => 128 + Math.round(100 * Math.sin(i / 7))); + const acc = { _wantAcc: true, _waves: null }; + fn.call(acc, { timeBytes: tb }, 0, 0); + assert.ok(acc._fL.every((v) => v === 0), 'ölçek 0: düz'); + const old = { _wantAcc: false, _waves: null }; + fn.call(old, { timeBytes: tb }, 0, 0); + assert.ok(old._fL.some((v) => v !== 0), 'eski yol 0\'ı 1 sayıyor'); }); /* Ölçek karışıma giriyor (`s * (1 - sm)`). Girmezse yumuşatma arttıkça @@ -114,7 +132,8 @@ test('özel dalga: ölçek yumuşatmadan sonra uygulanıyor', () => { kullanıyordu: özel dalgaların hepsi olması gerekenin yaklaşık iki katı büyüklükte çiziliyor ve `wave_scale` onlara hiç ulaşmıyordu. */ test('özel dalga: MilkDrop genliği ve wave_scale', () => { - assert.match(CUSTOM[0], /WP\.get\('wave_scale'\)/); + // Dalganın kendi presetinin dosyasından, karışmadan (milkdropfs.cpp:2429; #580) + assert.match(CUSTOM[0], /const ws = acc \? this\._fileOf\(WP, 'fwavescale', 1\) : 1;/); const m = /const sc = acc \? \(fq \? ([\d.]+) : ([\d.]+)\) \* w\.scaling \* ws/ .exec(CUSTOM[0]); assert.ok(m, 'ölçek satırı bulunamadı'); @@ -173,11 +192,34 @@ test('özel dalga: çizim yumuşatılmış diziyi kullanıyor', () => { test('alfa: sese göre saydamlık uygulanıyor', () => { assert.ok(ALPHA, '_waveVolAlpha bulunamadı'); - assert.match(ALPHA[0], /P\.get\('wave_modalpha'\) > 0/); + assert.match(ALPHA[0], /this\._fileOf\(P, 'bmodwavealphabyvolume', 0\) !== 0/); assert.match(ALPHA[0], /alpha \*= \(vol - a0\) \/ d;/); assert.match(BODY, /alpha = this\._waveVolAlpha\(alpha\);/); }); +/* Anahtar ve aralık DOSYADAN (#580): MilkDrop üçünü de denklemlere açmıyor; + aralık yazılmamışsa 0,75 ile 0,95 (state.cpp:570-572), geçişte doğrusal. + Havuzdan okumak eksik aralığı 0 yapıyordu. */ +test('alfa: anahtar ve aralık dosyadan, MilkDrop\'un varsayılanlarıyla', () => { + const fn = new Function('a', ALPHA[0].slice(ALPHA[0].indexOf('{') + 1, ALPHA[0].lastIndexOf('}'))); + const grab = (sig) => { const m = new RegExp(sig.replace(/[()]/g, '\\$&') + ' \\{[\\s\\S]*?\\n \\}').exec(BODY); return m[0]; }; + const fo = grab('_fileOf(P, key, dflt)'); + const fv = grab('_fileVal(key, dflt)'); + const fileOf = new Function('P', 'key', 'dflt', fo.slice(fo.indexOf('{') + 1, fo.lastIndexOf('}'))); + const fileVal = new Function('key', 'dflt', fv.slice(fv.indexOf('{') + 1, fv.lastIndexOf('}'))); + // Ses 0,85: aralık 0,75..0,95'in tam ortası → yarı saydam + const mk = (params, pool) => ({ + _wantAcc: true, oldPreset: null, blendProg: 1, _fileOf: fileOf, _fileVal: fileVal, + preset: { get: (k) => (k === 'bass' || k === 'mid' || k === 'treb' ? 0.85 : (pool || {})[k]), file: { params } }, + }); + assert.ok(Math.abs(fn.call(mk({ bmodwavealphabyvolume: 1 }), 1) - 0.5) < 1e-9, 'varsayılan aralık'); + assert.ok(Math.abs(fn.call(mk({ bmodwavealphabyvolume: 1, fmodwavealphastart: 0.8, fmodwavealphaend: 0.9 }), 1) - 0.5) < 1e-9); + // Anahtar kapalı: yalnız kırpma; havuzun yazdığı anahtar ve aralık MilkDrop'a ulaşmıyor + assert.strictEqual(fn.call(mk({}, { wave_modalpha: 1, wave_modalpha_start: 0, wave_modalpha_end: 0.1 }), 1), 1); + // Eksi anahtar da açık: MilkDrop `!= 0` diye okuyor + assert.ok(fn.call(mk({ bmodwavealphabyvolume: -1 }), 1) < 1); +}); + /* Ses ölçüsü `vol` DEĞİL, `bass/mid/treb` ortalaması. Presetlerin %37,9'u `vol`ü kendi denklemlerinde başka bir şey için yeniden yazıyor; o değer buraya girseydi alfa preset yazarının hesabına göre değil rastgele @@ -193,5 +235,5 @@ test('alfa: ters ya da sıfır aralıkta sonsuza gitmiyor', () => { }); test('alfa: anahtar kapalıyken sadece kırpma yapılıyor', () => { - assert.match(ALPHA[0], /this\._wantAcc !== false && P && P\.get\('wave_modalpha'\) > 0/); + assert.match(ALPHA[0], /this\._wantAcc !== false && P && this\._fileOf\(P, 'bmodwavealphabyvolume', 0\) !== 0/); });