mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
audio: triangle and noise sfx channels
Adds `channel: triangle` / `channel: noise` to the `sfx` declaration form. The existing pulse-1 / pulse-2 driver is unchanged (and is still byte-identical for programs that don't use the new channels) — when a program declares a triangle or noise sfx the runtime splices in an additional per-channel slot that writes to $4008- $400B (triangle) or $400C-$400F (noise) on play. Includes a new `examples/noise_triangle_sfx.ne` demo with committed golden PNG + audio hash. https://claude.ai/code/session_01MaNVcDmK9gsspRkdxowQAM
This commit is contained in:
parent
8610aecdac
commit
201664ea04
18 changed files with 1116 additions and 52 deletions
|
|
@ -28,6 +28,7 @@ Open any `.nes` file in an NES emulator ([Mesen](https://www.mesen.ca/), [FCEUX]
|
|||
| `mmc1_banked.ne` | MMC1, banks, multiply | Banked mapper with software multiply |
|
||||
| `palette_and_background.ne` | palette, background, set_palette, load_background | Reset-time initial load plus vblank-safe runtime swaps |
|
||||
| `friendly_assets.ne` | named colours, grouped palette, pixel art, tilemap+legend, palette_map, scalar sfx pitch, note-name music | Exercises every "friendlier" asset syntax at once — the `palette` uses `bg0..sp3` + a shared `universal:`, the sprite is authored as ASCII pixel art, the background uses a `legend { ... } + map:` tilemap with a `palette_map:` for attributes, the sfx uses a scalar `pitch:` + `envelope:` alias, and the music uses note names (`C4, E4 40, rest 10`) with a `tempo:` default. |
|
||||
| `noise_triangle_sfx.ne` | `channel: noise`, `channel: triangle` on `sfx` blocks | Demonstrates the noise and triangle sfx channels. Declares one noise burst and one triangle bass note, plays each on a timer so the emulator harness captures both the pixel output and the APU state. |
|
||||
| `platformer.ne` | **every subsystem** | End-to-end side-scrolling demo: custom CHR tileset, full 32×30 nametable with per-region attribute palettes, 2×2 metasprite hero with gravity/jump physics, wrap-around horizontal scrolling, stomp-or-die enemy collisions with a live stomp-count HUD, coin pickups, user-declared SFX + music, and a Title → Playing → GameOver state machine with a proximity-based autopilot so the headless harness cycles through stomp, stomp, die, and retry inside six seconds. Regenerate the tile art with `cargo run --bin gen_platformer_tiles`. |
|
||||
|
||||
## Emulator Controls
|
||||
|
|
|
|||
70
examples/noise_triangle_sfx.ne
Normal file
70
examples/noise_triangle_sfx.ne
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Noise / Triangle SFX Demo
|
||||
//
|
||||
// Showcases the newer `channel:` property on `sfx` blocks. The audio
|
||||
// driver's per-frame tick gains a noise channel (writes to $400C) and
|
||||
// a triangle channel (writes to $4008) whenever the program declares
|
||||
// at least one sfx targeting those channels. Programs that stick to
|
||||
// pulse 1 still emit the exact same driver code as before.
|
||||
//
|
||||
// Every 30 frames we trigger one of the two effects and move a small
|
||||
// smiley back and forth so it's obvious the program is still running.
|
||||
// The emulator harness sees the registers get poked and hashes the
|
||||
// APU output, so the golden locks in both the pixels *and* the sound.
|
||||
//
|
||||
// Build: cargo run -- build examples/noise_triangle_sfx.ne
|
||||
// Output: examples/noise_triangle_sfx.nes
|
||||
|
||||
game "Noise Triangle SFX" {
|
||||
mapper: NROM
|
||||
}
|
||||
|
||||
// A short, sharp noise burst — perfect for explosions / footsteps.
|
||||
// `pitch: 4` indexes the APU's 16-entry noise period table; lower
|
||||
// values are higher-pitched. `volume` is a per-frame amplitude ramp,
|
||||
// exactly like a pulse sfx.
|
||||
sfx Crash {
|
||||
channel: noise
|
||||
pitch: 4
|
||||
volume: [15, 13, 11, 9, 7, 5, 3, 1]
|
||||
}
|
||||
|
||||
// A sustained triangle "bass" note. Triangle has no volume register,
|
||||
// so `volume:` entries are just "hold" flags — nonzero means sustain,
|
||||
// zero means silence. The numeric value doesn't matter.
|
||||
// `pitch: 60` picks a period-table entry; triangle shares the pulse
|
||||
// period table, and 60 is the lowest note in it (C1).
|
||||
sfx Bass {
|
||||
channel: triangle
|
||||
pitch: 60
|
||||
volume: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
}
|
||||
|
||||
var px: u8 = 120
|
||||
var timer: u8 = 0
|
||||
var bounce: u8 = 0
|
||||
|
||||
on frame {
|
||||
// Bounce the smiley between x=110 and x=140 so frame 180 is
|
||||
// not a still frame — the golden image diff would otherwise
|
||||
// miss the "program is running" signal.
|
||||
timer += 1
|
||||
if bounce == 0 {
|
||||
px += 1
|
||||
if px == 140 { bounce = 1 }
|
||||
} else {
|
||||
px -= 1
|
||||
if px == 110 { bounce = 0 }
|
||||
}
|
||||
|
||||
// Cycle through the two sfx every 30 frames so each channel
|
||||
// retriggers at least twice inside the 180-frame capture window.
|
||||
if timer == 30 { play Crash }
|
||||
if timer == 60 {
|
||||
timer = 0
|
||||
play Bass
|
||||
}
|
||||
|
||||
draw Smiley at: (px, 120)
|
||||
}
|
||||
|
||||
start Main
|
||||
BIN
examples/noise_triangle_sfx.nes
Normal file
BIN
examples/noise_triangle_sfx.nes
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue