mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-21 03:34:04 +09:00
playgui: better wavescope visuals
This commit is contained in:
6
2taud.sh
6
2taud.sh
@@ -11,6 +11,6 @@ for f in *.XM; python3 xm2taud.py $f assets/disk0/home/music/(basename $f .XM).t
|
|||||||
for f in *.mon; python3 mon2taud.py $f assets/disk0/home/music/(basename $f .mon).taud; end
|
for f in *.mon; python3 mon2taud.py $f assets/disk0/home/music/(basename $f .mon).taud; end
|
||||||
for f in *.MON; python3 mon2taud.py $f assets/disk0/home/music/(basename $f .MON).taud; end
|
for f in *.MON; python3 mon2taud.py $f assets/disk0/home/music/(basename $f .MON).taud; end
|
||||||
|
|
||||||
for f in *.mid; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .mid).taud --force-synth-loop; end
|
for f in *.mid; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .mid).taud --force-synth-loop --mixingvol 255; end
|
||||||
for f in *.MID; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .MID).taud --force-synth-loop; end
|
for f in *.MID; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .MID).taud --force-synth-loop --mixingvol 255; end
|
||||||
for f in *.midi; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .midi).taud --force-synth-loop; end
|
for f in *.midi; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .midi).taud --force-synth-loop --mixingvol 255; end
|
||||||
|
|||||||
@@ -654,9 +654,13 @@ function aa_alowed(i) {
|
|||||||
const c = i & 0xff
|
const c = i & 0xff
|
||||||
const attr = (i >>> 8)
|
const attr = (i >>> 8)
|
||||||
if (attr >= AA_NATTRS) return false
|
if (attr >= AA_NATTRS) return false
|
||||||
// printable ASCII, space, or extended (>160) — keep AA_EIGHT chars so the
|
// Printable ASCII + space ONLY. Excluding the CP437 shade / solid-block /
|
||||||
// glyph palette includes the TSVM ROM's box-drawing / shade / dot range.
|
// half-block range (▒ ▓ █ ▄ ▌ ▀, codes 0xB0-0xDF) is what keeps the
|
||||||
if (!(c >= 33 && c <= 126) && c !== 0x20 && !(c > 160)) return false
|
// wavescope trace thin: a fully-lit cell now resolves to a dense *ASCII*
|
||||||
|
// glyph (# a 6 J) whose inter-stroke gaps read as a fine scope line rather
|
||||||
|
// than a filled bar. The mini-AAlib has no other consumer, so this only
|
||||||
|
// affects the wavescope.
|
||||||
|
if (!(c >= 33 && c <= 126) && c !== 0x20) return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -914,10 +918,11 @@ function aa_render(img, scrW, scrH, tbOut, attrOut) {
|
|||||||
// then converted to ASCII glyphs by the mini-AAlib above. Mid-signal only —
|
// then converted to ASCII glyphs by the mini-AAlib above. Mid-signal only —
|
||||||
// stereo info lives on the bottom bar.
|
// stereo info lives on the bottom bar.
|
||||||
//
|
//
|
||||||
// Three monochrome intensities pick out the wave's body / peaks: DIM cells
|
// The mini-AAlib palette is ASCII-only (no CP437 block glyphs), so the trace
|
||||||
// are the dim trace, NORMAL cells are the bulk of the waveform, BOLD cells
|
// stays a fine line instead of a filled bar. Colour is by DENSITY, not AA
|
||||||
// land on the brightest patches (full-blocked peaks). Amber → white ramp
|
// weight: each cell's lit-pixel count drives a blue→orange ramp — sparse
|
||||||
// mimics phosphor bloom.
|
// fringes read blue, the solid body reads orange — matching the VISUALS
|
||||||
|
// section's cool-ground / warm-beam language.
|
||||||
|
|
||||||
const AA_WAVE_W = AG_LANE_W // 78 cells
|
const AA_WAVE_W = AG_LANE_W // 78 cells
|
||||||
const AA_WAVE_H = AG_ROW_WAVE_BOT - AG_ROW_WAVE_TOP + 1 // 3 cells
|
const AA_WAVE_H = AG_ROW_WAVE_BOT - AG_ROW_WAVE_TOP + 1 // 3 cells
|
||||||
@@ -928,8 +933,11 @@ const ag_waveImg = new Uint8Array(AA_WAVE_IW * AA_WAVE_IH)
|
|||||||
const ag_waveTb = new Uint8Array(AA_WAVE_W * AA_WAVE_H)
|
const ag_waveTb = new Uint8Array(AA_WAVE_W * AA_WAVE_H)
|
||||||
const ag_waveAttr = new Uint8Array(AA_WAVE_W * AA_WAVE_H)
|
const ag_waveAttr = new Uint8Array(AA_WAVE_W * AA_WAVE_H)
|
||||||
|
|
||||||
// AA_NORMAL=0, AA_DIM=1, AA_BOLD=2 → amber phosphor palette.
|
// Per-cell colour by trace DENSITY (lit source-pixels in the cell, 0..4),
|
||||||
const AG_WAVE_FG = [166, 130, AG_COL_LABEL]
|
// blue→orange exactly like the VISUALS section: sparse fringes read blue (the
|
||||||
|
// cool "ground" from AG_STEREO_COL), the solid body reads orange/gold (the
|
||||||
|
// warm peak from AG_BEAM_PAL). Index 0 is background (empty cell).
|
||||||
|
const AG_WAVE_DENS_FG = [AG_COL_BG, 94, 130, 166, 220]
|
||||||
|
|
||||||
function ag_drawWavescope() {
|
function ag_drawWavescope() {
|
||||||
const N = AG_SNAPSHOT_N
|
const N = AG_SNAPSHOT_N
|
||||||
@@ -960,16 +968,21 @@ function ag_drawWavescope() {
|
|||||||
|
|
||||||
aa_render(img, AA_WAVE_W, AA_WAVE_H, ag_waveTb, ag_waveAttr)
|
aa_render(img, AA_WAVE_W, AA_WAVE_H, ag_waveTb, ag_waveAttr)
|
||||||
|
|
||||||
// Blit, skipping cells whose packed (attr<<8 | glyph) key is unchanged.
|
// Blit, skipping cells whose packed (density<<8 | glyph) key is unchanged.
|
||||||
for (let r = 0; r < AA_WAVE_H; r++) {
|
for (let r = 0; r < AA_WAVE_H; r++) {
|
||||||
for (let c = 0; c < AA_WAVE_W; c++) {
|
for (let c = 0; c < AA_WAVE_W; c++) {
|
||||||
const idx = r * AA_WAVE_W + c
|
const idx = r * AA_WAVE_W + c
|
||||||
const att = ag_waveAttr[idx]
|
|
||||||
const ch = ag_waveTb[idx]
|
const ch = ag_waveTb[idx]
|
||||||
const key = (att << 8) | ch
|
// Density = lit source-pixels in this cell's 2×2 block (0..4) →
|
||||||
|
// blue (sparse) … orange (dense).
|
||||||
|
const px = (2 * r) * IW + (2 * c)
|
||||||
|
const lit = (img[px] ? 1 : 0) + (img[px + 1] ? 1 : 0)
|
||||||
|
+ (img[px + IW] ? 1 : 0) + (img[px + IW + 1] ? 1 : 0)
|
||||||
|
const fg = AG_WAVE_DENS_FG[lit]
|
||||||
|
const key = (lit << 8) | ch
|
||||||
if (ag_waveGlyph[idx] === key) continue
|
if (ag_waveGlyph[idx] === key) continue
|
||||||
ag_waveGlyph[idx] = key
|
ag_waveGlyph[idx] = key
|
||||||
ag_color(AG_WAVE_FG[att] || AG_COL_LABEL, AG_COL_BG)
|
ag_color(fg, AG_COL_BG)
|
||||||
ag_mvprn(AG_ROW_WAVE_TOP + r, AG_COL_INSIDE_L + c, ch)
|
ag_mvprn(AG_ROW_WAVE_TOP + r, AG_COL_INSIDE_L + c, ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -513,6 +513,7 @@ class IOSpace(val vm: VM) : PeriBase("io"), InputProcessor {
|
|||||||
private class Beeper {
|
private class Beeper {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val ARPRATE = 60
|
||||||
private const val SAMPLE_RATE = 48000
|
private const val SAMPLE_RATE = 48000
|
||||||
// SN76489 NTSC colourburst clock (3579545 Hz) after the chip's internal /32
|
// SN76489 NTSC colourburst clock (3579545 Hz) after the chip's internal /32
|
||||||
// prescaler. The square wave toggles every `divider` master ticks, so one full
|
// prescaler. The square wave toggles every `divider` master ticks, so one full
|
||||||
@@ -520,8 +521,8 @@ private class Beeper {
|
|||||||
// (divider 127 -> 440.4 Hz.)
|
// (divider 127 -> 440.4 Hz.)
|
||||||
private const val MASTER_CLOCK = 3579545.4545454545 / 32.0
|
private const val MASTER_CLOCK = 3579545.4545454545 / 32.0
|
||||||
// Arpeggio note-effects step at 60 Hz: 48000 / 60 = 800 samples per step.
|
// Arpeggio note-effects step at 60 Hz: 48000 / 60 = 800 samples per step.
|
||||||
private const val SAMPLES_PER_ARP_TICK = SAMPLE_RATE / 60
|
private const val SAMPLES_PER_ARP_TICK = SAMPLE_RATE / ARPRATE
|
||||||
private const val CHUNK = 512
|
private const val CHUNK = SAMPLES_PER_ARP_TICK
|
||||||
private const val AMPLITUDE = 8192 // ~ -12 dBFS; square waves are loud
|
private const val AMPLITUDE = 8192 // ~ -12 dBFS; square waves are loud
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user