Playing Sounds

DreamBox features 32 configurable channels of PCM sound for playing sound effects. Audio samples are mono and can be 8-bit signed PCM, 16-bit signed PCM, or IMA ADPCM compressed - in addition, there is a 2MB total limit for audio samples in memory.
The API for uploading audio samples and configuring voice parameters is in the audio module.
However, you don't really need to worry about any of this, because there's also an included sound driver built on top of the low level voice API in the sounddriver module.

Using the sound driver

The very first thing we're going to do is use the lazy_static crate to let us keep track of our sound driver:

lazy_static! {
    static ref SOUND_DRIVER: RwLock<SoundDriver> = RwLock::new(SoundDriver::new(32));
}

Next, in main(), we make sure the sound driver is initialized:

lazy_static::initialize(&SOUND_DRIVER);

And next, in tick() we grab a write lock and update the sound driver:

let mut sound_driver = SOUND_DRIVER.write().unwrap();
sound_driver.update();

This allows the sound driver to initialize itself and update sound emitters each frame. With that out of the way, we can now load and play sounds.

Loading WAV files from disc

Before playing sound effects, we need to load sound samples into memory. First, let's create a content/ folder to hold all of our content files.
Your project structure should look like this:

  • Cargo.toml
  • src
    • lib.rs
  • content
    • content files go in here!

dbsdk-cli will automatically copy this content folder into the output ISO when we build, so now we're ready to load WAV files from disk!

lazy_static! {
    static ref SOUND_EFFECT: Arc<AudioSample> = {
        let mut wav_file = FileStream::open("/cd/content/mySound.wav", FileMode::Read)
            .expect("Failed to open wav file");
        let my_sound = sounddriver::load_wav(&mut wav_file).expect("Failed to load wav file");
        Arc::new(my_sound)
    };
}

Remember, sound samples must be single-channel and either 8-bit, 16-bit, or IMA ADPCM.
Other formats will return an error result.

Playing simple sound effects

Now that we have a sound sample loaded, we can play sound effects. You can call play to start playing a sound and get a handle to it.
This handle is a Weak<RwLock<SoundEmitter>> - note that when the sound stops playing, calling upgrade() on the weak reference will return None.

// play sound effect with priority 128, looping disabled, reverb disabled, 1.0 volume, 1.0 pitch, and 0.0 pan
let sound_emitter = sound_driver.play(128, &SOUND_EFFECT, false, false, 1.0, 1.0, 0.0);

You can also call play_3d, which calculates 3D attenuation and panning for you relative to the last set listener position and orientation:

// play sound effect with priority 128, looping disabled, reverb disabled, 1.0 volume, 1.0 pitch, 0.0 pan,
// linear attenuation curve, min attenuation distance = 1.0, max attenuation distance = 10.0
let sound_emitter = sound_driver.play_3d(128, &SOUND_EFFECT, false, false, 1.0, 1.0,
    Vector3::zero(), AttenuationType::Linear, 1.0, 10.0, 1.0);

Note that if you just want to play a one-shot sound, you can just ignore the returned result. Be careful - if the sound is set to looping, and you consume the result, you will not be able to stop it.
Also note that priority is used to keep track of which voices should be playing - remember, DreamBox only has 32 channels, and you can limit this further when you construct the sound driver, so priority is used to manage which sounds actually get access to those channels.
0 represents the highest priority, and 255 the lowest priority.

Playing music

This is all fine for sound effects, but what about music? Sounds are mono only, and even though ADPCM is supported you still only get 2MB of sample data.
That's not a whole lot - far too small to cram music into at the same time as sound effects (although it IS possible to work around this with custom streaming solutions).
To work around this limitation, DreamBox supports MIDI playback. To play MIDI files, you first initialize the MIDI engine with an SF2 soundfont:

// sf2_data is slice of bytes representing the contents of the SF2 file
audio::init_synth(sf2_data).expect("Failed to initialize MIDI synth");

And next, begin playing a MIDI file using the soundfont:

// begin play a looping MIDI
// midi_data is slice of bytes representing the contents of the MIDI file
audio::play_midi(midi_data, true).expect("Failed to start MIDI playback");

DreamBox supports looping MIDI playback, as well as defining arbitrary loop points within MIDI files (loop points in a MIDI file are marked with a CC#111 control change on any channel - upon reaching the end of the file, playback will loop back to that point if looping is enabled)

Reverb

DreamBox also has reverb built-in. Each of the 32 voices can be optionally routed through the reverb unit. The reverb unit can be customized as follows:

// room size = 0.5, damping = 0.5, stereo width = 1.0, wet gain = 0.2, dry gain = 0.8
audio::set_reverb_params(0.5, 0.5, 1.0, 0.2, 0.8);

MIDI playback can also be routed through the reverb unit:

audio::set_midi_reverb(true);