From 2fd0d3e07bb1234d7ca23862fbd1082d2ebc17df Mon Sep 17 00:00:00 2001 From: you Date: Sun, 22 Mar 2026 19:36:07 +0000 Subject: [PATCH] Audio Lab: show WHY each parameter has its value Sound Mapping: 3-column table (Parameter | Value | Formula/Reason) Note Sequence: payload index + duration/gap derivation formulas --- public/audio-lab.js | 87 +++++++++++++++++++++++++++++++++++++-------- public/index.html | 2 +- 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/public/audio-lab.js b/public/audio-lab.js index db3edce..12012e2 100644 --- a/public/audio-lab.js +++ b/public/audio-lab.js @@ -78,6 +78,12 @@ .alab-hex .playing { background: #ff6b6b !important; color: #fff !important; border-radius: 2px; padding: 0 2px; transition: background 0.1s; } .alab-note-table tr.playing { background: var(--accent) !important; color: #fff; } .alab-note-table tr.playing td { color: #fff; } + .alab-map-table { width: 100%; font-size: 13px; border-collapse: collapse; } + .alab-map-table td { padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top; } + .alab-map-table .map-param { font-weight: 600; white-space: nowrap; width: 110px; } + .alab-map-table .map-value { font-family: var(--mono); font-weight: 700; white-space: nowrap; width: 120px; } + .alab-map-table .map-why { font-size: 11px; color: var(--text-muted); font-family: var(--mono); } + .map-why-inline { display: block; font-size: 10px; color: var(--text-muted); font-family: var(--mono); margin-top: 2px; } .alab-empty { text-align: center; padding: 60px 20px; color: var(--text-muted); font-size: 15px; } .alab-slider-group { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); } .alab-slider-group input[type=range] { width: 80px; } @@ -133,11 +139,22 @@ const volume = Math.min(0.6, 0.15 + (obsCount - 1) * 0.02); const voiceCount = Math.min(Math.max(1, Math.ceil(Math.log2(obsCount + 1))), 8); let panValue = 0; + let panSource = 'no location data → center'; try { const d = JSON.parse(pkt.decoded_json || '{}'); - if (d.lon != null) panValue = Math.max(-1, Math.min(1, mapRange(d.lon, -125, -65, -1, 1))); + if (d.lon != null) { + panValue = Math.max(-1, Math.min(1, mapRange(d.lon, -125, -65, -1, 1))); + panSource = `lon ${d.lon.toFixed(1)}° → map(-125...-65) → ${panValue.toFixed(2)}`; + } } catch {} + // Detune description + const detuneDesc = []; + for (let v = 0; v < voiceCount; v++) { + const d = v === 0 ? 0 : (v % 2 === 0 ? 1 : -1) * (v * 5 + 3); + detuneDesc.push((d >= 0 ? '+' : '') + d + '¢'); + } + const bpm = MeshAudio.getBPM ? MeshAudio.getBPM() : 120; const tm = 60 / bpm * speedMult; @@ -159,6 +176,7 @@ oscType, scaleName, hopCount, obsCount, totalSize: allBytes.length, payloadSize: payloadBytes.length, color: TYPE_COLORS[typeName] || TYPE_COLORS.UNKNOWN, + panSource, detuneDesc, }; } @@ -193,29 +211,68 @@

🎵 Sound Mapping

-
-
Instrument
${m.oscType}
-
Scale
${m.scaleName}
-
Notes
${m.noteCount} (√${m.payloadSize})
-
Filter Cutoff
${m.filterHz} Hz
-
Volume
${m.volume} (${m.obsCount} obs)
-
Voices
${m.voiceCount}
-
Pan
${m.panValue}
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Instrument${m.oscType}payload_type = ${m.typeName} → ${m.oscType} oscillator
Scale${m.scaleName}payload_type = ${m.typeName} → ${m.scaleName} (root MIDI ${SCALE_INTERVALS[m.typeName]?.root || 48})
Notes${m.noteCount}⌈√${m.payloadSize}⌉ = ⌈${Math.sqrt(m.payloadSize).toFixed(1)}⌉ = ${m.noteCount} bytes sampled evenly across payload
Filter Cutoff${m.filterHz} Hz${m.hopCount} hops → map(1...10 → 8000...800 Hz) = ${m.filterHz} Hz lowpass — more hops = more muffled
Volume${m.volume}min(0.6, 0.15 + (${m.obsCount} obs − 1) × 0.02) = ${m.volume} — more observers = louder
Voices${m.voiceCount}min(⌈log₂(${m.obsCount} + 1)⌉, 8) = ${m.voiceCount} — more observers = richer chord
Detune${m.detuneDesc.join(', ')}${m.voiceCount} voices detuned for shimmer — wider spread with more voices
Pan${m.panValue}${m.panSource}

🎹 Note Sequence

- - ${m.notes.map((n, i) => ` + + ${m.notes.map((n, i) => { + const durWhy = `byte ${n.byte} → map(0...255 → 50...400ms) × tempo`; + const gapWhy = i < m.notes.length - 1 + ? `|${n.byte} − ${m.notes[i+1].byte}| = ${Math.abs(m.notes[i+1].byte - n.byte)} → map(0...255 → 30...300ms) × tempo` + : ''; + return ` + - - - `).join('')} + + + `;}).join('')}
#ByteMIDIFreqDurationGap
#Payload IndexByte→ MIDI→ FreqDuration (why)Gap (why)
${i + 1}[${n.index}] 0x${n.byte.toString(16).padStart(2, '0').toUpperCase()} (${n.byte}) ${n.midi} ${n.freq} Hz${n.duration} ms${i < m.notes.length - 1 ? n.gap + ' ms' : '—'}
${n.duration} ms ${durWhy}${i < m.notes.length - 1 ? n.gap + ' ms ' + gapWhy + '' : '—'}
diff --git a/public/index.html b/public/index.html index 4d3c663..3500474 100644 --- a/public/index.html +++ b/public/index.html @@ -93,7 +93,7 @@ - +