Max Did It
Coding
,Thu
1 Comment
Tagged

Generate Procedural Motor Sounds in Flash

When developing a racing game like Rubberband Racing, you will need decent motor sounds. Driving a silent car just feels weird (Maybe it won’t 50 years from now?).

If I had a loop of a car engine, I could then change the volume and the pitch of the sound dynamically depending on the velocity of the car.

The MP3 Gap Problem

My problem was that usually, you can’t make clean loops and save them as MP3 files. Doing so will always introduce gaps into the audio file that will prevent it from repeating seamlessly.

If you are using Adobe’s Flash Authoring Software to save your MP3s in a SWF file, then you will be able to loop them, as the program uses a trick to avoid the gap introduced by the compression.

However, I am not using Adobe Flash and I am loading the sounds as single files during runtime, so I didn’t have that option.

Instead, I decided create seamlessly looping engine audio by creating it procedurally during runtime.

Generate Sounds With
Actionscript 3

This article on Realtime Audio Processing in Actionscript 3 has helped me in learning the key information I needed.

Basically, whether you want to manipulate existing sounds or create completely new sound data in Flash, you will need the SampleDataEvent.

The SampleDataEvent is continually thrown by a Sound object during playback.

Working with the SampleDataEvent is pretty straightforward. Simply add an event listener to a Sound object and then start playback. The kicker is, the object doesn’t even have to contain any sound data to begin with.

var _sound:Sound = new Sound();
_sound.addEventListener(SampleDataEvent.SAMPLE_DATA, handleSampleData);
_sound.play();

The SampleDataEvent contains the data property, which contains a ByteArray with the audio samples. A sample consists of two floating point numbers, denoting the amplitude of your speakers or headphones at that very moment, for the left and the right channel respectively.

Waveform Samples

An analog soundwave represented by discrete samples. Via Wikimedia Commons

A looping sound in Actionscript 3 has to contain at least 2048 samples. Since sounds in Flash are sampled at 44100 Hz, 2048 samples represent a little more than 46 milliseconds of audio data.

With this knowledge, you can now write the values of sinus curves or similiar stuff into the ByteArray and create sounds.

Creating the Engine Sound

At first, I tried to create a convincing engine sound by layering several sinus curves on top of each other, however the results weren’t very satisfying.

I have downloaded and studied car engine audio files and noticed that they are approximately two sinus curves layered on each other, but with slightly chaotic amplitudes.

At first I tried to introduce that degree of variance by adding a random value on each sample, but that just resulted in white noise. The randomness has to be “smoother”.

Using Perlin Noise to Generate Sounds

I could have implemented a way to smoothly interpolate between random values at this point, but there is a way of easily doing that with Actionscript: The perlinNoise function of the BitmapData class.

Actionscript 3 Perlin Noise

Above, you can see the resulting image created by perlinNoise. You can scale the noise and layer it as you see fit. Different seed values will generate slightly different results.

Also, you can let the function create images that can be repeated seamlessly, which is very useful for our audio loop.

I then performed the following steps:

I create a bitmap with perlin noise which has only one pixel in height.

_bitmapDataNoise = new BitmapData(4096, 1);
_bitmapDataNoise.perlinNoise(	4096, 0, 3, _seed,
				true, true,
				BitmapDataChannel.BLUE);

I create an index Number variable.

In the repeatedly called event handler for the SampleDataEvent, I read a pixel value from the bitmap for the coordinates (index % bitmapDataWidth, 0) and write it into the bytearray.

var _bitmapDataWidth:int = _bitmapDataNoise.width;
for (var i:uint = 0; i < 2048; i++)
{			
	var x:int = int(_index) % bitmapDataWidth;
	var pixel:int = _bitmapDataNoise.getPixel(x, 0) & 0xFF;
 
	var value1:Number = ((Number(pixel) / 128 - 1) * _volume;
 
	e.data.writeFloat(value1); // Left Channel
	e.data.writeFloat(value1); // Right Channel
 
	_index += _speed;
}

I use a bitmask to make sure I get only the blue color value from getPixel. The result is an integer between 0 and 255. I map this value to a floating point number between -1.0 and 1.0.

As the final step in the loop, I increment the index by an arbitrary speed value that I can set. This allows me to play the sample at any given speed.

The Result

This will create an irregular wave that we can use as an engine sound.

Sorry, either Adobe flash is not installed or you do not have it enabled

It’s difficult to prevent the result from sounding at least a little artificial, but the result should be convincing enough to use in a game, depending on the seed you use.

Also, this way it’s very easy to generate as many different sounds as you like without having to download them from a server.

Comments

1 Comment has been posted so far - Leave a Comment

Eason
Eason
,Fri

This is awesome!

Reply

Leave A Comment

Your email address will not be published. Required fields are marked *

Connect with Facebook

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>