Posts
Search
Contact
Cookies
About
RSS

raylib playing multiple sound concurrently

Added 27 Dec 2024, 2:51 a.m. edited 27 Dec 2024, 3:18 a.m.

Quite some time ago I contributed a PR to raylib that enabled multiple sounds to be played concurrently, at the time when you played a particular sound, if you tried to play it again before it had finished, the sound would simply be restarted.  As you can imagine with multiple aliens firing at your player in quick succession this would sound... well... wrong. 

Alas my solution was later deemed too "complex" to maintain (although I'd never been asked, I'd have happily maintained it if needed) and a massive backwards step was taken off loading complexity on to the raylib user to simplify the internals - a sad decision indeed.

I had at the time intended to somehow come up with something that would take the burden of the raylib user but yet still be easier to maintain (what was not actually terribly complex solution in the first place).  Then real life got in the way, and I found myself without the free time to do very much in the way of coding my own projects....

Things having calmed down somewhat, I decided to look at the current state of raylib's sound system, and spotted a way (with some fugly hackery) to simply implement the facility to play multiple (or the same sound) concurrently.

This actual method would be trivial to implement in raylib itself, but I'll leave that to someone else rather than waste my time (not having much at the moment!)

Anyhow below is some example source code for you to experiment with...

Enjoy!

/*******************************************************************************************
*
* raylib [audio] example - Playing sound multiple times
*
* Example originally created with raylib 4.6
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2023 Jeffery Myers (@JeffM2501)
* modified 2024 codifies to make it actually multy sound
*
********************************************************************************************/

#include "raylib.h"
#include "external/miniaudio.h" // just for internal struct types

// Audio buffer struct, so we can hack hotswapping
struct rAudioBuffer {
ma_data_converter converter; // Audio data converter

AudioCallback callback; // Audio buffer callback for buffer filling on audio threads
rAudioProcessor *processor; // Audio processor

float volume; // Audio buffer volume
float pitch; // Audio buffer pitch
float pan; // Audio buffer pan (0.0f to 1.0f)

bool playing; // Audio buffer state: AUDIO_PLAYING
bool paused; // Audio buffer state: AUDIO_PAUSED
bool looping; // Audio buffer looping, default to true for AudioStreams
int usage; // Audio buffer usage mode: STATIC or STREAM

bool isSubBufferProcessed[2]; // SubBuffer processed (virtual double buffer)
unsigned int sizeInFrames; // Total buffer size in frames
unsigned int frameCursorPos; // Frame cursor position
unsigned int framesProcessed; // Total frames processed in this buffer (required for play timing)

unsigned char *data; // Data buffer, on music stream keeps filling

rAudioBuffer *next; // Next audio buffer on the list
rAudioBuffer *prev; // Previous audio buffer on the list
};

#define MAX_SOUNDS 32 // 32 "channels" which can be any of the loaded sounds
Sound soundArray[MAX_SOUNDS] = { 0 };
Sound snd[4] = { 0 }; // for the actual sound data
bool flash = false;

// hack to hot swap alias sounds
// in an ideal world the end user shouldn't have to think about this complexity
// and sounds should be able to be triggered multiple times concurrently...
void play(int idx)
{
for (int i = 0; i < MAX_SOUNDS; i++)
{
if (!IsSoundPlaying(soundArray[i]))
{
// hot swap the alias
soundArray[i].stream.buffer->sizeInFrames = snd[idx].stream.buffer->sizeInFrames;
soundArray[i].stream.buffer->volume = snd[idx].stream.buffer->volume;
soundArray[i].stream.buffer->data = snd[idx].stream.buffer->data;
soundArray[i].frameCount = snd[idx].frameCount;
PlaySound(soundArray[i]);
return;
}
}
flash = true; // warn if not enough channels...
}

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;

InitWindow(screenWidth, screenHeight, "raylib [audio] example - playing sound multiple times");

InitAudioDevice(); // Initialize audio device

// load the sound list
snd[0] = LoadSound("resources/one.wav");
snd[1] = LoadSound("resources/two.wav");
snd[2] = LoadSound("resources/three.wav");
snd[3] = LoadSound("resources/four.wav");

for (int i = 0; i < MAX_SOUNDS; i++)
{
soundArray[i] = LoadSoundAlias(snd[0]); // set up all the "channels" with an initial alias
}

SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------

// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update

if (IsKeyPressed(KEY_ONE)) play(0);
if (IsKeyPressed(KEY_TWO)) play(1);
if (IsKeyPressed(KEY_THREE)) play(2);
if (IsKeyPressed(KEY_FOUR)) play(3);

//----------------------------------------------------------------------------------

// Draw
//----------------------------------------------------------------------------------
BeginDrawing();

if (flash) {
ClearBackground(RED);
flash = false;
} else {
ClearBackground(RAYWHITE);
}

DrawText("Press 1-4 to PLAY a WAV sound!", 200, 180, 20, LIGHTGRAY);

EndDrawing();
//----------------------------------------------------------------------------------
}

// De-Initialization
//--------------------------------------------------------------------------------------
for (int i = 0; i < MAX_SOUNDS; i++)
UnloadSoundAlias(soundArray[i]); // Unload sound aliases
UnloadSound(snd[0]); // Unload source sound data
UnloadSound(snd[1]); // Unload source sound data
UnloadSound(snd[2]); // Unload source sound data
UnloadSound(snd[3]); // Unload source sound data

CloseAudioDevice(); // Close audio device

CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

return 0;
}