diff options
| -rw-r--r-- | src/audio.c | 135 | ||||
| -rw-r--r-- | src/audio.h | 7 | ||||
| -rw-r--r-- | src/raylib.h | 7 |
3 files changed, 123 insertions, 26 deletions
diff --git a/src/audio.c b/src/audio.c index 39d1ee8b..ff4f2858 100644 --- a/src/audio.c +++ b/src/audio.c @@ -37,6 +37,7 @@ #include "AL/al.h" // OpenAL basic header #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) +#include "AL/alext.h" // extensions for other format types #include <stdlib.h> // Declares malloc() and free() for memory management #include <string.h> // Required for strcmp() @@ -93,12 +94,11 @@ typedef struct Music { // Audio Context, used to create custom audio streams that are not bound to a sound file. There can be // no more than 4 concurrent audio contexts in use. This is due to each active context being tied to -// a dedicated mix channel. +// a dedicated mix channel. All audio is 32bit floating point in stereo. typedef struct AudioContext_t { unsigned short sampleRate; // default is 48000 - unsigned char bitsPerSample; // 16 is default + unsigned char channels; // 1=mono,2=stereo unsigned char mixChannel; // 0-3 or mixA-mixD, each mix channel can receive up to one dedicated audio stream - unsigned char channels; // 1=mono, 2=stereo ALenum alFormat; // openAL format specifier ALuint alSource; // openAL source ALuint alBuffer[2]; // openAL sample buffer @@ -126,6 +126,10 @@ static void UnloadWave(Wave wave); // Unload wave data static bool BufferMusicStream(ALuint buffer); // Fill music buffers with data static void EmptyMusicStream(void); // Empty music buffers +static void FillAlBufferWithSilence(AudioContext_t *ac, ALuint buffer);// fill buffer with zeros +static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // pass two arrays of the same legnth in +static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // pass two arrays of same length in + #if defined(AUDIO_STANDALONE) const char *GetExtension(const char *fileName); // Get the extension for a filename void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message (INFO, ERROR, WARNING) @@ -197,8 +201,9 @@ bool IsAudioDeviceReady(void) // Audio contexts are for outputing custom audio waveforms, This will shut down any other sound sources currently playing // The mixChannel is what mix channel you want to operate on, 0-3 are the ones available. Each mix channel can only be used one at a time. -// exmple usage is InitAudioContext(48000, 16, 0, 2); // stereo, mixchannel 1, 16bit, 48khz -AudioContext InitAudioContext(unsigned short sampleRate, unsigned char bitsPerSample, unsigned char mixChannel, unsigned char channels) +// exmple usage is InitAudioContext(48000, 0, 2); // mixchannel 1, 48khz, stereo +// all samples are floating point by default +AudioContext InitAudioContext(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels) { if(mixChannel > MAX_AUDIO_CONTEXTS) return NULL; if(!IsAudioDeviceReady()) InitAudioDevice(); @@ -207,22 +212,15 @@ AudioContext InitAudioContext(unsigned short sampleRate, unsigned char bitsPerSa if(!mixChannelsActive_g[mixChannel]){ AudioContext_t *ac = malloc(sizeof(AudioContext_t)); ac->sampleRate = sampleRate; - ac->bitsPerSample = bitsPerSample; - ac->mixChannel = mixChannel; ac->channels = channels; + ac->mixChannel = mixChannel; mixChannelsActive_g[mixChannel] = ac; // setup openAL format - if (channels == 1) - { - if (bitsPerSample == 8 ) ac->alFormat = AL_FORMAT_MONO8; - else if (bitsPerSample == 16) ac->alFormat = AL_FORMAT_MONO16; - } - else if (channels == 2) - { - if (bitsPerSample == 8 ) ac->alFormat = AL_FORMAT_STEREO8; - else if (bitsPerSample == 16) ac->alFormat = AL_FORMAT_STEREO16; - } + if(channels == 1) + ac->alFormat = AL_FORMAT_MONO_FLOAT32; + else + ac->alFormat = AL_FORMAT_STEREO_FLOAT32; // Create an audio source alGenSources(1, &ac->alSource); @@ -234,6 +232,13 @@ AudioContext InitAudioContext(unsigned short sampleRate, unsigned char bitsPerSa // Create Buffer alGenBuffers(2, ac->alBuffer); + //fill buffers + FillAlBufferWithSilence(ac, ac->alBuffer[0]); + FillAlBufferWithSilence(ac, ac->alBuffer[1]); + alSourceQueueBuffers(ac->alSource, 2, ac->alBuffer); + alSourcei(ac->alSource, AL_LOOPING, AL_FALSE); // this could cause errors + alSourcePlay(ac->alSource); + return ac; } @@ -245,6 +250,19 @@ void CloseAudioContext(AudioContext ctx) { AudioContext_t *context = (AudioContext_t*)ctx; if(context){ + alSourceStop(context->alSource); + + //flush out all queued buffers + ALuint buffer = 0; + int queued = 0; + alGetSourcei(context->alSource, AL_BUFFERS_QUEUED, &queued); + while (queued > 0) + { + alSourceUnqueueBuffers(context->alSource, 1, &buffer); + queued--; + } + + //delete source and buffers alDeleteSources(1, &context->alSource); alDeleteBuffers(2, context->alBuffer); mixChannelsActive_g[context->mixChannel] = NULL; @@ -254,12 +272,89 @@ void CloseAudioContext(AudioContext ctx) } // Pushes more audio data into context mix channel, if none are ever pushed then zeros are fed in -void UpdateAudioContext(AudioContext ctx, void *data, unsigned short *dataLength) +// Call "UpdateAudioContext(ctx, NULL, 0)" every game tick if you want to pause the audio +// Returns number of floats that where processed +unsigned short UpdateAudioContext(AudioContext ctx, float *data, unsigned short dataLength) { + unsigned short numberProcessed = 0; + unsigned short numberRemaining = dataLength; AudioContext_t *context = (AudioContext_t*)ctx; - if(!musicEnabled && context && mixChannelsActive_g[context->mixChannel] == context) + + if (context && mixChannelsActive_g[context->mixChannel] == context) { - ; + ALint processed = 0; + ALuint buffer = 0; + alGetSourcei(context->alSource, AL_BUFFERS_PROCESSED, &processed); // Get the number of already processed buffers (if any) + + if(!processed) return 0;//nothing to process, queue is still full + + if (!data || !dataLength)// play silence + while (processed > 0) + { + alSourceUnqueueBuffers(context->alSource, 1, &buffer); + FillAlBufferWithSilence(context, buffer); + alSourceQueueBuffers(context->alSource, 1, &buffer); + processed--; + numberProcessed+=MUSIC_BUFFER_SIZE; + } + if(numberRemaining)// buffer data stream in increments of MUSIC_BUFFER_SIZE + while (processed > 0) + { + alSourceUnqueueBuffers(context->alSource, 1, &buffer); + if(numberRemaining >= MUSIC_BUFFER_SIZE) + { + float pcm[MUSIC_BUFFER_SIZE]; + memcpy(pcm, &data[numberProcessed], MUSIC_BUFFER_SIZE); + alBufferData(buffer, context->alFormat, pcm, MUSIC_BUFFER_SIZE*sizeof(float), context->sampleRate); + alSourceQueueBuffers(context->alSource, 1, &buffer); + numberProcessed+=MUSIC_BUFFER_SIZE; + numberRemaining-=MUSIC_BUFFER_SIZE; + } + else // less than MUSIC_BUFFER_SIZE number of samples left to buffer, the remaining data is padded with zeros + { + float pcm[MUSIC_BUFFER_SIZE] = {0.f}; // pad with zeros + } + + processed--; + } + } + return numberProcessed; +} + +// fill buffer with zeros +static void FillAlBufferWithSilence(AudioContext_t *context, ALuint buffer) +{ + float pcm[MUSIC_BUFFER_SIZE] = {0.f}; + alBufferData(buffer, context->alFormat, pcm, MUSIC_BUFFER_SIZE*sizeof(float), context->sampleRate); +} + +// example usage: +// short sh[3] = {1,2,3};float fl[3]; +// ResampleShortToFloat(sh,fl,3); +static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len) +{ + int x; + for(x=0;x<len;x++) + { + if(shorts[x] < 0) + floats[x] = (float)shorts[x] / 32766.f; + else + floats[x] = (float)shorts[x] / 32767.f; + } +} + +// example usage: +// char ch[3] = {1,2,3};float fl[3]; +// ResampleByteToFloat(ch,fl,3); +static void ResampleByteToFloat(char *chars, float *floats, unsigned short len) +{ + int x; + for(x=0;x<len;x++) + { + if(chars[x] < 0) + floats[x] = (float)chars[x] / 127.f; + else + floats[x] = (float)chars[x] / 128.f; } } diff --git a/src/audio.h b/src/audio.h index 9c681044..2b36b3f0 100644 --- a/src/audio.h +++ b/src/audio.h @@ -84,10 +84,11 @@ bool IsAudioDeviceReady(void); // True if call // Audio contexts are for outputing custom audio waveforms, This will shut down any other sound sources currently playing // The mixChannel is what mix channel you want to operate on, 0-3 are the ones available. Each mix channel can only be used one at a time. -// exmple usage is InitAudioContext(48000, 16, 0, 2); // stereo, mixchannel 1, 16bit, 48khz -AudioContext InitAudioContext(unsigned short sampleRate, unsigned char bitsPerSample, unsigned char mixChannel, unsigned char channels); +// exmple usage is InitAudioContext(48000, 0, 2); // mixchannel 1, 48khz, stereo +// all samples are floating point by default +AudioContext InitAudioContext(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels); void CloseAudioContext(AudioContext ctx); // Frees audio context -void UpdateAudioContext(AudioContext ctx, void *data, unsigned short *dataLength); // Pushes more audio data into context mix channel, if none are ever pushed then zeros are fed in +unsigned short UpdateAudioContext(AudioContext ctx, float *data, unsigned short dataLength); // Pushes more audio data into context mix channel, if NULL is passed to data then zeros are played Sound LoadSound(char *fileName); // Load sound to memory Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data diff --git a/src/raylib.h b/src/raylib.h index 337b9813..5c727b61 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -870,10 +870,11 @@ bool IsAudioDeviceReady(void); // True if call // Audio contexts are for outputing custom audio waveforms, This will shut down any other sound sources currently playing // The mixChannel is what mix channel you want to operate on, 0-3 are the ones available. Each mix channel can only be used one at a time. -// exmple usage is InitAudioContext(48000, 16, 0, 2); // stereo, mixchannel 1, 16bit, 48khz -AudioContext InitAudioContext(unsigned short sampleRate, unsigned char bitsPerSample, unsigned char mixChannel, unsigned char channels); +// exmple usage is InitAudioContext(48000, 0, 2); // mixchannel 1, 48khz, stereo +// all samples are floating point by default +AudioContext InitAudioContext(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels); void CloseAudioContext(AudioContext ctx); // Frees audio context -void UpdateAudioContext(AudioContext ctx, void *data, unsigned short *dataLength); // Pushes more audio data into context mix channel, if none are ever pushed then zeros are fed in +unsigned short UpdateAudioContext(AudioContext ctx, float *data, unsigned short dataLength); // Pushes more audio data into context mix channel, if NULL is passed to data then zeros are played Sound LoadSound(char *fileName); // Load sound to memory Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data |
