Add SetAudioStreamCallback method to fill audio streams on the audio thread.

This commit is contained in:
ptarabbia 2021-12-12 19:37:09 -05:00
parent 20f4d8c147
commit 9238d8e656
4 changed files with 69 additions and 49 deletions

View File

@ -20,6 +20,33 @@
#define MAX_SAMPLES 512 #define MAX_SAMPLES 512
#define MAX_SAMPLES_PER_UPDATE 4096 #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) int main(void)
{ {
// Initialization // Initialization
@ -33,9 +60,11 @@ int main(void)
SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE); 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); AudioStream stream = LoadAudioStream(44100, 16, 1);
SetAudioStreamCallback(stream, &audioCallback, NULL);
// Buffer for the single cycle waveform we are synthesizing // Buffer for the single cycle waveform we are synthesizing
short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES); short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES);
@ -47,15 +76,6 @@ int main(void)
// Position read in to determine next frequency // Position read in to determine next frequency
Vector2 mousePosition = { -100.0f, -100.0f }; 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 // Computed size in samples of the sine wave
int waveLength = 1; int waveLength = 1;
@ -78,56 +98,23 @@ int main(void)
float fp = (float)(mousePosition.y); float fp = (float)(mousePosition.y);
frequency = 40.0f + (float)(fp); frequency = 40.0f + (float)(fp);
} }
// Rewrite the sine wave. // Rewrite the sine wave.
// Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc.
if (frequency != oldFrequency) if (frequency != oldFrequency)
{ {
// Compute wavelength. Limit size in both directions. // Compute wavelength. Limit size in both directions.
int oldWavelength = waveLength; int oldWavelength = waveLength;
waveLength = (int)(22050/frequency); waveLength = (int)(22050 / frequency);
if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2; if (waveLength > MAX_SAMPLES / 2) waveLength = MAX_SAMPLES / 2;
if (waveLength < 1) waveLength = 1; if (waveLength < 1) waveLength = 1;
// Write sine wave. // 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; 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 // Draw
@ -147,7 +134,6 @@ int main(void)
DrawPixelV(position, RED); DrawPixelV(position, RED);
} }
EndDrawing(); EndDrawing();
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
} }

View File

@ -315,6 +315,9 @@ typedef enum {
struct rAudioBuffer { struct rAudioBuffer {
ma_data_converter converter; // Audio data converter 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 volume; // Audio buffer volume
float pitch; // Audio buffer pitch float pitch; // Audio buffer pitch
@ -396,6 +399,7 @@ void PlayAudioBuffer(AudioBuffer *buffer);
void StopAudioBuffer(AudioBuffer *buffer); void StopAudioBuffer(AudioBuffer *buffer);
void PauseAudioBuffer(AudioBuffer *buffer); void PauseAudioBuffer(AudioBuffer *buffer);
void ResumeAudioBuffer(AudioBuffer *buffer); void ResumeAudioBuffer(AudioBuffer *buffer);
void SetAudioBufferCallback(AudioBuffer* buffer, int callback(void*));
void SetAudioBufferVolume(AudioBuffer *buffer, float volume); void SetAudioBufferVolume(AudioBuffer *buffer, float volume);
void SetAudioBufferPitch(AudioBuffer *buffer, float pitch); void SetAudioBufferPitch(AudioBuffer *buffer, float pitch);
void TrackAudioBuffer(AudioBuffer *buffer); void TrackAudioBuffer(AudioBuffer *buffer);
@ -553,6 +557,8 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
// Init audio buffer values // Init audio buffer values
audioBuffer->volume = 1.0f; audioBuffer->volume = 1.0f;
audioBuffer->pitch = 1.0f; audioBuffer->pitch = 1.0f;
audioBuffer->audioCallback = NULL;
audioBuffer->audioCallbackData = NULL;
audioBuffer->playing = false; audioBuffer->playing = false;
audioBuffer->paused = false; audioBuffer->paused = false;
audioBuffer->looping = false; audioBuffer->looping = false;
@ -635,6 +641,16 @@ void ResumeAudioBuffer(AudioBuffer *buffer)
if (buffer != NULL) buffer->paused = false; 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 // Set volume for an audio buffer
void SetAudioBufferVolume(AudioBuffer *buffer, float volume) 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]->volume = sound.stream.buffer->volume;
AUDIO.MultiChannel.pool[index]->pitch = sound.stream.buffer->pitch; 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]->looping = sound.stream.buffer->looping;
AUDIO.MultiChannel.pool[index]->usage = sound.stream.buffer->usage; AUDIO.MultiChannel.pool[index]->usage = sound.stream.buffer->usage;
AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[0] = false; AUDIO.MultiChannel.pool[index]->isSubBufferProcessed[0] = false;
@ -1987,6 +2006,12 @@ void SetAudioStreamBufferSizeDefault(int size)
AUDIO.Buffer.defaultSize = 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 // 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. // Reads audio data from an AudioBuffer object in internal format.
static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer, void *framesOut, ma_uint32 frameCount) 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 subBufferSizeInFrames = (audioBuffer->sizeInFrames > 1)? audioBuffer->sizeInFrames/2 : audioBuffer->sizeInFrames;
ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; ma_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
@ -2030,7 +2061,8 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
} }
else else
{ {
if (isSubBufferProcessed[currentSubBufferIndex]) break; if (isSubBufferProcessed[currentSubBufferIndex])
break;
} }
ma_uint32 totalFramesRemaining = (frameCount - framesRead); ma_uint32 totalFramesRemaining = (frameCount - framesRead);

View File

@ -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 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 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 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 #ifdef __cplusplus
} }

View File

@ -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 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 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 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) #if defined(__cplusplus)
} }