Page 1 of 1

How do I make background sound?

Posted: Wed Nov 14, 2018 10:57 am
by dfzx
I'm looking at making sound on the 48K, and although I can see how it basically works, I can't get my head around making sounds "in the background."

I get that toggling the speaker's bit in port 254 makes it go 'click', and that toggling it repeatedly with carefully controlled pauses between the clicks can make those clicks run at, say, 261Hz, which makes a middle C tone. But all the examples I've managed to find on the interwebs do exactly that - play tones using 100% of the CPU in a carefully controlled loop. That's not so much use in games. Even the squeak-and-pop examples I'm finding hog the CPU causing the game to stutter. i.e. if it's squeaking it can't be updating the screen. Yet many games have smooth graphics alongside the sounds. Some have music playing, admittedly with rather wobbly notes, but they're still notes that don't disturb game play.

So how is that done? Interrupts, somehow? Move a bit, beep a bit? If it's the latter I'd expect it to sound a bit stuttery, or crackly. So what's the approach? Initially I'd like a few pings and pops in my 50fps game, but background music is the ultimate aim.

Re: How do I make background sound?

Posted: Wed Nov 14, 2018 11:58 am
by Ralf
Well, it's all true that you write. To make sound from the beeper you have to flip the bit responsible for the sound between 0 and 1 and send this value to port 254. There is no other way.

Getting different sound effects depends on about precise caluclations when you should send 0 or 1 to the beeper

Quality tunes created for the beeper use 100% of CPU time, not allowing for anything else. In the games you have a lot of other stuff to do so you can't devote all CPU time to beeper. You have to deal with it in short pauses between doing other things.

Doing it right generally needs experimenting. Or analysing others' code. You may search old magazines for sound effects, there should be quite a lot of this stuff.

Re: How do I make background sound?

Posted: Wed Nov 14, 2018 12:35 pm
by dfzx
Ralf wrote: Wed Nov 14, 2018 11:58 am You may search old magazines for sound effects, there should be quite a lot of this stuff.
You'd think! But everything I can find describes techniques which play using 100% CPU time.

I was just trying to work out the JSW disassembly. It looks like each "note" of the background music is played for a very short duration. So I'm thinking that if I can work out how much of my frame time is left over after all the game logic, I can play a note that long. e.g. I have 1/50th of a second in total (20ms). If my game logic takes half of that (10ms) I could use the other 10ms to play a 10ms musical note. If my note has a duration of more than that I need to play enough 10ms notes sequentially to get the duration I need.

All of which makes sense, but it rather assumes my game logic always takes the same amount of time, leaving me a consistent amount with which to play the music. I don't think my game logic is that consistent. So I start thinking maybe I can play music until the interrupt fires, at which point I somehow stop playing music and go back to doing game logic. Or something like that. Seems... complicated!

Re: How do I make background sound?

Posted: Wed Nov 14, 2018 3:49 pm
by Joefish
To play a note of a tune in the background you need one of these routines to toggle the bit high/low with precise timing. And as you suggest, you run the game code for a bit, then play the note for a bit, and repeat. This is why the music in Manic Miner sounds so farty, like someone blowing a raspberry to the tune - because it's cutting in and out many times a second.

Joffa Smith mastered the technique you're suggesting by synching it with his screen update in games like Green Beret and Hysteria. The way it worked is the screen update started on an interrupt. It took a little less than 2 frames of the TV picture to refresh the game, then it went into the sound routine to play the current note. It was arranged so that the very next interrupt would break out of the sound routine and trigger the next game refresh cycle. An interrupt routine doesn't have to return to the function it interrupted. In the interrupt handler, just do a POP to pull the return address off the stack and throw it away, then jump to whatever code you want to run next. Remember you actually have to tell the Spectrum to 'EI' from one firing of the interrupt routine to the next, so just hold off doing 'EI' until you enter your sound routine. Do the keyboard checking in your own code; don't rely on the interrupt for anything except timing.

Some of the better tunes are the little ditties played in Head Over Heels when you collect significant objects. Note though that this technique works best with higher pitches, as then there are more cycles within the brief time the note is played for.

There is another method though, and I don't know of any game that uses it except my own. For sound effects, you don't need to be able to play precise notes. You just want a tone or noise you can play at different pitches, then ramp up for a laser 'pew' or ramp down for a 'thud'. So if you've already got a big loop function in your code (such as copying or clearing a screen) you can put a series of bytes in a buffer, then send them to OUT 254 one at a time at the end of each pass of your loop.
Then to make a high tone you can fill the buffer with a 0-1-0-1 pattern, 0-0-1-1-0-0-1-1 for a lower tone, random data for noise, etc. For better noise, fill a buffer twice as big as you need with random numbers and jiggle the start position randomly each time it's re-used. Or you could just take numbers from the ROM, they're random enough in most places to generate noise. And repeat the same number two or three times in the buffer to lower the frequency range of the noise.

You won't get a perfectly tuned middle C, but it might be more consistent and so louder than the previous method, particularly if your big program loop is already taking up most of the processing time in your game. This method is better for lower pitches and random noise. And if you genuinely want a squidgy splat, you can still turn the whole thing on/off every few frames to get that farty effect.

Re: How do I make background sound?

Posted: Wed Nov 14, 2018 5:16 pm
by utz
Ahh, background beeper music, the holy grail of ZX 48K programming :mrgreen:

While the Joffa approach certainly works, it won't help much if you plan on making a 50 Hz or 25 Hz game. There is another solution, but it's so tedious that it has never been done.

Ok, so your idea about dedicating half a frame to logic and half a frame to music won't work, because this will give a loud 50Hz buzz on top of everything. What you do need to do is update the sound continually, which means you have to interleave it with the game logic. Which, like multicolor, needs precise timing. But unlike with multicolor, timing needs to be cycle-exact at all times, which means you basically can't have an interrupt running. It is ok to periodically spend a few extra cycles at a rate of <50Hz (but not too many) because that'll generate a tone outside of the audible range. You can also get away with interrupting sound generation for a longer period once in a while, as demonstrated by this demo. Another one you might want to look up is the credits part in my 2014 music disk, which features a 25fps scroller with BASIC BEEP style music. I can probably dig up introspec's code for that if you're interested.

Re: How do I make background sound?

Posted: Wed Nov 14, 2018 5:43 pm
by Ast A. Moore
The best I’ve seen recently is Dark Fusion. The music plays in the menu while the star field is being updated at 50 fps (plus the attribute flashing of the highlighted menu item). Pretty darned impressive.

Re: How do I make background sound?

Posted: Thu Nov 15, 2018 2:09 am
by Joefish
Joffa's scrolling games did all run at 25fps.

Re: How do I make background sound?

Posted: Thu Nov 15, 2018 9:49 am
by utz
And which of them have ingame music?

Re: How do I make background sound?

Posted: Thu Nov 15, 2018 10:32 am
by dfzx
Having read the responses I decided I'm still a bit out of my depth with this sort of thing. :?

I decided to cheat and look at the background music code in a Manic Miner disassembly. The loop which plays a single note has its timing hardcoded - if my reading of it is correct it goes round 768 times. It takes 31,000 T-states, which, again based on my understanding, means each note is about 8.8ms. The data numbers only represent pitch, so it's quite simple.

I took that code and dropped it into my game, which is written in C and runs at 50fps. Manic Miner appears to run at maybe 12.5fps, so I call the play-a-note function once every 4 times round my loop. It sounds spot on!

In Manic Miner each frame lasts about 80ms, of which about 10% is taken by the CPU playing music. So in fact the game is actually about 90% silent, but because the sound is played frequently (4 times a second) it sounds fine (for an 80's micro). I'm not sure where this takes me, but it struck me as interesting. :)

Re: How do I make background sound?

Posted: Thu Nov 15, 2018 11:02 am
by Ralf
And which of them have ingame music?
I suppose none :) But they have decent sound effects.

If you are interested in games with beeper music, check O.K. Yah:
https://spectrumcomputing.co.uk/index.p ... 96&id=3499

The game is crap in terms of gameplay but has quite a lot of action on the screen + ingame beeper music, better than Manic Miner I would say.

Re: How do I make background sound?

Posted: Mon Nov 19, 2018 9:58 am
by Ast A. Moore
Found another interesting example: Moonlight Madness. It’s two-channel, naturally choppy, but not as grating as Manic Miner, and doesn’t seem to conflict with other sound effects.

Re: How do I make background sound?

Posted: Mon Dec 24, 2018 4:28 am
by Cosmium
From what I can remember when I programmed the sound effects (not music) code on Quadron, I had the 50Hz interrupt handler checking a pointer variable that if non-0 would step through a list of encoded frequencies each frame. These 'frequencies' were really the delay introduced between turning the sound bit on and off and then OUTted to the sound port. Higher numbers meant bigger delays hence lower notes, and consequently a slight slow down of the rest of the game. But this was a lot better than calling the 100% CPU hogging BEEP routine!

The list of encoded frequencies were made by trial and error. I kind of got the feel for it after a while and got some quite varied sound effects in the end! To prepare them I think I had a simple BASIC program reading through a list with READ and DATA statements and POKEing them into memory to form the list ready for the interrupt routine. Doing it in BASIC made editing and changes easier.

Once I had all the sound effect lists ready it was just a case of saving them out with SAVE "sfx" CODE xxxxx,xxx so that it could be included by the assembler later.

Re: How do I make background sound?

Posted: Thu Feb 21, 2019 10:58 am
by Turtle_Quality
I've been wondering the same about background sound, I'm very slowly writing a game that will be in assembly (New Year's Resolution), and probably overcomplicating the whole thing for myself.

Back in the day I used nested loops to make differing sound effects, with increasing/falling pitch or weird combinations to make sound patterns -sirens, quacks (using a duck nested loop obviously) etc... I had some code (long lost) that would "sing" text by plotting the pixels of a word in a pseudo random way at the same time as sounding a note (or two). By maintaining the sound after a updating each byte on screen the sound was continous.

I'm guessing that most in game sound engines work by playing some sound on the interrupt before the graphics render, or play sound after the graphics render up until the interrupt, giving it a stuttered effect ; sound for half a frame then silent.

I'm wondering if it's feasible to embed very frequent calls to a continous sound engine, after each sprite row and other items in the game loop, maybe every 60 t states or thereabouts, to enable smooth sound fx. The continuous sound engine would then need to decide if it's time for a click and decide the next frequency. It could use the r register and a small delay loop to ensure consistent number of t states before a click. Just don't keep resetting the r register, not on a real speccy. Did the refresh occur when R = 127 ? Maybe set it to 127 then for a timer reset.

I'm still musing and doing the maths to see if this feasible. The game I'm working on should not be too CPU intensive on it's own so I reckon I can make 50fps with sound. And I'm talking about the beeper here obviously, not the AY chip. And I'm aiming just to make sound effects rather than backround music (although some of the best fx are musical and need a good pitch : the Penetrator bugle call before the game starts, a congratulatory "tada" effect etc..)