Working with dynamically generated audio
Instead of loading or streaming an existing sound, you can generate audio data
dynamically. You can generate audio data when you assign an event listener for
the sampleData
event of a Sound object. (The sampleData
event is defined in
the SampleDataEvent class in the
openfl.events package.) In
this environment, the Sound object doesn't load sound data from a file. Instead,
it acts as a socket for sound data that is being streamed to it through the use
of the function you assign to this event.
When you add a sampleData
event listener to a Sound object, the object
periodically requests data to add to the sound buffer. This buffer contains data
for the Sound object to play. When you call the play()
method of the Sound
object, it dispatches the sampleData
event when requesting new sound data.
(This is true only when the Sound object has not loaded mp3, ogg, or wav data
from a file.)
The SampleDataEvent object includes a data
property. In your event listener,
you write ByteArray objects to this data
object. The byte arrays you write to
this object add to buffered sound data that the Sound object plays. The byte
array in the buffer is a stream of floating-point values from -1 to 1. Each
floating-point value represents the amplitude of one channel (left or right) of
a sound sample. Sound is sampled at 44,100 samples per second. Each sample
contains a left and right channel, interleaved as floating-point data in the
byte array.
In your handler function, you use the ByteArray.writeFloat()
method to write
to the data
property of the sampleData
event. For example, the following
code generates a sine wave:
import openfl.display.Sprite;
import openfl.events.SampleDataEvent;
import openfl.media.Sound;
class SineWaveSample extends Sprite {
public function new() {
super();
var mySound:Sound = new Sound();
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, sineWaveGenerator);
mySound.play();
}
function sineWaveGenerator(event:SampleDataEvent):Void
{
for (i in 0...8192)
{
var n = Math.sin((i + event.position) / Math.PI / 4);
event.data.writeFloat(n);
event.data.writeFloat(n);
}
}
}
When you call Sound.play()
, the application starts calling your event handler,
requesting sound sample data. The application continues to send events as the
sound plays back until you stop providing data, or until you call
SoundChannel.stop()
.
The latency of the event varies from platform to platform, and could change in future versions of OpenFL. Do not depend on a specific latency; calculate it instead. To calculate the latency, use the following formula:
(SampleDataEvent.position / 44.1) - SoundChannelObject.position
Provide from 2048 through 8192 samples to the data
property of the
SampleDataEvent object (for each call to the event listener). For best
performance, provide as many samples as possible (up to 8192). The fewer samples
you provide, the more likely it is that clicks and pops will occur during
playback. This behavior can differ on various platforms and can occur in various
situations—for example, when resizing the browser. Code that works on one
platform when you provide only 2048 sample might not work as well when run on a
different platform. If you require the lowest latency possible, consider making
the amount of data user-selectable.
If you provide fewer than 2048 samples (per call to the sampleData
event
listener), the application stops after playing the remaining samples. The
SoundChannel object then dispatches a soundComplete
event.