Skip to content

Commit b6cc351

Browse files
authored
Merge pull request #4 from elPytel:dev
Rainbow tables
2 parents e1bcb62 + fa8c8d0 commit b6cc351

22 files changed

Lines changed: 13870 additions & 307 deletions

DOC/MIDI.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
2+
# 1) Přehrávací hlava + auto-scroll
3+
4+
Uvidíš běžící svislou linku a editor se bude jemně posouvat.
5+
6+
**CSS** (přidej do `<style>`):
7+
8+
```css
9+
.playhead {
10+
position:absolute; top:0; bottom:0; width:2px;
11+
background: var(--accent, #3b82f6); opacity:.85; pointer-events:none;
12+
}
13+
```
14+
15+
**HTML** (do `#gridArea` přidej hned po vytvoření kontejneru – stačí jednorázově):
16+
17+
```js
18+
const playhead = document.createElement('div');
19+
playhead.id = 'playhead';
20+
playhead.className = 'playhead';
21+
gridElArea.appendChild(playhead);
22+
```
23+
24+
**JS** (nahoru k proměnným):
25+
26+
```js
27+
let playheadRAF = null;
28+
```
29+
30+
**JS** (po `Tone.Transport.start()` ve `rebuildAndPlay()`):
31+
32+
```js
33+
const startTime = Tone.now();
34+
cancelAnimationFrame(playheadRAF);
35+
const bpmNow = Tone.Transport.bpm.value;
36+
const secPerQ = 60 / bpmNow;
37+
38+
const animate = () => {
39+
const t = Tone.now() - startTime; // sekundy od startu
40+
const q = t / secPerQ; // čtvrti
41+
const x = Math.round(q * PX_PER_Q);
42+
playhead.style.left = x + 'px';
43+
// auto-scroll, drž playhead s malou rezervou
44+
const viewL = roll.scrollLeft, viewR = viewL + roll.clientWidth - 120;
45+
if (x > viewR) roll.scrollLeft = x - (roll.clientWidth - 120);
46+
playheadRAF = requestAnimationFrame(animate);
47+
};
48+
playheadRAF = requestAnimationFrame(animate);
49+
50+
// při stopnutí:
51+
Tone.Transport.scheduleOnce(()=>{
52+
cancelAnimationFrame(playheadRAF);
53+
playhead.style.left = '0px';
54+
status('Stop (konec skladby)');
55+
}, endSec);
56+
```
57+
58+
A když klikneš na **Stop**:
59+
60+
```js
61+
btnStop.addEventListener('click', ()=>{
62+
Tone.Transport.stop();
63+
cancelAnimationFrame(playheadRAF);
64+
playhead.style.left = '0px';
65+
status('Stop');
66+
});
67+
```
68+
69+
---
70+
71+
# 2) Respektuj tempo z MIDI (tempo map)
72+
73+
Když načteš soubor, převezmi první nalezené tempo (fallback na ruční vstup).
74+
75+
**Po `midi = new Midi(...)` v `loadMidiFromArrayBuffer`:**
76+
77+
```js
78+
const tempoEv = midi.header.tempos?.[0];
79+
if (tempoEv && tempoEv.bpm) {
80+
tempoEl.value = Math.round(tempoEv.bpm);
81+
}
82+
```
83+
84+
> Pozn.: @tonejs/midi umí i vícetempové skladby; pro MVP ber první tempo. Později lze přehrávání plánovat v „ticks“ s mapou.
85+
86+
---
87+
88+
# 3) Export zpět do .mid
89+
90+
Umožní stáhnout, co jsi v editoru poskládal.
91+
92+
**Tlačítko do toolbaru:**
93+
94+
```html
95+
<button id="btnExport" class="btn">Export .mid</button>
96+
```
97+
98+
**JS – handler:**
99+
100+
```js
101+
import { Midi } from 'https://cdn.jsdelivr.net/npm/@tonejs/midi@2.0.28/build/Midi.js';
102+
103+
const btnExport = document.getElementById('btnExport');
104+
btnExport.addEventListener('click', ()=>{
105+
const m = new Midi();
106+
m.header.timeSignatures.push({ ticks:0, timeSignature:[4,4], measures:0 });
107+
m.header.setTempo(Number(tempoEl.value) || 120);
108+
109+
const tr = m.addTrack();
110+
// převod čtvrťů -> sekundy
111+
const bpm = Number(tempoEl.value) || 120;
112+
const secPerQ = 60 / bpm;
113+
114+
for (const n of notes) {
115+
tr.addNote({
116+
midi: n.pitch,
117+
time: n.timeQ * secPerQ,
118+
duration: n.durQ * secPerQ,
119+
velocity: Math.max(0, Math.min(1, n.vel ?? 0.8)),
120+
});
121+
}
122+
123+
const blob = new Blob([m.toArray()], { type: 'audio/midi' });
124+
const a = document.createElement('a');
125+
a.href = URL.createObjectURL(blob);
126+
a.download = 'midi_editor_export.mid';
127+
document.body.appendChild(a);
128+
a.click();
129+
a.remove();
130+
});
131+
```
132+
133+
---
134+
135+
# 4) Rychlá kvantizace vybraných not
136+
137+
Jedno tlačítko, které srovná vybranou notu na mřížku.
138+
139+
**Tlačítko:**
140+
141+
```html
142+
<button id="btnQuant" class="btn">⌁ Kvantizovat</button>
143+
```
144+
145+
**JS – handler:**
146+
147+
```js
148+
const btnQuant = document.getElementById('btnQuant');
149+
btnQuant.addEventListener('click', ()=>{
150+
if (!selectedId) return;
151+
const n = notes.find(x=>x.id===selectedId);
152+
if (!n) return;
153+
const step = 1/Math.max(1, Number(gridEl.value)||4);
154+
n.timeQ = Math.max(0, Math.round(n.timeQ / step) * step);
155+
n.durQ = Math.max(1/16, Math.round(n.durQ / step) * step);
156+
redrawGrid(); buildRuler(); updateSelectedPanel();
157+
});
158+
```
159+
160+
---
161+
162+
# 5) Jemné doladění UX editoru
163+
164+
* **Zabránit nechtěnému označování textu** během drag:
165+
166+
```css
167+
.grid, .note { user-select:none; -webkit-user-select:none; }
168+
```
169+
170+
* **Arrow klávesy** pro jemný posun vybrané noty:
171+
172+
```js
173+
document.addEventListener('keydown', (e)=>{
174+
if (!selectedId) return;
175+
const n = notes.find(x=>x.id===selectedId);
176+
if (!n) return;
177+
const stepQ = 1/Math.max(1, Number(gridEl.value)||4);
178+
if (e.key==='ArrowLeft'){ n.timeQ = Math.max(0, n.timeQ - stepQ); }
179+
else if (e.key==='ArrowRight'){ n.timeQ = n.timeQ + stepQ; }
180+
else if (e.key==='ArrowUp'){ n.pitch = Math.min(MAX_NOTE, n.pitch + 1); }
181+
else if (e.key==='ArrowDown'){ n.pitch = Math.max(MIN_NOTE, n.pitch - 1); }
182+
else return;
183+
e.preventDefault();
184+
redrawGrid(); selectNote(n.id);
185+
});
186+
```
187+
188+
* **Kolečko myši nad notou mění velocity** (rychlejší výuka dynamiky):
189+
190+
```js
191+
gridElArea.addEventListener('wheel', (e)=>{
192+
const el = e.target.closest('.note');
193+
if (!el) return;
194+
e.preventDefault();
195+
const n = notes.find(x => x.id === el.dataset.id);
196+
if (!n) return;
197+
const delta = (e.deltaY < 0 ? 0.05 : -0.05);
198+
n.vel = Math.max(0.05, Math.min(1, (n.vel ?? 0.8) + delta));
199+
selectNote(n.id);
200+
});
201+
```
202+
203+
---
204+
205+
# 6) Robustnější načítání demo souborů
206+
207+
Na GitHub Pages občas selže caching/manifest; pro jistotu ještě:
208+
209+
* v `populateDemoSelect()` přidej `cache: 'no-store'` (už máš),
210+
* validuj CORS v konzoli (u cizích URL),
211+
* udrž placeholder v selectu (děláš dobře),
212+
* případně dej fallback na pár vestavěných Base64 dem.
213+
214+
---
215+
216+
# 7) Drobné bezpečné defaulty
217+
218+
* Když nejsou žádné noty: `estimateTotalQ()` už řeší minimum 32 — super.
219+
* Při přehrávání: disable/enable tlačítka, aby se nespouštělo víckrát:
220+
221+
```js
222+
btnPlay.disabled = true;
223+
Tone.Transport.scheduleOnce(()=>{ btnPlay.disabled = false; }, endSec);
224+
```
225+

numer_of_lines_of_code.bat

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
@echo off
2+
REM Usage: numer_of_lines_of_code.bat [path]
3+
REM If path not provided, current directory is used.
4+
setlocal EnableDelayedExpansion
5+
6+
rem --- root directory (optional first argument) ---
7+
set "root=%~1"
8+
if "%root%"=="" set "root=%cd%"
9+
10+
pushd "%root%" >nul 2>&1 || (
11+
echo Error: cannot access "%root%"
12+
exit /b 1
13+
)
14+
15+
rem --- counters ---
16+
set /a jsFiles=0, jsLines=0
17+
set /a htmlFiles=0, htmlLines=0
18+
set /a cssFiles=0, cssLines=0
19+
20+
rem --- helper to count files/lines only under src directory ---
21+
if not exist "src\" (
22+
echo Pozor: adresar "src" nenalezen v %root%. Nulove statistiky.
23+
) else (
24+
rem We use nested FOR to run: find /v /c "" < "file" which returns a single number.
25+
for /R "src" %%F in (*.js) do (
26+
set /a jsFiles+=1
27+
for /f %%N in ('find /v /c "" ^< "%%~fF"') do set /a jsLines+=%%N
28+
)
29+
for /R "src" %%F in (*.html) do (
30+
set /a htmlFiles+=1
31+
for /f %%N in ('find /v /c "" ^< "%%~fF"') do set /a htmlLines+=%%N
32+
)
33+
for /R "src" %%F in (*.css) do (
34+
set /a cssFiles+=1
35+
for /f %%N in ('find /v /c "" ^< "%%~fF"') do set /a cssLines+=%%N
36+
)
37+
)
38+
39+
set /a totalFiles = jsFiles + htmlFiles + cssFiles
40+
set /a totalLines = jsLines + htmlLines + cssLines
41+
42+
rem --- column widths ---
43+
set /a COL1=8 & rem file type (left)
44+
set /a COL2=6 & rem files count (right)
45+
set /a COL3=12 & rem lines count (right)
46+
47+
rem a string of spaces used for padding (must be >= max column width)
48+
set "SPACES= " & rem 40 spaces
49+
50+
rem --- print table ---
51+
echo.
52+
echo Statistiky po typu souboru (pracovni adresar: %root%)
53+
echo.
54+
rem header with aligned columns
55+
call :padRight "Type" %COL1% h1
56+
call :padLeft "Files" %COL2% h2
57+
call :padLeft "Lines" %COL3% h3
58+
echo %h1% ^| %h2% ^| %h3%
59+
60+
rem print separator line matching total width
61+
set /a totalWidth = COL1 + 3 + COL2 + 3 + COL3
62+
set "sep="
63+
for /L %%i in (1,1,%totalWidth%) do set "sep=%sep%-"
64+
echo %sep%
65+
66+
call :printRow ".js" %jsFiles% %jsLines%
67+
call :printRow ".html" %htmlFiles% %htmlLines%
68+
call :printRow ".css" %cssFiles% %cssLines%
69+
70+
echo %sep%
71+
call :printRow "TOTAL" %totalFiles% %totalLines%
72+
echo.
73+
74+
popd >nul 2>&1
75+
endlocal
76+
exit /b 0
77+
78+
:printRow
79+
REM args: %1 = label, %2 = files, %3 = lines
80+
set "label=%~1"
81+
set "files=%~2"
82+
set "lines=%~3"
83+
call :padRight "%label%" %COL1% labelP
84+
call :padLeft "%files%" %COL2% filesP
85+
call :padLeft "%lines%" %COL3% linesP
86+
echo %labelP% ^| %filesP% ^| %linesP%
87+
exit /b
88+
89+
:padRight
90+
REM args: %1 text, %2 width, returns var name in %3
91+
set "txt=%~1"
92+
set /a width=%~2
93+
set "res=%txt%%SPACES%"
94+
REM take leftmost 'width' chars, use CALL to defer expansion so substring is evaluated at runtime
95+
call set "%~3=%%res:~0,%width%%%"
96+
exit /b
97+
98+
:padLeft
99+
REM args: %1 text, %2 width, returns var name in %3
100+
set "txt=%~1"
101+
set /a width=%~2
102+
set "res=%SPACES%%txt%"
103+
REM take rightmost 'width' chars (negative start) using CALL to defer expansion
104+
call set "%~3=%%res:~-%width%%%"
105+
exit /b

0 commit comments

Comments
 (0)