Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m0s
akbasic CI Build / sanitizers (push) Successful in 3m45s
akbasic CI Build / coverage (push) Failing after 3m22s
akbasic CI Build / akgl_build (push) Failing after 21s
akbasic CI Build / mutation_test (push) Successful in 11m26s
Organised the way the C128 Programmer's Reference Guide is: the language first, then each hardware area, then the reference sections. One markdown file per chapter. The verb and function references are generated from the interpreter's own dispatch table, with an assertion that every row is described, so they cannot drift out of step with what the program accepts. 98 verbs and 30 functions. Every example was run before it was written down, which caught three claims that were wrong: a whole FOR loop on one line prints nothing rather than looping once, MID and INSTR count from zero where a C128 counts from one, and a multi-line DEF returns a value the caller has to assign away. Chapter 13 is the list a BASIC 7.0 programmer needs -- roughly sixty documented differences, including the two known FOR defects and the fact that drawing does not survive a frame. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
2.8 KiB
Markdown
95 lines
2.8 KiB
Markdown
# 7. Sound
|
|
|
|
The sound verbs need the SDL build and an audio device. Without one they refuse by
|
|
name, and a machine with no sound card still runs the interpreter — only `SOUND` and
|
|
`PLAY` fail, and they say so.
|
|
|
|
## SOUND
|
|
|
|
```
|
|
10 SOUND 1, 4000, 60
|
|
```
|
|
|
|
Voice, frequency, duration. Voices are numbered from 1. The duration is in *jiffies* —
|
|
sixtieths of a second — as on a C128, so 60 is one second.
|
|
|
|
Further arguments give a frequency sweep: a step, a direction and a range, which is
|
|
what makes a siren or a laser.
|
|
|
|
**`SOUND` does not block.** On a C128 the note starts and the program carries on; here
|
|
the same is true, and it has to be — an embedded interpreter that stopped the host's
|
|
frame loop for a second would freeze the game.
|
|
|
|
## PLAY
|
|
|
|
`PLAY` takes a string of notes:
|
|
|
|
```
|
|
10 PLAY "C D E F G"
|
|
```
|
|
|
|
Within the string:
|
|
|
|
| | |
|
|
|---|---|
|
|
| `A` to `G` | a note |
|
|
| `#` `$` | sharp, flat |
|
|
| `O` *n* | octave |
|
|
| `V` *n* | voice |
|
|
| `T` *n* | envelope preset |
|
|
| `U` *n* | volume |
|
|
| `W`, `H`, `Q`, `I`, `S` | whole, half, quarter, eighth, sixteenth notes |
|
|
| `R` | rest |
|
|
| `.` | dotted |
|
|
|
|
Notes are queued and released in time, paced by the host's clock. A program that
|
|
`PLAY`s and then carries on works the way you expect; a program that `PLAY`s and then
|
|
immediately `QUIT`s may not hear all of it.
|
|
|
|
`M` — measure — is accepted and does nothing. There is no bar-line accounting to do.
|
|
|
|
## TEMPO
|
|
|
|
```
|
|
10 TEMPO 8
|
|
```
|
|
|
|
How fast `PLAY` releases its queue. The number is the same range a C128 uses; the
|
|
constant that turns it into milliseconds is a calibration choice rather than a
|
|
transcription, so it may not match a real machine exactly.
|
|
|
|
## ENVELOPE and VOL
|
|
|
|
```
|
|
10 ENVELOPE 0, 5, 9, 4, 6
|
|
20 VOL 8
|
|
```
|
|
|
|
`ENVELOPE` defines one of the ten presets `PLAY`'s `T` selects: attack, decay, sustain
|
|
and release. `VOL` sets the overall volume, 0 to 15.
|
|
|
|
## FILTER
|
|
|
|
`FILTER` parses and then **refuses at execution**:
|
|
|
|
```
|
|
? 10 : RUNTIME ERROR FILTER needs an audio device that can filter, and this one cannot
|
|
```
|
|
|
|
It sets the SID's filter cutoff, band switches and resonance. The audio backend
|
|
synthesises raw waveforms and mixes them; there is no filter stage to configure and
|
|
SDL supplies no primitive to build one from. Refusing is deliberate — a program that
|
|
asks for a low-pass and gets an unfiltered square wave has been lied to.
|
|
|
|
This is the one verb still waiting on a capability from the graphics library. It is
|
|
filed there rather than worked around here.
|
|
|
|
## The clock
|
|
|
|
Everything with a duration — `SOUND`, `PLAY`, `TEMPO`, and `SLEEP` from Chapter 2's
|
|
neighbourhood — is paced by a clock the *host* provides. The standalone interpreter
|
|
sets it from a monotonic timer every step, so it just works.
|
|
|
|
An embedded host that never sets the clock gets durations that expire immediately:
|
|
audible, but never a hang. That is the intended failure direction.
|