LINUX SDL: Audio issues with HMH code - day 20

I'm following up HMH using pure C and sometimes my own approach to code. I do not change the algorithm, but some times I change the code structure (for example moving some code to an external function to make things easier to read).

Unfortunately my actual code shows some audio artifacts that I don't know how to debug, comparing my code vs day 20 code do not shows any algorithmic differences (as far as I can tell).

I would like to understand what I'm doing wrong, since the official code from day 20 compiles here and do not show the artifacts (except the regular day 20 bugs).

My code can be found here

https://gist.github.com/igormorgado/ecbb0aca7d077887fdc884d18a56fcd0

and can be built with a simple `make`.

Edited by Igor Morgado on
game.c and sdl_game.c are the same file.

Could you describe what's the sound issue ? Is it always wrong ? Is it a glitch at interval ? Is it a glitch when you move the joystick ? Have you tried just playing a fixed continuous tone ?

One difference is that you're using float instead of integers to compute the sample values. I would try to use integers and use the same values as Casey to see if it solves the issue.

This line should use floorf (or plain casting) instead of roundf in your code to be closer to Casey's code.
1
i16 sample_value = (i16)(roundf(sine_value * amplitude));
Indeed, are the same file, my fault. I have fixed that. Please, check the code again.

I have also removed all "unused" code. The code on gist right now only contains audio code, and still shows the clipping.

I tried to `cast/floorf`. But the change didn't improve anything.


Is pretty hard to describe audio issues. Seems that for some period of time, no sound is being played and other period of time there is a full tone.

Changing the frequency of updates at `game_update_hz` the period of sound/nosound changes. For exeample if I set game_update hz to 1. seems there is .5 seconds of pure tone, and .5 seconds of silence. Raising game_update_hz makes the period shorter giving a very unpleasant noise.

I have recorded a audio check if you can access from here:

My code, bad sound... (irrc 120hz)

https://cdn.discordapp.com/attach...4/773630795373084702/imp_code.m4a

Original HMH day 20 sound


https://cdn.discordapp.com/attach...4/773630899627491328/hmh_code.m4a


Thank you for your reply.


Edited by Igor Morgado on
It doesn't sound at all like your recording on my side (could the recording be wrong, because it doesn't sound like what you describe) ?

On my machine (manjaro linux in a virtual machine), the sound is ok except for a glitch after 1 second, and it seems that the glitch is kept in the next loop into the buffer, with an offset, so the sound become worse with time (first loop there is a glitch after 1 second, then there are 2 glitches, at different times, than 3 ...). So it seems that either the copy from the game memory to the global buffer, or the copy from the global buffer to the sdl data has an issue.

In sdl_SoundGetCursors you're multiplying sound_output->safety_bytes by sound_output->bytes_per_sample but that should already be in bytes. Removing that improved the situation for me, I'm left with only a single glitch when the ring buffer loops.

I tried to fill the buffer with a sine wave at 240hz (multiple of 48000hz) at startup, never update the buffer after that and just send that to sdl, but it still had a glitch (although it was hard to hear). I wasn't able to debug that as my the virtual machine is quite slow and working on it is painful, but I would suggest you to get that running, before continuing on other things.

I'm assuming you're trying to stay close to the handmade hero code, because otherwise you could probably simplify the code (and you probably should as the DirectSound API is probably not the best thing to imitate and handmade hero has still not done much in term of audio).

mrmixer
It doesn't sound at all like your recording on my side (could the recording be wrong, because it doesn't sound like what you describe) ?


It sounds, is 1/120 of tone and 1/120 of silence. 60 times per seconds. That is the result. In line `sdl_game.c:162` replace `perf->game_update_hz` with value `120`, you will get something like the weird noise.


mrmixer

On my machine (manjaro linux in a virtual machine), the sound is ok except for a glitch after 1 second, and it seems that the glitch is kept in the next loop into the buffer, with an offset, so the sound become worse with time (first loop there is a glitch after 1 second, then there are 2 glitches, at different times, than 3 ...). So it seems that either the copy from the game memory to the global buffer, or the copy from the global buffer to the sdl data has an issue.


Indeed this also happens...

mrmixer

In sdl_SoundGetCursors you're multiplying sound_output->safety_bytes by sound_output->bytes_per_sample but that should already be in bytes. Removing that improved the situation for me, I'm left with only a single glitch when the ring buffer loops.


Indeed.

mrmixer

I tried to fill the buffer with a sine wave at 240hz (multiple of 48000hz) at startup, never update the buffer after that and just send that to sdl, but it still had a glitch (although it was hard to hear). I wasn't able to debug that as my the virtual machine is quite slow and working on it is painful, but I would suggest you to get that running, before continuing on other things.


What running? Audio? If so, yes, I would like much. I'm almost throwing all that code away and rewriting it from some day ahead (like 30) where the plataform layer reached some point. But I really want to understand what wrong is happening there, otherwise I will be just postponing the problem.

mrmixer

I'm assuming you're trying to stay close to the handmade hero code, because otherwise you could probably simplify the code (and you probably should as the DirectSound API is probably not the best thing to imitate and handmade hero has still not done much in term of audio).


Yes, as close as I can, except I use pure C, snake_case, avoid globals, put things in extenal function calls and using SDL.

What do you think about SDL_AudioQueue vs SDL_AudioCallback?

Should Queue the next frame seconds (in fact 1/FPS ms) be reasonable? Like in start of every game loop clean queue and requeue next frame audio (that to avoid queue to get too behind game updates)? What about audio continuity? It will suffef from those clicks and clancks when resetting audio?

I'm really concerned about audio part, is too important to me in game development.


Edited by Igor Morgado on
I tried at 120hz and the glitch is nowhere near what I ear on your recording. For me it's 1 glitch per second at 60Hz and 2 glitches at 120Hz (when I remove the invalid multiply it's 1 glitch even at 120hz).
Here is a recording at 120hz, without the invalid multiply.

igormorgado
What running? Audio? If so, yes, I would like much. I'm almost throwing all that code away and rewriting it from some day ahead (like 30) where the plataform layer reached some point. But I really want to understand what wrong is happening there, otherwise I will be just postponing the problem.


What I meant was to try to fill your buffer with a sine wav at 240Hz (it should fit perfectly in a 48Khz buffer) when you initialize the application, and never touch it again. Then in your main loop make the code that will upload that in chunk to the SDL and make sure that doesn't glitch.

When you have that working, then move on to update the buffer in the main loop.

igormorgado
What do you think about SDL_AudioQueue vs SDL_AudioCallback?

Should Queue the next frame seconds (in fact 1/FPS ms) be reasonable? Like in start of every game loop clean queue and requeue next frame audio (that to avoid queue to get too behind game updates)? What about audio continuity? It will suffef from those clicks and clancks when resetting audio?


I don't use SDL so I don't know.

It seems that if you can figure out the minimum latency needed to stay ahead of the sound playing, you wouldn't need to clean the queue, only push new audio. It looks a bit like WASAPI (this thread on HMN) on windows (the low level audio api to use instead of DirectSound) where you can only add audio, never overwrite.

If you can make something that allows you to send sound correctly to SDL, don't worry about having the same API as HMH. Anything sound related will be in the game code (e.g. audio mixing) and it doesn't matter how you upload the sound to the "sound card".