It's a bit late, but Leilani and a maca were able to settle their differences and enjoy some birthday cake together. ^_^
Audio System - Further ImprovementsI've begun the daunting task of doing a full pass over the game's sound effects. I spent a few hours playing through what currently exists of the game, making notes of all the situations where sound effects were missing or obviously placeholder. I also noted down some new features that it'd be nice for the sound system to have.
I previously wrote about the audio systems
here (basics),
here (music, reverb) and
here (expanded features).
Reverb ZonesPreviously, the game supported reverb but only for entire areas of a level. I added the ability to instead specify individual zones where the reverb should occur.
In this video, on the left side there's no reverb. As Leilani goes into the cave, the echoey reverb effect kicks in.
The FMOD API made this easy; it has a ready made system for setting up reverb properties and then applying them to either whole groups of channels (which is what I do for the level-wide reverb) or to individual channels (which is used here). I didn't showcase this in the video, but the reverb is on a per-sound basis - so even if Leilani is in the reverb zone, if a sound occurred outside the zone, the reverb wouldn't be applied to it.
This feature is a bit overkill! There's a specific level with a mix of outside and cave areas where I make good use of it, though. As I make more levels I'll see if there are other appropriate places to use reverb.
New sounds: Rainbow drop sequenceThe
rainbow drop sequence was previously entirely silent! I've done a pass over it adding some sparkly, bubbly effects.
Audio design isn't my strong suit - I take a long time to get anything done, and I'm not always able to even describe to myself what I want a sound to be, let alone have the skills / tools / knowledge to make it. As such all sounds in the game are considered possibly-placeholder with the idea that I may find a sound designer to work with at some point in the future.
However for now, I have a few tools that I use. I source some sounds (such as the sparkly sound used here) from
Freesound. I'm maintaining a list of all the sounds that I grab from there, and where I used them in the game, so I can double check licenses and attribution in the future. If I edit sounds I usually use
GoldWave or occasionally
Audacity.
I also synthesise some sounds. The bubbly sounds in the above video were made in
LabChirp. I also use the old favourite
BFXR.
I don't want the effects in the game to be overly 8-bit-sounding, so those are the most likely candidates to be replaced further down the line.
Data-driven Sound ControlsWhile working on the rainbow sequence above, I added another new feature to the audio system.
I'd like to focus on the RainbowAppear sound. It's a looping sound. When the rainbow begins to appear, I play the sound. When the rainbow finishes appearing, I stop it.
<Sound name="RainbowAppear"
filename="Sparkle.wav"
loop="1"
sustain="1.0"/>
In the XML data for the sound, I add a one second sustain which causes the sound to fade off nicely even after the rainbow's code has stopped the sound. However, I don't really have any more control over the sound. I could alter the pan, pitch or volume, but only for the entire sound.
The effect I want is for the sound to increase in pitch over the duration of the rainbow's animation, and also pan from left to right as it goes across the screen. It would sound really flat to play a monotone looping sound for the whole animation!
One possible solution would be to author a unique sound for this situation, tailored specifically for the rainbow's animation. I don't use this solution for various reasons. It doesn't suit my limited audio editing skills - it would take too long to tweak the sound to behave how I want. It's wasteful as the sound file can't be reused for other purposes, as well as the sound itself being longer than a short looping sound is. It's also not flexible; if I decided to change the length of the rainbow anim, the sound would need to be remade.
The previous solution I would use was to hard-code all this behaviour. The code for the rainbow, which starts and stops the sound, would also calculate the appropriate pan and pitch changes to make and apply them to the animation. Some pseudo-code to demonstrate this:
// Progress the rainbow appear anim from 0-1
anim += deltaTime;
anim = Clamp(anim, 0, 1);
// Apply visual effects
...
// Alter sounds
float pitch = Lerp(0.5f, 1.5f, anim);
float pan = Lerp(-1.0f, 1.0f, anim);
SoundSystem.SetChannelPitch(rainbowSound, pitch);
SoundSystem.SetChannelPan(rainbowSound, pan);
This works, but has slow iteration times. If I need to tweak one of the values, I have to stop the game, compile the code, and relaunch the game.
I've now made this data-driven. The sound XML data can contain a new feature called a Control.
<Control name="RainbowAppearProgress">
<Pitch>
<Keyframe input="0.0" output="0.5"/>
<Keyframe input="1.0" output="1.5"/>
</Pitch>
<Pan>
<Keyframe input="0.0" output="-1.0"/>
<Keyframe input="1.0" output="1.0"/>
</Pan>
</Control>
The control maps a single float value to multiple sound changes. This simplifies the code, and moves all the tweakable numbers into data. If I want to change the numbers, I can just edit the XML and hit a button to reload the level, without any compilation needed.
// Progress the rainbow appear anim from 0-1
anim += deltaTime;
anim = Clamp(anim, 0, 1);
// Apply visual effects
...
// Alter sounds
SoundSystem.ApplyControlToChannel(rainbowSound, "RainbowAppearProgress", anim);
As I continue my sound effect improvement throughout the game, I'll be changing old cases of hard-coded sound alterations to use this new system.
SemitonesOne final thing! You'll notice in the rainbow drop video that the pitch of the sounds increases throughout the sequence. It could be done by specifying pitch multiplication values directly, like so:
<Sound name="RainbowDropCollect1"
filename="RainbowDropCollect.wav"
pitch="1.0"/>
<Sound name="RainbowDropCollect2"
filename="RainbowDropCollect.wav"
pitch="1.2" />
<Sound name="RainbowDropCollect3"
filename="RainbowDropCollect.wav"
pitch="1.4" />
<!-- etc -->
However this can end up sounding really wonky. Numbers that look nice on paper - increasing the pitch by 0.2 seems like it should sound good - don't tend to sound good in practise.
I have a little helper code in place to calculate these pitch values for me based on the idea of semitones. In music, the notes (ABCDEFG) form an octave. In a single octave there are 12 semitones (in piano terms this includes the white and black keys in the octave). The difference between octaves - for example between note A on one octave, and note A on the next octave up - is that the pitch is doubled!
So to change the pitch of a sound by one octave, or 12 semitones, we double it. It's a little more tricky to change the pitch by individual semitones. For this we need a calculation that I pulled off the internet.
pitchMultiplier = 1.059463 ^ semitoneChange
1.059463^0 = 1.0 (same pitch, no change)
1.059463^1 = 1.059463 (1 semitone increase)
1.059463^6 = 1.414 (6 semitone increase. The result here is also the square root of 2.)
1.059463^12 = 2.0 (doubled the pitch, gone up one octave!)
In the XML I specify the pitch change for each collection sound in semitones:
<Sound name="RainbowDropCollect1"
filename="RainbowDropCollect.wav"
semitoneChange="0.0"/>
<Sound name="RainbowDropCollect2"
filename="RainbowDropCollect.wav"
semitoneChange="1.0" />
<Sound name="RainbowDropCollect3"
filename="RainbowDropCollect.wav"
semitoneChange="2.0" />
<!-- etc -->
And, the result of this is a more pleasant-sounding increase in pitch for each sound. I recommend making use of this trick where possible!