Sound handling
Death Dawn's sound code is designed to abstract the lower-level details of the sound system from the game, and provide a method for sounds to be processed even if sound output is disabled. The sound code consists of several components, split over the sounds.c and sndbuf.s files:
Sample management
This involves loading sound samples from disc and retrieving pointers to them from memory at the request of the sound scheduler. Two file formats are currently supported - data files containing 16bit signed mono samples at 22khz, and uncompressed .WAV files. The .WAV loader should cope with almost any uncompressed PCM data, and resamples it to 16bit signed mono at 22khz as the data is loaded. Once loaded, samples are stored in a hashed map, indexed by the sample file name.
Sound management
This is the meat of the code. Each entity in the game has a static list of sound slots; each slot can play one sound. Each frame, every slot in every object is processed by the sound code, and any slots with active sounds have the relevant data copied into the next_sounds list. next_sounds is a list of low-level data containing the sample volume (for left and right channels), playback rate compared to 22khz (in 8.24 format), and a pointer to the sound structure (contained within the father entity).
At the end of each frame sounds_activate() is called, to swap the next_sounds list with active_sounds (and delete active_sounds). The active_sounds list is the one used by the buffer fill code; it is kept seperate from next_sounds to ensure that the buffer fill code always has a full and accurate list of sounds to play.
During entity deletion, it is important that sounds_stop() gets called. This is to ensure that any entries in the active_sounds or next_sounds lists for that object are removed, to ensure the buffer fill code doesn't attempt to play a sound attached to a deleted object (which would involve reading and writing to that object's memory).
The system supports playback of sounds attached to entities, as well as 'free' sounds, although playback requests for free sounds must be made each frame in order to ensure they continue to play.
One of the integral parts of the sound system is the getnewpos() function, which updates the sample position to the current playback position. However, the sample position is also updated by the buffer fill code, at the end of each fill; the basic rule governing the use of getnewpos() is that if more than frametime time has passed since the last playback request, the sample position will be recalculated. This is so that, if a sound goes out of earshot, it will continue playback from the correct position once it enters range again. The sample position is also recalculated during saves, to avoid the need to save the time of the last playback request. The buffer fill code ignores the last playback time entirely, and just goes by the buffer position pointer.
SharedSound interface
There are two functions to control the SharedSound interface: install_handler() and remove_handler(). Apart from installing the sound buffer fill code, these functions also install environment handlers (on ErrorV and via atexit()) to attempt to shutdown the buffer fill code should an unhandled error occur during gameplay.
TODO:
CODE: MED: Work out why removing the error handler was causing the crash, if the atexit handler is able to remove it without problems (presumably from the same event). Maybe register wasn't getting saved on the stack, or wrong processor mode, or it's OK to remove it from the atexit because it gets removed from code that isn't directly inside the error handler that's being removed? Or maybe the atexit handler does happen outside the vector call?
CODE: QUICK: Add UI sounds to savegames?
MUST-CODE: MED: Implement sound effects: Weapons, tyres, crashing, engine, ped sounds, UI sounds, object/world (ambient) sounds, (car) fires, etc.
CODE: MED: Add sound conversion funcs to ddconvert? I.e. WAV to/from raw
MUST-CODE: MED: Fix weapon sounds not always playing after discarding game and starting new ons
MUST-DOCS: MED: Get missing sound effects: car crash, bullet impact, explosion, tyre screech, grunts, spluts, 'talk' sound for mission text, other game sounds (mission complete/fail, etc.)
CODE: QUICK: Simple doppler effect - compare distance object is from camera now to the distance it will be 1 second later (i.e. just add velocities), and work out pitch multiplier from that. Maybe even using real-world shift amount?
MUST-CODE: MED: Check how well sound volume clamping works - maybe introduce automatic volume adjustment (e.g. if it detects clamp/overflow it will calculate a scale factor which will then be applied the next frame?), or use 32bit intermediate buffer to allow clamping to be performed at the end? (eliminating issues where large positive and large negative values combine in one sample) Or try summing up volumes of all palying samples for each frame, and apply a global scale to bring it down if it's too large (e.g. just before making the sample list active). Could also do with on-screen indicator for sound levels.
CODE: MED: Could also do with a way of specifying volume scale to be applied to each sample (under the assumption that all samples will have had their volume increased to span the sample range). Perhaps when the sample is assigned to an object in the cardef/peddef/etc. file (for samples in mission scripts it can just set the volume manually).
???
Profit!