![]() |
Sounds: the effects and the tunes |
A 48K Spectrum has one bit of sound hardware, bit 4 of port $FE, and Pentagram makes every sound by setting and clearing it with the processor counting in between, the border kept black. While a sound plays nothing else happens, which is why the game stands still for a tune. There are two players. The tunes go through PLAY_NOTE, a note at a time; everything else is a burst of waves from CLICK, which turns the speaker on for B turns of a 13 T-state loop and off for as long, and BEEP, which repeats it C times.
The tune player and its note table are Knight Lore's (see Knight Lore's sounds). A tune is a string of note bytes ended by $FF: the low six bits index NOTES, five octaves of semitones from G#1 to G6 with 0 a rest, and the top two bits are the length less one, one to four units of about 0.155 s. Checked at this build against Knight Lore's snapshot: of the 88 bytes of PLAY_NOTE, the 4 that differ from Knight Lore's play_note ($B2DA) are exactly the two addresses in it, the CALL to ADD_HL_A and the LD of NOTES; and the 183 bytes of NOTES are Knight Lore's note table ($B332) exactly. The six tunes themselves are Pentagram's own: none of them is in Knight Lore.
The effects are Pentagram's. Most in-game sounds are a single burst -- a jump, a bolt, a footstep, a puff. Two are sequences, played a note a turn by EFFECT_NOTE from the end of the main loop's turn (OBJECT_DONE): SOUND_COUNT holds how many notes are left and which of the four effects in EFFECTS, and each turn one note of twelve waves is played, from the last byte of the effect's notes back. Only effects 0 (entering a room) and 1 (picking up or putting down) are ever started; 2 and 3 have their notes and their addresses, and nothing sets them. About a hundred bytes of other sound code and pitch tables, from BLIP_BY_TURN to MORE_PITCHES_D and at BEEP_BY_GRAPHIC, are never reached at all.
Every recording here is the game's own code running in SkoolKit's simulator, each change of the speaker bit logged to the T-state and rendered at 44100 Hz; nothing is synthesised. Each says how it was recorded and how it was checked against what the code implies: the number of waves, and every half-wave's length in T-states from the instructions of the loop that timed it. The simulator has no memory contention, so all of this runs a touch faster, and a touch higher, than on a real Spectrum, where the ULA holds up the OUTs. A quiet turn (room 30, nothing moving) lasts 160,637 T-states, 46 ms; the calls that stand for an effect played over several turns are spaced by that.
Played in full by START once 0 has been pressed at the menu, before the quest is set out and the first room is built.
Recorded: PLAY_TUNE called with DE = TUNE_START.
Checked: 19 notes, 32 units, no rests, from row 37 (G#4, 415 Hz) to row 49 (G#5, 824 Hz). The table's wave counts make that 2,955 waves, so 5,909 edges (the first OUT turns off a speaker already off); the recording has 5,909 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
5.06 s, 5,909 edges, 415 to 824 Hz (G#4 to G#5).
Played in full by BUCKET when the bucket has flown over a quest item: the game stands still while it plays, and then the bucket turns into a puff and the item into its finished form (QUEST_ITEM).
Recorded: PLAY_TUNE called with DE = TUNE_QUEST.
Checked: 11 notes, 15 units, no rests, from row 32 (D#4, 313 Hz) to row 44 (D#5, 619 Hz). The table's wave counts make that 969 waves, so 1,937 edges (the first OUT turns off a speaker already off); the recording has 1,937 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
2.35 s, 1,937 edges, 313 to 619 Hz (D#4 to D#5).
Played in full by WON under the congratulations, when the fifth collectable reaches its place on the pentagram.
Recorded: PLAY_TUNE called with DE = TUNE_WON.
Checked: 31 notes, 48 units, no rests, from row 32 (D#4, 313 Hz) to row 46 (F5, 696 Hz). The table's wave counts make that 3,887 waves, so 7,773 edges (the first OUT turns off a speaker already off); the recording has 7,773 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
7.55 s, 7,773 edges, 313 to 696 Hz (D#4 to F5).
Played in full by GAME_OVER (in WON) under the percentage, after the last life is lost and after the win; then about four seconds' wait, and the menu.
Recorded: PLAY_TUNE called with DE = TUNE_OVER.
Checked: 27 notes, 48 units, no rests, from row 30 (C#4, 279 Hz) to row 44 (D#5, 619 Hz). The table's wave counts make that 3,117 waves, so 6,233 edges (the first OUT turns off a speaker already off); the recording has 6,233 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
7.53 s, 6,233 edges, 279 to 619 Hz (C#4 to D#5).
The notes the tunes use, worked out from NOTES and PLAY_NOTE's timing: B and C time each half-wave, and a unit is that many waves. The table has 61 rows; these are the ones any of the six tunes plays.
| Row | Note | B | C | Waves a unit | Pitch | A unit | Played in |
|---|---|---|---|---|---|---|---|
| 23 | F#3 | 203 | 3 | 29 | 186.7 Hz | 0.155 s | A tune nothing plays |
| 25 | G#3 | 125 | 3 | 33 | 209.3 Hz | 0.158 s | A tune nothing plays |
| 27 | A#3 | 56 | 3 | 37 | 234.5 Hz | 0.158 s | A tune nothing plays |
| 30 | C#4 | 221 | 2 | 44 | 279.2 Hz | 0.158 s | A tune nothing plays, Game over |
| 32 | D#4 | 169 | 2 | 49 | 312.9 Hz | 0.157 s | A quest item done, Game over, The menu, The quest complete |
| 34 | F4 | 123 | 2 | 55 | 350.4 Hz | 0.157 s | A quest item done, Game over |
| 35 | F#4 | 102 | 2 | 58 | 370.6 Hz | 0.156 s | A tune nothing plays |
| 36 | G4 | 81 | 2 | 62 | 393.4 Hz | 0.158 s | A quest item done, Game over, The quest complete |
| 37 | G#4 | 63 | 2 | 65 | 415.2 Hz | 0.157 s | A quest item done, Game over, The menu, The quest complete, The start of a game |
| 38 | A4 | 45 | 2 | 69 | 439.6 Hz | 0.157 s | A quest item done |
| 39 | A#4 | 28 | 2 | 73 | 465.5 Hz | 0.157 s | A tune nothing plays, Game over, The menu, The quest complete, The start of a game |
| 40 | B4 | 12 | 2 | 78 | 492.7 Hz | 0.158 s | The start of a game |
| 41 | C5 | 253 | 1 | 82 | 523.1 Hz | 0.157 s | A quest item done, Game over, The menu, The quest complete |
| 42 | C#5 | 239 | 1 | 87 | 553.2 Hz | 0.157 s | A tune nothing plays, Game over, The menu, The quest complete, The start of a game |
| 44 | D#5 | 213 | 1 | 98 | 619.4 Hz | 0.158 s | A quest item done, Game over, The menu, The quest complete, The start of a game |
| 45 | E5 | 201 | 1 | 104 | 655.6 Hz | 0.159 s | The start of a game |
| 46 | F5 | 189 | 1 | 110 | 696.2 Hz | 0.158 s | The quest complete |
| 47 | F#5 | 179 | 1 | 117 | 734.2 Hz | 0.159 s | The start of a game |
| 49 | G#5 | 159 | 1 | 131 | 824.1 Hz | 0.159 s | The start of a game |
Effect 0: on the first turn in a room OBJECT_DONE sets SOUND_COUNT to four notes of effect 0, and EFFECT_NOTE plays one a turn, twelve waves each, from the last byte of the effect's notes back (EFFECTS). Every room, whether he came through a doorway or started a life there.
Recorded: the game running: a life started in room 30, which has nothing in it that makes a sound, and five turns from the room's first.
Checked: 48 waves, 96 edges (the code implies 48 and 96); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64; one note a turn.
495 ms, 96 edges, 5 turns, 1169 to 2005 Hz.
FOOTSTEP, called by HANDLE_FORWARD for every step of his legs: STEP_SOUND counts them, and every fourth plays two waves, at a half-wave count of 64 and 96 by turns -- a step of the legs is a turn, so a tick every four turns, the pitch alternating.
Recorded: the game running in room 30: walk held for 16 turns.
Checked: 8 waves, 16 edges (the code implies 8 and 16); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64, the step counter going from 1 to 17.
789 ms, 16 edges, 16 turns, 1358 to 2005 Hz.
FIRE_SOUND as the bolt is fired, and then, when it bumps into the wall and turns into a puff, PUFF_SOUND on each of the puff's turns (PUFF): four single waves at pitches read from the ROM, from an address made of the random number and the byte after it, so no two puffs sound alike. Every puff makes it: a bolt's, the player's own when he dies, the bucket's, a thing shot down.
Recorded: the game running in room 30, fire pressed for a turn with him facing the wall at U 150, until the puff has gone.
Checked: 32 waves fired, 64 edges (the code implies 32 and 64); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 76; then 7 bursts of 4 waves, one a turn, every off half-wave inside a burst 13B + 133 for its B (PUFF_SOUND's loop round BEEP), and each burst's four pitches four bytes in a row of the ROM's first 512, less their top bits, as PUFF_SOUND reads them.
851 ms, 120 edges, 18 turns, noise.
PAUSE_BEEP, played by PAUSE as SPACE (or CAPS SHIFT) alone pauses the game and again as it lets it go on: twelve waves at 64 plus SOUND_COUNT's first byte, after adding one to it. So each beep is a little lower than the last, and the two added to the count make EFFECT_NOTE play the last effect's last two notes again on the turns after.
Recorded: the game running in room 30, nothing under way (SOUND_COUNT 0, effect 0): SPACE held 0.3 s, let go 0.5 s, held 0.3 s, let go; recorded on to three turns later. The silence in the middle is the pause, as long as it was held.
Checked: 48 waves, 96 edges (the code implies 48 and 96); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64.
1.37 s, 96 edges, 1169 to 2005 Hz.
Effect 1: every press of the pick-up key that TAKE_OR_LEAVE acts on sets SOUND_COUNT to five notes of effect 1, which EFFECT_NOTE plays one a turn, falling -- whether he picks something up, puts something down, or only moves what he carries along a place.
Recorded: the game running in room 30: collectable record 4 (graphic 148) moved into the room 15 units in front of him, and 1 pressed for a turn; recorded from that turn for seven. He picked it up: its quest record's graphic went to 0, as TAKE_OR_LEAVE marks a thing carried.
Checked: 60 waves, 120 edges (the code implies 60 and 120); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64; one note a turn.
360 ms, 120 edges, 7 turns, 1169 to 2005 Hz.
JUMP_SOUND, from HANDLE_JUMP as he leaves the ground: sixteen single waves at a half-wave count of (C XOR $A5) + C for C = 16 down to 1, so the pitch jumps about rather than sweeping -- Knight Lore's werewolf warble with $A5 in place of $55.
Recorded: one call; it sets its own pitches.
Checked: 16 waves, 32 edges (the code implies 16 and 32); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 83.
22 ms, 32 edges, 670 to 797 Hz.
FIRE_SOUND, from FIRE when a bolt sets off: 32 single waves, each half-wave count the step count less the one before. FIRE comes straight from an LDIR that leaves B at 0, so the counts go 32, 255, 31, 254 ... a high note and a low one interleaved, both falling.
Recorded: one call with B = 0, as the LDIR leaves it.
Checked: 32 waves, 64 edges (the code implies 32 and 64); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 76.
33 ms, 64 edges, 521 to 6530 Hz.
Code and data that the game has but never plays, recorded by calling it to hear what it would have made.
The notes EFFECT_NOTE would play for effect 2, whose address is in EFFECTS and whose notes are there after effect 1's; but the only code that starts an effect sets 0 (OBJECT_DONE) or 1 (TAKE_OR_LEAVE). How many notes it has is not written anywhere; 7 is what fits before effect 3's address, as each effect's address is the last note of the one before.
Recorded: 7 calls of EFFECT_NOTE with SOUND_COUNT set to 7, 6 ... 1 and effect 2, as the game would leave it turn by turn, a quiet turn apart (160,637 T-states, measured below).
Checked: 84 waves, 168 edges (the code implies 84 and 168); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64.
342 ms, 168 edges, 1026 to 2632 Hz.
The notes EFFECT_NOTE would play for effect 3, whose address is in EFFECTS and whose notes are there after effect 1's; but the only code that starts an effect sets 0 (OBJECT_DONE) or 1 (TAKE_OR_LEAVE). How many notes it has is not written anywhere; 3 is what fits before the code at FIRE_SOUND, as each effect's address is the last note of the one before.
Recorded: 3 calls of EFFECT_NOTE with SOUND_COUNT set to 3, 2 ... 1 and effect 3, as the game would leave it turn by turn, a quiet turn apart (160,637 T-states, measured below).
Checked: 36 waves, 72 edges (the code implies 36 and 72); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64.
114 ms, 72 edges, 1358 to 2632 Hz.
BLIP_BY_TURN: twelve waves at one of sixteen pitches from BLIP_PITCHES, picked by the turn counter, if bit 1 of the object's flags was set. No code or table holds its address; it is Knight Lore's movable-block buzz, gated on a flag, left behind.
Recorded: 16 calls with IX on a record whose flags have bit 1 set and the turn counter 0 to 15, a quiet turn apart: what one object calling it every turn would make.
Checked: 192 waves, 384 edges (the code implies 192 and 384); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64.
765 ms, 384 edges, 1619 to 3829 Hz.
The code at BEEP_BY_GRAPHIC, which nothing reaches (the listing keeps it as data): six waves pitched by the low three bits of the object's graphic (its complement, moved to the top three bits), and, entered after its data byte, eight waves pitched by the complement of that byte, which is 0 on the tape and written by nothing.
Recorded: the first part called with IX on a record of graphic 0, 1 ... 7 -- the eight pitches it can make -- then the second part once, a quiet turn apart.
Checked: 56 waves, 112 edges (the code implies 56 and 112); every on half-wave was 13B + 18 T-states for the B the code gives it, and every off half-wave between two waves of a burst was 13B + 64.
435 ms, 112 edges, 519 to 3829 Hz.
At TUNE_UNUSED, in the tunes' format among the others, and no code or table refers to its address: never played in the game. Recorded by handing it to the tune player all the same.
Recorded: PLAY_TUNE called with DE = TUNE_UNUSED.
Checked: 14 notes, 29 units, no rests, from row 23 (F#3, 187 Hz) to row 42 (C#5, 553 Hz). The table's wave counts make that 1,611 waves, so 3,221 edges (the first OUT turns off a speaker already off); the recording has 3,221 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
4.55 s, 3,221 edges, 187 to 553 Hz (F#3 to C#5).