Game Engine Devlog Part 2 - Audio, threads, and a shell
After spending a while immersed in graphics programming I decided to take a break and work on some game engine elements which aren't graphical.
I'm using WAV files since I've never done audio programming before and this is as simple as it gets, it's just a header then raw data. I decided to use PortAudio
since it's cross platform and very easy to use, I also wrote my own mixer after I heard it's as easy as adding the waveforms together (it really is that easy).
After that, audio programming went from from trivial to hard as nails as I started to deal with sample rates, I had a naive attempt at up-sampling by just repeating frames when necessary which wasn't awful, but my uninformed attempt at down sampling didn't go so well, I considered applying a fourier transform and doing a proper job of it but decided it was too much work and just changed the sample rate of all my files to avoid the problem.
If I play lots of sounds at once I have clipping issues as shown in the video above, if it becomes a problem in normal use I'll either reduce the volume or write a compressor.
I've created an events system with 4 separate queues:
- 1 - user input
- 2 - network input
- 3 - internal functions
- 4 - timers
The events system provides a uniform set of functions for handling input and provides a safe interface for interaction between separate parts of the engine. The implementation is just multiple linked lists containing data and/or functions which are dealt with between commands being sent to the GPU and the game logic. They are separate queues so I can prioritise them if necessary later on.
The network queue is currently unused but I'll come back to this later on in the project.
I've decided to go with C11 threads because they're very easy to use, work natively in Linux and FreeBSD, and aren't hard to get working in Windows (see tinycthreads
and this emulation layer by yohhoy
). In the future I'd like to speed up the game logic using threads, but for now I'm only interested in implementing a shell.
I've included tcl.h so I have an interactive shell which can call my c functions. This was a no-brainer for me after years of working with Cisco equipment and then using tcl/expect to write regression tests for my C compiler. I could have written my own shell but this was so easy to integrate and comes with some nice features. With the exception of debugging memory issues, working with a shell is much nicer than working with a debugger because it doesn't noticably slow down runtime, it can read/write anything, and after I tied it into the event system it can call arbitrary functions without affecting anything in flight.