From 9238d8e6561eb7e10e1a6d90efcde129b0c52da5 Mon Sep 17 00:00:00 2001 From: ptarabbia Date: Sun, 12 Dec 2021 19:37:09 -0500 Subject: [PATCH] Add SetAudioStreamCallback method to fill audio streams on the audio thread. --- examples/audio/audio_raw_stream.c | 82 +++++++++++++------------------ src/raudio.c | 34 ++++++++++++- src/raudio.h | 1 + src/raylib.h | 1 + 4 files changed, 69 insertions(+), 49 deletions(-) diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c index c9ec7a9df..0489146cf 100644 --- a/examples/audio/audio_raw_stream.c +++ b/examples/audio/audio_raw_stream.c @@ -20,6 +20,33 @@ #define MAX_SAMPLES 512 #define MAX_SAMPLES_PER_UPDATE 4096 +// Cycles per second (hz) +float frequency = 440.0f; + +// Audio frequency, for smoothing +float audioFrequency = 440.0f; + +// Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency +float oldFrequency = 1.0f; + +// index for audio rendering +float sineIdx = 0.0f; + +void audioCallback(void* buffer, unsigned int nFrames, void* callbackData) +{ + audioFrequency = frequency + (audioFrequency - frequency) * 0.95f; + audioFrequency += 1.0f; + audioFrequency -= 1.0f; + float incr = audioFrequency / 44100.0f; + short* d = (short*)buffer; + for (int i = 0; i < nFrames; i++) + { + d[i] = (short)(32000.0f * sinf(2 * PI * sineIdx)); + sineIdx += incr; + if (sineIdx > 1.0f) sineIdx -= 1.0f; + } +} + int main(void) { // Initialization @@ -33,9 +60,11 @@ int main(void) SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE); - // Init raw audio stream (sample rate: 22050, sample size: 16bit-short, channels: 1-mono) + // Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono) AudioStream stream = LoadAudioStream(44100, 16, 1); + SetAudioStreamCallback(stream, &audioCallback, NULL); + // Buffer for the single cycle waveform we are synthesizing short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES); @@ -47,15 +76,6 @@ int main(void) // Position read in to determine next frequency Vector2 mousePosition = { -100.0f, -100.0f }; - // Cycles per second (hz) - float frequency = 440.0f; - - // Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency - float oldFrequency = 1.0f; - - // Cursor to read and copy the samples of the sine wave buffer - int readCursor = 0; - // Computed size in samples of the sine wave int waveLength = 1; @@ -78,56 +98,23 @@ int main(void) float fp = (float)(mousePosition.y); frequency = 40.0f + (float)(fp); } - // Rewrite the sine wave. - // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc. if (frequency != oldFrequency) { // Compute wavelength. Limit size in both directions. int oldWavelength = waveLength; - waveLength = (int)(22050/frequency); - if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; + waveLength = (int)(22050 / frequency); + if (waveLength > MAX_SAMPLES / 2) waveLength = MAX_SAMPLES / 2; if (waveLength < 1) waveLength = 1; // Write sine wave. - for (int i = 0; i < waveLength*2; i++) + for (int i = 0; i < waveLength * 2; i++) { - data[i] = (short)(sinf(((2*PI*(float)i/waveLength)))*32000); + data[i] = (short)(sinf(((2 * PI * (float)i / waveLength))) * 32000); } - - // Scale read cursor's position to minimize transition artifacts - readCursor = (int)(readCursor * ((float)waveLength / (float)oldWavelength)); oldFrequency = frequency; } - // Refill audio stream if required - if (IsAudioStreamProcessed(stream)) - { - // Synthesize a buffer that is exactly the requested size - int writeCursor = 0; - - while (writeCursor < MAX_SAMPLES_PER_UPDATE) - { - // Start by trying to write the whole chunk at once - int writeLength = MAX_SAMPLES_PER_UPDATE-writeCursor; - - // Limit to the maximum readable size - int readLength = waveLength-readCursor; - - if (writeLength > readLength) writeLength = readLength; - - // Write the slice - memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short)); - - // Update cursors and loop audio - readCursor = (readCursor + writeLength) % waveLength; - - writeCursor += writeLength; - } - - // Copy finished frame to audio stream - UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE); - } //---------------------------------------------------------------------------------- // Draw @@ -147,7 +134,6 @@ int main(void) DrawPixelV(position, RED); } - EndDrawing(); //---------------------------------------------------------------------------------- } diff --git a/src/raudio.c b/src/raudio.c index ec92cf2a3..524d36d03 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -315,6 +315,9 @@ typedef enum { struct rAudioBuffer { ma_data_converter converter; // Audio data converter + void (*audioCallback)(void*, unsigned int); // optional callback for filling in buffer on audio threads + void* audioCallbackData; // optional data passed to callback + float volume; // Audio buffer volume float pitch; // Audio buffer pitch @@ -396,6 +399,7 @@ void PlayAudioBuffer(AudioBuffer *buffer); void StopAudioBuffer(AudioBuffer *buffer); void PauseAudioBuffer(AudioBuffer *buffer); void ResumeAudioBuffer(AudioBuffer *buffer); +void SetAudioBufferCallback(AudioBuffer* buffer, int callback(void*)); void SetAudioBufferVolume(AudioBuffer *buffer, float volume); void SetAudioBufferPitch(AudioBuffer *buffer, float pitch); void TrackAudioBuffer(AudioBuffer *buffer); @@ -553,6 +557,8 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam // Init audio buffer values audioBuffer->volume = 1.0f; audioBuffer->pitch = 1.0f; + audioBuffer->audioCallback = NULL; + audioBuffer->audioCallbackData = NULL; audioBuffer->playing = false; audioBuffer->paused = false; audioBuffer->looping = false; @@ -635,6 +641,16 @@ void ResumeAudioBuffer(AudioBuffer *buffer) if (buffer != NULL) buffer->paused = false; } +// Audio thread callback to request new data +void SetAudioBufferCallback(AudioBuffer* buffer, void callback(void*, unsigned int, void*), void* callbackData) +{ + if (buffer != NULL) + { + buffer->audioCallback = callback; + buffer->audioCallbackData = callbackData; + } +} + // Set volume for an audio buffer void SetAudioBufferVolume(AudioBuffer *buffer, float volume) { @@ -1040,6 +1056,9 @@ void PlaySoundMulti(Sound sound) AUDIO.MultiChannel.pool[index]->volume = sound.stream.buffer->volume; AUDIO.MultiChannel.pool[index]->pitch = sound.stream.buffer->pitch; + AUDIO.MultiChannel.pool[index]->audioCallback = sound.stream.buffer->audioCallback; + AUDIO.MultiChannel.pool[index]->audioCallbackData = sound.stream.buffer->audioCallbackData; + AUDIO.MultiChannel.pool[index]->looping = sound.stream.buffer->looping; AUDIO.MultiChannel.pool[index]->usage = sound.stream.buffer->usage; AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[0] = false; @@ -1987,6 +2006,12 @@ void SetAudioStreamBufferSizeDefault(int size) AUDIO.Buffer.defaultSize = size; } +// Audio thread callback to request new data +void SetAudioStreamCallback(AudioStream stream, void callback(void*, unsigned int, void*), void* callbackData) +{ + SetAudioBufferCallback(stream.buffer, callback, callbackData); +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- @@ -2003,6 +2028,12 @@ static void OnLog(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, // Reads audio data from an AudioBuffer object in internal format. static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount) { + if (audioBuffer->audioCallback) + { + audioBuffer->audioCallback(framesOut, frameCount, audioBuffer->audioCallbackData); + return frameCount; + } + ma_uint32 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames; ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; @@ -2030,7 +2061,8 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, } else { - if (isSubBufferProcessed[currentSubBufferIndex]) break; + if (isSubBufferProcessed[currentSubBufferIndex]) + break; } ma_uint32 totalFramesRemaining = (frameCount - framesRead); diff --git a/src/raudio.h b/src/raudio.h index 7e3c42f4e..1f3488d17 100644 --- a/src/raudio.h +++ b/src/raudio.h @@ -190,6 +190,7 @@ void StopAudioStream(AudioStream stream); // Stop audio st void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams +void SetAudioStreamCallback(AudioStream stream, void callback(void*, unsigned int, void*), void* callbackData); // Audio thread callback to request new data #ifdef __cplusplus } diff --git a/src/raylib.h b/src/raylib.h index 41d23bfad..2051bfe9b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1530,6 +1530,7 @@ RLAPI void StopAudioStream(AudioStream stream); // Stop au RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams +RLAPI void SetAudioStreamCallback(AudioStream stream, void callback(void*, unsigned int, void*), void* callbackData); // Audio thread callback to request new data #if defined(__cplusplus) }