From 75433a670e0880c4d23d5178b073836de3628547 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 12 Nov 2017 14:17:05 +1000 Subject: Initial work on adding support for mini_al. --- src/audio.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 402 insertions(+), 12 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 06af8ed4..a5f117b5 100644 --- a/src/audio.c +++ b/src/audio.c @@ -72,6 +72,8 @@ #define SUPPORT_FILEFORMAT_MOD //------------------------------------------------- +#define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. + #if defined(AUDIO_STANDALONE) #include "audio.h" #include // Required for: va_list, va_start(), vfprintf(), va_end() @@ -80,17 +82,21 @@ #include "utils.h" // Required for: fopen() Android mapping #endif -#if defined(__APPLE__) - #include "OpenAL/al.h" // OpenAL basic header - #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) -#else - #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" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS -#endif - -// OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples -// OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) +//#if USE_MINI_AL + #include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. +//#else + #if defined(__APPLE__) + #include "OpenAL/al.h" // OpenAL basic header + #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) + #else + #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" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS + #endif + + // OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples + // OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) +//#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strncmp() @@ -200,10 +206,195 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Device initialization and Closing //---------------------------------------------------------------------------------- +#if USE_MINI_AL +#define DEVICE_FORMAT mal_format_f32 +#define DEVICE_CHANNELS 2 +#define DEVICE_SAMPLE_RATE 44100 + +typedef struct SoundInternal SoundInternal; +struct SoundInternal +{ + mal_format format; + mal_uint32 channels; + mal_uint32 sampleRate; + mal_uint32 frameCount; + mal_uint32 frameCursorPos; // Keeps track of the next frame to read when mixing + float volume; + float pitch; + bool playing; + bool paused; + bool looping; + SoundInternal* next; + SoundInternal* prev; + mal_uint8 data[1]; // Raw audio data. +}; + +static mal_context context; +static mal_device device; +static mal_bool32 isAudioInitialized = MAL_FALSE; +static float masterVolume = 1; +static mal_mutex soundLock; +static SoundInternal* firstSound; // Sounds are tracked in a linked list. +static SoundInternal* lastSound; + +static void AppendSound(SoundInternal* internalSound) +{ + mal_mutex_lock(&context, &soundLock); + { + if (firstSound == NULL) { + firstSound = internalSound; + } else { + lastSound->next = internalSound; + internalSound->prev = lastSound; + } + + lastSound = internalSound; + } + mal_mutex_unlock(&context, &soundLock); +} + +static void RemoveSound(SoundInternal* internalSound) +{ + mal_mutex_lock(&context, &soundLock); + { + if (internalSound->prev == NULL) { + firstSound = internalSound->next; + } else { + internalSound->prev->next = internalSound->next; + } + + if (internalSound->next == NULL) { + lastSound = internalSound->prev; + } else { + internalSound->next->prev = internalSound->prev; + } + } + mal_mutex_unlock(&context, &soundLock); +} + +static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* message) +{ + (void)pContext; + (void)pDevice; + TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors. +} + +static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameCount, void* pFramesOut) +{ + // This is where all of the mixing takes place. + (void)pDevice; + + // Mixing is basically just an accumulation. We need to initialize the output buffer to 0. + memset(pFramesOut, 0, frameCount*pDevice->channels*mal_get_sample_size_in_bytes(pDevice->format)); + + // Using a mutex here for thread-safety which makes things not real-time. This is unlikely to be necessary for this project, but may + // want to consider how you might want to avoid this. + mal_mutex_lock(&context, &soundLock); + { + float* pFramesOutF = (float*)pFramesOut; // <-- Just for convenience. + + // Sounds. + for (SoundInternal* internalSound = firstSound; internalSound != NULL; internalSound = internalSound->next) + { + // Ignore stopped or paused sounds. + if (!internalSound->playing || internalSound->paused) { + continue; + } + + mal_uint32 framesRead = 0; + for (;;) { + if (framesRead > frameCount) { + TraceLog(LOG_DEBUG, "Mixed too many frames from sound"); + break; + } + if (framesRead == frameCount) { + break; + } + + // Keep reading until the end of the buffer, or we've already read as much as is allowed. + mal_uint32 framesToRead = (frameCount - framesRead); + mal_uint32 framesRemaining = (internalSound->frameCount - internalSound->frameCursorPos); + if (framesToRead > framesRemaining) { + framesToRead = framesRemaining; + } + + // This is where the real mixing takes place. This can be optimized. This assumes the device and sound are of the same format. + // + // TODO: Implement pitching. + for (mal_uint32 iFrame = 0; iFrame < framesToRead; ++iFrame) { + float* pFrameOut = pFramesOutF + ((framesRead+iFrame) * device.channels); + float* pFrameIn = ((float*)internalSound->data) + ((internalSound->frameCursorPos+iFrame) * device.channels); + + for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { + pFrameOut[iChannel] += pFrameIn[iChannel] * masterVolume * internalSound->volume; + } + } + + framesRead += framesToRead; + internalSound->frameCursorPos += framesToRead; + + // If we've reached the end of the sound's internal buffer we do one of two things: loop back to the start, or just stop. + if (framesToRead == framesRemaining) { + if (!internalSound->looping) { + break; + } + } + } + } + + // Music. + // TODO: Implement me. + } + mal_mutex_unlock(&context, &soundLock); + + return frameCount; // We always output the same number of frames that were originally requested. +} +#endif // Initialize audio device void InitAudioDevice(void) { +#if USE_MINI_AL + // Context. + mal_context_config contextConfig = mal_context_config_init(OnLog_MAL); + mal_result result = mal_context_init(NULL, 0, &contextConfig, &context); + if (result != MAL_SUCCESS) + { + return; + } + + // Device. Using the default device. Format is floating point because it simplifies mixing. + mal_device_config deviceConfig = mal_device_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, OnSendAudioDataToDevice); + result = mal_device_init(&context, mal_device_type_playback, NULL, &deviceConfig, NULL, &device); + if (result != MAL_SUCCESS) + { + mal_context_uninit(&context); + return; + } + + // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running + // while there's at least one sound being played. + result = mal_device_start(&device); + if (result != MAL_SUCCESS) + { + mal_device_uninit(&device); + mal_context_uninit(&context); + return; + } + + // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may + // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. + if (!mal_mutex_create(&context, &soundLock)) + { + TraceLog(LOG_ERROR, "Failed to create mutex for audio mixing"); + mal_device_uninit(&device); + mal_context_uninit(&context); + return; + } + + + isAudioInitialized = MAL_TRUE; +#else // Open and initialize a device with default settings ALCdevice *device = alcOpenDevice(NULL); @@ -230,13 +421,30 @@ void InitAudioDevice(void) alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f); alListenerf(AL_GAIN, 1.0f); + + if (alIsExtensionPresent("AL_EXT_float32")) { + TraceLog(LOG_INFO, "AL_EXT_float32 supported"); + } else { + TraceLog(LOG_INFO, "AL_EXT_float32 not supported"); + } } } +#endif } // Close the audio device for all contexts void CloseAudioDevice(void) { +#if USE_MINI_AL + if (!isAudioInitialized) { + TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); + return; + } + + mal_mutex_delete(&context, &soundLock); + mal_device_uninit(&device); + mal_context_uninit(&context); +#else ALCdevice *device; ALCcontext *context = alcGetCurrentContext(); @@ -247,6 +455,7 @@ void CloseAudioDevice(void) alcMakeContextCurrent(NULL); alcDestroyContext(context); alcCloseDevice(device); +#endif TraceLog(LOG_INFO, "Audio device closed successfully"); } @@ -254,6 +463,9 @@ void CloseAudioDevice(void) // Check if device has been initialized successfully bool IsAudioDeviceReady(void) { +#if USE_MINI_AL + return isAudioInitialized; +#else ALCcontext *context = alcGetCurrentContext(); if (context == NULL) return false; @@ -264,6 +476,7 @@ bool IsAudioDeviceReady(void) if (device == NULL) return false; else return true; } +#endif } // Set master volume (listener) @@ -271,8 +484,12 @@ void SetMasterVolume(float volume) { if (volume < 0.0f) volume = 0.0f; else if (volume > 1.0f) volume = 1.0f; - + +#if USE_MINI_AL + masterVolume = 1; +#else alListenerf(AL_GAIN, volume); +#endif } //---------------------------------------------------------------------------------- @@ -349,6 +566,47 @@ Sound LoadSoundFromWave(Wave wave) if (wave.data != NULL) { +#if USE_MINI_AL + // When using mini_al we need to do our own mixing. To simplify this we need convert the format of each sound to be consistent with + // the format used to open the playback device. We can do this two ways: + // + // 1) Convert the whole sound in one go at load time (here). + // 2) Convert the audio data in chunks at mixing time. + // + // I have decided on the first option because it offloads work required for the format conversion to the to the loading stage. The + // downside to this is that it uses more memory if the original sound is u8 or s16. + mal_format formatIn = ((wave.sampleSize == 8) ? mal_format_u8 : ((wave.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + mal_uint32 frameCountIn = wave.sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. + + mal_uint32 frameCount = mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn); + if (frameCount == 0) { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion."); + } + + SoundInternal* internalSound = (SoundInternal*)calloc(sizeof(*internalSound) + (frameCount*DEVICE_CHANNELS*4), 1); // <-- Make sure this is initialized to zero for safety. + if (internalSound == NULL) { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to allocate memory for internal buffer"); + } + + frameCount = mal_convert_frames(internalSound->data, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); + if (frameCount == 0) { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed."); + } + + internalSound->format = DEVICE_FORMAT; + internalSound->channels = DEVICE_CHANNELS; + internalSound->sampleRate = DEVICE_SAMPLE_RATE; + internalSound->frameCount = frameCount; + internalSound->frameCursorPos = 0; + internalSound->volume = 1; + internalSound->pitch = 1; + internalSound->playing = 0; + internalSound->paused = 0; + internalSound->looping = 0; + AppendSound(internalSound); + + sound.handle = (void*)internalSound; +#else ALenum format = 0; // The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample) @@ -402,6 +660,7 @@ Sound LoadSoundFromWave(Wave wave) sound.source = source; sound.buffer = buffer; sound.format = format; +#endif } return sound; @@ -418,10 +677,16 @@ void UnloadWave(Wave wave) // Unload sound void UnloadSound(Sound sound) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + RemoveSound(internalSound); + free(internalSound); +#else alSourceStop(sound.source); alDeleteSources(1, &sound.source); alDeleteBuffers(1, &sound.buffer); +#endif TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer); } @@ -430,6 +695,22 @@ void UnloadSound(Sound sound) // NOTE: data must match sound.format void UpdateSound(Sound sound, const void *data, int samplesCount) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound"); + return; + } + + internalSound->playing = false; + internalSound->paused = false; + internalSound->frameCursorPos = 0; + + // TODO: May want to lock/unlock this since this data buffer is read at mixing time. However, this puts a mutex in + // in the mixing code which makes it no longer real-time. This is likely not a critical issue for this project, though. + memcpy(internalSound->data, data, samplesCount*internalSound->channels*mal_get_sample_size_in_bytes(internalSound->format)); +#else ALint sampleRate, sampleSize, channels; alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format @@ -451,12 +732,26 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) // Attach sound buffer to source again alSourcei(sound.source, AL_BUFFER, sound.buffer); +#endif } // Play a sound void PlaySound(Sound sound) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "PlaySound() : Invalid sound"); + return; + } + + internalSound->playing = 1; + internalSound->paused = 0; + internalSound->frameCursorPos = 0; +#else alSourcePlay(sound.source); // Play the sound +#endif //TraceLog(LOG_INFO, "Playing sound"); @@ -477,28 +772,72 @@ void PlaySound(Sound sound) // Pause a sound void PauseSound(Sound sound) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "PauseSound() : Invalid sound"); + return; + } + + internalSound->paused = true; +#else alSourcePause(sound.source); +#endif } // Resume a paused sound void ResumeSound(Sound sound) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "ResumeSound() : Invalid sound"); + return; + } + + internalSound->paused = false; +#else ALenum state; alGetSourcei(sound.source, AL_SOURCE_STATE, &state); if (state == AL_PAUSED) alSourcePlay(sound.source); +#endif } // Stop reproducing a sound void StopSound(Sound sound) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "StopSound() : Invalid sound"); + return; + } + + internalSound->playing = false; + internalSound->paused = false; +#else alSourceStop(sound.source); +#endif } // Check if a sound is playing bool IsSoundPlaying(Sound sound) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "IsSoundPlaying() : Invalid sound"); + return false; + } + + return internalSound->playing && !internalSound->paused; +#else bool playing = false; ALint state; @@ -506,23 +845,73 @@ bool IsSoundPlaying(Sound sound) if (state == AL_PLAYING) playing = true; return playing; +#endif } // Set volume for a sound void SetSoundVolume(Sound sound, float volume) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "SetSoundVolume() : Invalid sound"); + return; + } + + internalSound->volume = volume; +#else alSourcef(sound.source, AL_GAIN, volume); +#endif } // Set pitch for a sound void SetSoundPitch(Sound sound, float pitch) { +#if USE_MINI_AL + SoundInternal* internalSound = (SoundInternal*)sound.handle; + if (internalSound == NULL) + { + TraceLog(LOG_ERROR, "SetSoundPitch() : Invalid sound"); + return; + } + + internalSound->pitch = pitch; +#else alSourcef(sound.source, AL_PITCH, pitch); +#endif } // Convert wave data to desired format void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) { + mal_format formatIn = ((wave->sampleSize == 8) ? mal_format_u8 : ((wave->sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + mal_format formatOut = (( sampleSize == 8) ? mal_format_u8 : (( sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + + mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. + + mal_uint32 frameCount = mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn); + if (frameCount == 0) { + TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion."); + return; + } + + void* data = malloc(frameCount * channels * (sampleSize/8)); + + frameCount = mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn); + if (frameCount == 0) { + TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed."); + return; + } + + wave->sampleCount = frameCount; + wave->sampleSize = sampleSize; + wave->sampleRate = sampleRate; + wave->channels = channels; + free(wave->data); + wave->data = data; + +#if 0 // Format sample rate // NOTE: Only supported 22050 <--> 44100 if (wave->sampleRate != sampleRate) @@ -601,6 +990,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) free(wave->data); wave->data = data; } +#endif } // Copy a wave to a new wave -- cgit v1.2.3 From b0852002b83c615a67ae1b38feb8940213f6eed9 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 12 Nov 2017 14:54:37 +1000 Subject: Rename SoundInternal to SoundData for consistency with MusicData. --- src/audio.c | 42 +++++++++++++++++++++++------------------- src/raylib.h | 2 +- 2 files changed, 24 insertions(+), 20 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index a5f117b5..83c80d8e 100644 --- a/src/audio.c +++ b/src/audio.c @@ -211,8 +211,8 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo #define DEVICE_CHANNELS 2 #define DEVICE_SAMPLE_RATE 44100 -typedef struct SoundInternal SoundInternal; -struct SoundInternal +typedef struct SoundData SoundData; +struct SoundData { mal_format format; mal_uint32 channels; @@ -224,8 +224,8 @@ struct SoundInternal bool playing; bool paused; bool looping; - SoundInternal* next; - SoundInternal* prev; + SoundData* next; + SoundData* prev; mal_uint8 data[1]; // Raw audio data. }; @@ -234,10 +234,10 @@ static mal_device device; static mal_bool32 isAudioInitialized = MAL_FALSE; static float masterVolume = 1; static mal_mutex soundLock; -static SoundInternal* firstSound; // Sounds are tracked in a linked list. -static SoundInternal* lastSound; +static SoundData* firstSound; // Sounds are tracked in a linked list. +static SoundData* lastSound; -static void AppendSound(SoundInternal* internalSound) +static void AppendSound(SoundData* internalSound) { mal_mutex_lock(&context, &soundLock); { @@ -253,7 +253,7 @@ static void AppendSound(SoundInternal* internalSound) mal_mutex_unlock(&context, &soundLock); } -static void RemoveSound(SoundInternal* internalSound) +static void RemoveSound(SoundData* internalSound) { mal_mutex_lock(&context, &soundLock); { @@ -294,7 +294,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC float* pFramesOutF = (float*)pFramesOut; // <-- Just for convenience. // Sounds. - for (SoundInternal* internalSound = firstSound; internalSound != NULL; internalSound = internalSound->next) + for (SoundData* internalSound = firstSound; internalSound != NULL; internalSound = internalSound->next) { // Ignore stopped or paused sounds. if (!internalSound->playing || internalSound->paused) { @@ -583,7 +583,7 @@ Sound LoadSoundFromWave(Wave wave) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion."); } - SoundInternal* internalSound = (SoundInternal*)calloc(sizeof(*internalSound) + (frameCount*DEVICE_CHANNELS*4), 1); // <-- Make sure this is initialized to zero for safety. + SoundData* internalSound = (SoundData*)calloc(sizeof(*internalSound) + (frameCount*DEVICE_CHANNELS*4), 1); // <-- Make sure this is initialized to zero for safety. if (internalSound == NULL) { TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to allocate memory for internal buffer"); } @@ -678,7 +678,7 @@ void UnloadWave(Wave wave) void UnloadSound(Sound sound) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; RemoveSound(internalSound); free(internalSound); #else @@ -696,7 +696,7 @@ void UnloadSound(Sound sound) void UpdateSound(Sound sound, const void *data, int samplesCount) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound"); @@ -739,7 +739,7 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) void PlaySound(Sound sound) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "PlaySound() : Invalid sound"); @@ -773,7 +773,7 @@ void PlaySound(Sound sound) void PauseSound(Sound sound) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "PauseSound() : Invalid sound"); @@ -790,7 +790,7 @@ void PauseSound(Sound sound) void ResumeSound(Sound sound) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "ResumeSound() : Invalid sound"); @@ -811,7 +811,7 @@ void ResumeSound(Sound sound) void StopSound(Sound sound) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "StopSound() : Invalid sound"); @@ -829,7 +829,7 @@ void StopSound(Sound sound) bool IsSoundPlaying(Sound sound) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "IsSoundPlaying() : Invalid sound"); @@ -852,7 +852,7 @@ bool IsSoundPlaying(Sound sound) void SetSoundVolume(Sound sound, float volume) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "SetSoundVolume() : Invalid sound"); @@ -869,7 +869,7 @@ void SetSoundVolume(Sound sound, float volume) void SetSoundPitch(Sound sound, float pitch) { #if USE_MINI_AL - SoundInternal* internalSound = (SoundInternal*)sound.handle; + SoundData* internalSound = (SoundData*)sound.handle; if (internalSound == NULL) { TraceLog(LOG_ERROR, "SetSoundPitch() : Invalid sound"); @@ -1173,7 +1173,11 @@ void UnloadMusicStream(Music music) // Start music playing (open stream) void PlayMusicStream(Music music) { +#if USE_MINI_AL + //InternalMusic* internalMusic = +#else alSourcePlay(music->stream.source); +#endif } // Pause music playing diff --git a/src/raylib.h b/src/raylib.h index ae1e5813..b21b0878 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -486,7 +486,7 @@ typedef struct Wave { // Sound source type typedef struct Sound { - void* handle; // A pointer to internal data used by the sound system. + void* handle; // A pointer to internal data used by the audio system. unsigned int source; // OpenAL audio source id unsigned int buffer; // OpenAL audio buffer id -- cgit v1.2.3 From 68bf6c9701f9c1705849600838571f4f3b5c1446 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 12 Nov 2017 20:59:16 +1000 Subject: Initial work on porting AudioStream to use mini_al. --- src/audio.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/raylib.h | 2 + 2 files changed, 336 insertions(+), 3 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 83c80d8e..b04cc679 100644 --- a/src/audio.c +++ b/src/audio.c @@ -176,6 +176,22 @@ typedef struct MusicData { unsigned int samplesLeft; // Number of samples left to end } MusicData; +// AudioStreamData +typedef struct AudioStreamData AudioStreamData; +struct AudioStreamData { + mal_dsp dsp; // AudioStream data needs to flow through a persistent conversion pipeline. Not doing this will result in glitches between buffer updates. + float volume; + float pitch; + bool playing; + bool paused; + bool isSubBufferProcessed[2]; + unsigned int frameCursorPos; + unsigned int bufferSizeInFrames; + AudioStreamData* next; + AudioStreamData* prev; + unsigned char buffer[1]; +}; + #if defined(AUDIO_STANDALONE) typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; #endif @@ -236,6 +252,8 @@ static float masterVolume = 1; static mal_mutex soundLock; static SoundData* firstSound; // Sounds are tracked in a linked list. static SoundData* lastSound; +static AudioStreamData* firstAudioStream; +static AudioStreamData* lastAudioStream; static void AppendSound(SoundData* internalSound) { @@ -272,6 +290,42 @@ static void RemoveSound(SoundData* internalSound) mal_mutex_unlock(&context, &soundLock); } +static void AppendAudioStream(AudioStreamData* internalAudioStream) +{ + mal_mutex_lock(&context, &soundLock); + { + if (firstAudioStream == NULL) { + firstAudioStream = internalAudioStream; + } else { + lastAudioStream->next = internalAudioStream; + internalAudioStream->prev = lastAudioStream; + } + + lastAudioStream = internalAudioStream; + } + mal_mutex_unlock(&context, &soundLock); +} + +static void RemoveAudioStream(AudioStreamData* internalAudioStream) +{ + mal_mutex_lock(&context, &soundLock); + { + if (internalAudioStream->prev == NULL) { + firstAudioStream = internalAudioStream->next; + } else { + internalAudioStream->prev->next = internalAudioStream->next; + } + + if (internalAudioStream->next == NULL) { + lastAudioStream = internalAudioStream->prev; + } else { + internalAudioStream->next->prev = internalAudioStream->prev; + } + } + mal_mutex_unlock(&context, &soundLock); +} + + static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* message) { (void)pContext; @@ -342,8 +396,63 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC } } - // Music. - // TODO: Implement me. + // AudioStreams. These are handling slightly differently to sounds because we do data conversion at mixing time rather than + // load time. + for (AudioStreamData* internalData = firstAudioStream; internalData != NULL; internalData = internalData->next) + { + // Ignore stopped or paused streams. + if (!internalData->playing || internalData->paused) { + continue; + } + + mal_uint32 framesRead = 0; + for (;;) { + if (framesRead > frameCount) { + TraceLog(LOG_DEBUG, "Mixed too many frames from sound"); + break; + } + if (framesRead == frameCount) { + break; + } + + // Just read as much data we can from the stream. + mal_uint32 framesToRead = (frameCount - framesRead); + while (framesToRead > 0) { + float tempBuffer[1024]; // 512 frames for stereo. + + mal_uint32 framesToReadRightNow = framesToRead; + if (framesToReadRightNow > sizeof(tempBuffer)/DEVICE_CHANNELS) { + framesToReadRightNow = sizeof(tempBuffer)/DEVICE_CHANNELS; + } + + mal_uint32 framesJustRead = mal_dsp_read_frames(&internalData->dsp, framesToReadRightNow, tempBuffer); + if (framesJustRead > 0) { + // This is where the real mixing takes place. This can be optimized. This assumes the device and sound are of the same format. + // + // TODO: Implement pitching. + for (mal_uint32 iFrame = 0; iFrame < framesToRead; ++iFrame) { + float* pFrameOut = pFramesOutF + ((framesRead+iFrame) * device.channels); + float* pFrameIn = tempBuffer + (iFrame * device.channels); + + for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { + pFrameOut[iChannel] += pFrameIn[iChannel] * masterVolume * internalData->volume; + } + } + + framesToRead -= framesJustRead; + framesRead += framesJustRead; + } else { + break; // Avoid an infinite loop. + } + } + + // If for some reason we weren't able to read every frame we'll need to break from the loop. Not doing this could + // theoretically put us into an infinite loop. + if (framesToRead > 0) { + break; + } + } + } } mal_mutex_unlock(&context, &soundLock); @@ -1375,6 +1484,65 @@ float GetMusicTimePlayed(Music music) return secondsPlayed; } + +static mal_uint32 UpdateAudioStream_OnDSPRead(mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + AudioStreamData* internalData = (AudioStreamData*)pUserData; + + mal_uint32 subBufferSizeInFrames = AUDIO_BUFFER_SIZE; + mal_uint32 currentSubBufferIndex = internalData->frameCursorPos / subBufferSizeInFrames; + if (currentSubBufferIndex > 1) { + TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); + return 0; + } + + // Another thread can update the processed state of buffers so we just take a copy here to try and avoid potential synchronization problems. + bool isSubBufferProcessed[2]; + isSubBufferProcessed[0] = internalData->isSubBufferProcessed[0]; + isSubBufferProcessed[1] = internalData->isSubBufferProcessed[1]; + + mal_uint32 channels = internalData->dsp.config.channelsIn; + mal_uint32 sampleSizeInBytes = mal_get_sample_size_in_bytes(internalData->dsp.config.formatIn); + mal_uint32 frameSizeInBytes = sampleSizeInBytes*channels; + + // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. + mal_uint32 framesRead = 0; + while (!isSubBufferProcessed[currentSubBufferIndex]) + { + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining == 0) { + break; + } + + mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; + mal_uint32 framesRemainingInThisSubBuffer = subBufferSizeInFrames - (internalData->frameCursorPos - firstFrameIndexOfThisSubBuffer); + + mal_uint32 framesToRead = totalFramesRemaining; + if (framesToRead > framesRemainingInThisSubBuffer) { + framesToRead = framesRemainingInThisSubBuffer; + } + + memcpy((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), internalData->buffer + (internalData->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); + + framesRead += framesToRead; + internalData->frameCursorPos = (internalData->frameCursorPos + framesToRead) % internalData->bufferSizeInFrames; + + // If we've read to the end of the buffer, mark it as processed. + if (framesToRead == framesRemainingInThisSubBuffer) { + internalData->isSubBufferProcessed[currentSubBufferIndex] = true; + currentSubBufferIndex = (currentSubBufferIndex + 1) % 2; + } + } + + // Zero-fill excess. + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining > 0) { + memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); + } + + return frameCount; +} + // Init audio stream (to stream audio pcm data) AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) { @@ -1391,6 +1559,41 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un stream.channels = 1; // Fallback to mono channel } + +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)calloc(1, sizeof(*internalData) + (AUDIO_BUFFER_SIZE*2 * stream.channels*(stream.sampleSize/8))); + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Failed to allocate buffer for audio stream"); + return stream; + } + + mal_dsp_config config; + memset(&config, 0, sizeof(config)); + config.formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + config.channelsIn = stream.channels; + config.sampleRateIn = stream.sampleRate; + config.formatOut = DEVICE_FORMAT; + config.channelsOut = DEVICE_CHANNELS; + config.sampleRateOut = DEVICE_SAMPLE_RATE; + mal_result result = mal_dsp_init(&config, UpdateAudioStream_OnDSPRead, internalData, &internalData->dsp); + if (result != MAL_SUCCESS) + { + TraceLog(LOG_ERROR, "InitAudioStream() : Failed to initialize data conversion pipeline"); + free(internalData); + return stream; + } + + // Buffers should be marked as processed by default so that a call to UpdateAudioStream() immediately after initialization works correctly. + internalData->isSubBufferProcessed[0] = true; + internalData->isSubBufferProcessed[1] = true; + internalData->bufferSizeInFrames = AUDIO_BUFFER_SIZE*2; + internalData->volume = 1; + internalData->pitch = 1; + AppendAudioStream(internalData); + + stream.handle = internalData; +#else // Setup OpenAL format if (stream.channels == 1) { @@ -1435,6 +1638,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un free(pcm); alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); +#endif TraceLog(LOG_INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo"); @@ -1444,6 +1648,11 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // Close audio stream and free memory void CloseAudioStream(AudioStream stream) { +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + RemoveAudioStream(internalData); + free(internalData); +#else // Stop playing channel alSourceStop(stream.source); @@ -1462,7 +1671,8 @@ void CloseAudioStream(AudioStream stream) // Delete source and buffers alDeleteSources(1, &stream.source); alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers); - +#endif + TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); } @@ -1471,6 +1681,67 @@ void CloseAudioStream(AudioStream stream) // NOTE 2: To unqueue a buffer it needs to be processed: IsAudioBufferProcessed() void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return; + } + + // We need to determine which half of the buffer needs updating. If the stream is not started and the cursor position is + // at the front of the buffer, update the first subbuffer. + if (internalData->isSubBufferProcessed[0] || internalData->isSubBufferProcessed[1]) + { + mal_uint32 subBufferToUpdate; + if (internalData->isSubBufferProcessed[0] && internalData->isSubBufferProcessed[1]) + { + // Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front. + subBufferToUpdate = 0; + internalData->frameCursorPos = 0; + } + else + { + // Just update whichever sub-buffer is processed. + subBufferToUpdate = (internalData->isSubBufferProcessed[0]) ? 0 : 1; + } + + mal_uint32 subBufferSizeInFrames = AUDIO_BUFFER_SIZE; + unsigned char *subBuffer = internalData->buffer + ((subBufferSizeInFrames * stream.channels * (stream.sampleSize/8)) * subBufferToUpdate); + + // Does this API expect a whole buffer to be updated in one go? Assuming so, but if not will need to change this logic. + if (subBufferSizeInFrames >= (mal_uint32)samplesCount) + { + mal_uint32 framesToWrite = subBufferSizeInFrames; + if (framesToWrite > (mal_uint32)samplesCount) { + framesToWrite = (mal_uint32)samplesCount; + } + + mal_uint32 bytesToWrite = framesToWrite * stream.channels * (stream.sampleSize/8); + memcpy(subBuffer, data, bytesToWrite); + + // Any leftover frames should be filled with zeros. + mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; + if (leftoverFrameCount > 0) { + memset(subBuffer + bytesToWrite, 0, leftoverFrameCount * stream.channels * (stream.sampleSize/8)); + } + + internalData->isSubBufferProcessed[subBufferToUpdate] = false; + } + else + { + TraceLog(LOG_ERROR, "[AUD ID %i] UpdateAudioStream() : Attempting to write too many frames to buffer"); + return; + } + } + else + { + TraceLog(LOG_ERROR, "[AUD ID %i] Audio buffer not available for updating"); + return; + } + + +#else ALuint buffer = 0; alSourceUnqueueBuffers(stream.source, 1, &buffer); @@ -1481,44 +1752,104 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) alSourceQueueBuffers(stream.source, 1, &buffer); } else TraceLog(LOG_WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source); +#endif } // Check if any audio stream buffers requires refill bool IsAudioBufferProcessed(AudioStream stream) { +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return false; + } + + return internalData->isSubBufferProcessed[0] || internalData->isSubBufferProcessed[1]; +#else ALint processed = 0; // Determine if music stream is ready to be written alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed); return (processed > 0); +#endif } // Play audio stream void PlayAudioStream(AudioStream stream) { +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return; + } + + internalData->playing = true; +#else alSourcePlay(stream.source); +#endif } // Play audio stream void PauseAudioStream(AudioStream stream) { +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return; + } + + internalData->paused = true; +#else alSourcePause(stream.source); +#endif } // Resume audio stream playing void ResumeAudioStream(AudioStream stream) { +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return; + } + + internalData->paused = false; +#else ALenum state; alGetSourcei(stream.source, AL_SOURCE_STATE, &state); if (state == AL_PAUSED) alSourcePlay(stream.source); +#endif } // Stop audio stream void StopAudioStream(AudioStream stream) { +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return; + } + + internalData->playing = 0; + internalData->paused = 0; + internalData->frameCursorPos = 0; + internalData->isSubBufferProcessed[0] = true; + internalData->isSubBufferProcessed[1] = true; +#else alSourceStop(stream.source); +#endif } //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index b21b0878..5e3724c9 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -504,6 +504,8 @@ typedef struct AudioStream { unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) unsigned int channels; // Number of channels (1-mono, 2-stereo) + void* handle; // A pointer to internal data used by the audio system. + int format; // OpenAL audio format specifier unsigned int source; // OpenAL audio source id unsigned int buffers[2]; // OpenAL audio buffers (double buffering) -- cgit v1.2.3 From ac4c911ad7e08cca3691896befbe8c09e3f3262f Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 12 Nov 2017 21:55:24 +1000 Subject: Work on porting Music to mini_al. --- src/audio.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/raylib.h | 3 +- 2 files changed, 116 insertions(+), 3 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index b04cc679..d4f426e0 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1283,7 +1283,7 @@ void UnloadMusicStream(Music music) void PlayMusicStream(Music music) { #if USE_MINI_AL - //InternalMusic* internalMusic = + PlayAudioStream(music->stream); #else alSourcePlay(music->stream.source); #endif @@ -1292,12 +1292,19 @@ void PlayMusicStream(Music music) // Pause music playing void PauseMusicStream(Music music) { +#if USE_MINI_AL + PauseAudioStream(music->stream); +#else alSourcePause(music->stream.source); +#endif } // Resume music playing void ResumeMusicStream(Music music) { +#if USE_MINI_AL + ResumeAudioStream(music->stream); +#else ALenum state; alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); @@ -1306,12 +1313,16 @@ void ResumeMusicStream(Music music) TraceLog(LOG_INFO, "[AUD ID %i] Resume music stream playing", music->stream.source); alSourcePlay(music->stream.source); } +#endif } // Stop music playing (close stream) // TODO: To clear a buffer, make sure they have been already processed! void StopMusicStream(Music music) { +#if USE_MINI_AL + StopAudioStream(music->stream); +#else alSourceStop(music->stream.source); /* @@ -1327,6 +1338,7 @@ void StopMusicStream(Music music) free(pcm); */ +#endif // Restart music context switch (music->ctxType) @@ -1351,6 +1363,77 @@ void StopMusicStream(Music music) // TODO: Make sure buffers are ready for update... check music state void UpdateMusicStream(Music music) { +#if USE_MINI_AL + bool streamEnding = false; + + // NOTE: Using dynamic allocation because it could require more than 16KB + void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); + + int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts + + while (IsAudioBufferProcessed(music->stream)) + { + if (music->samplesLeft >= AUDIO_BUFFER_SIZE) samplesCount = AUDIO_BUFFER_SIZE; + else samplesCount = music->samplesLeft; + + // TODO: Really don't like ctxType thingy... + switch (music->ctxType) + { + case MUSIC_AUDIO_OGG: + { + // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) + int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount*music->stream.channels); + + } break; + #if defined(SUPPORT_FILEFORMAT_FLAC) + case MUSIC_AUDIO_FLAC: + { + // NOTE: Returns the number of samples to process + unsigned int numSamplesFlac = (unsigned int)drflac_read_s16(music->ctxFlac, samplesCount*music->stream.channels, (short *)pcm); + + } break; + #endif + #if defined(SUPPORT_FILEFORMAT_XM) + case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; + #endif + #if defined(SUPPORT_FILEFORMAT_MOD) + case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break; + #endif + default: break; + } + + UpdateAudioStream(music->stream, pcm, samplesCount); + music->samplesLeft -= samplesCount; + + if (music->samplesLeft <= 0) + { + streamEnding = true; + break; + } + } + + // Free allocated pcm data + free(pcm); + + // Reset audio stream for looping + if (streamEnding) + { + StopMusicStream(music); // Stop music (and reset) + + // Decrease loopCount to stop when required + if (music->loopCount > 0) + { + music->loopCount--; // Decrease loop count + PlayMusicStream(music); // Play again + } + } + else + { + // NOTE: In case window is minimized, music stream is stopped, + // just make sure to play again on window restore + if (IsMusicPlaying(music)) PlayMusicStream(music); + } +#else ALenum state; ALint processed = 0; @@ -1431,11 +1514,15 @@ void UpdateMusicStream(Music music) if (state != AL_PLAYING) PlayMusicStream(music); } } +#endif } // Check if any music is playing bool IsMusicPlaying(Music music) { +#if USE_MINI_AL + return IsAudioStreamPlaying(music->stream); +#else bool playing = false; ALint state; @@ -1444,6 +1531,7 @@ bool IsMusicPlaying(Music music) if (state == AL_PLAYING) playing = true; return playing; +#endif } // Set volume for music @@ -1460,7 +1548,7 @@ void SetMusicPitch(Music music, float pitch) // Set music loop count (loop repeats) // NOTE: If set to -1, means infinite loop -void SetMusicLoopCount(Music music, float count) +void SetMusicLoopCount(Music music, int count) { music->loopCount = count; } @@ -1831,6 +1919,30 @@ void ResumeAudioStream(AudioStream stream) #endif } +// Check if audio stream is playing. +bool IsAudioStreamPlaying(AudioStream stream) +{ +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return false; + } + + return internalData->playing; +#else + bool playing = false; + ALint state; + + alGetSourcei(stream.source, AL_SOURCE_STATE, &state); + + if (state == AL_PLAYING) playing = true; + + return playing; +#endif +} + // Stop audio stream void StopAudioStream(AudioStream stream) { diff --git a/src/raylib.h b/src/raylib.h index 5e3724c9..725eb23b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1150,7 +1150,7 @@ RLAPI void ResumeMusicStream(Music music); // Resume RLAPI bool IsMusicPlaying(Music music); // Check if music is playing RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) -RLAPI void SetMusicLoopCount(Music music, float count); // Set music loop count (loop repeats) +RLAPI void SetMusicLoopCount(Music music, int count); // Set music loop count (loop repeats) RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) @@ -1163,6 +1163,7 @@ RLAPI bool IsAudioBufferProcessed(AudioStream stream); // Check i RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream +RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream #ifdef __cplusplus -- cgit v1.2.3 From e52f4282d6f8e7465367effb22ba6f90a9099667 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 12 Nov 2017 22:56:53 +1000 Subject: Update comments. --- src/audio.c | 2 +- src/external/mini_al.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index d4f426e0..70426532 100644 --- a/src/audio.c +++ b/src/audio.c @@ -396,7 +396,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC } } - // AudioStreams. These are handling slightly differently to sounds because we do data conversion at mixing time rather than + // AudioStreams. These are handled slightly differently to sounds because we do data conversion at mixing time rather than // load time. for (AudioStreamData* internalData = firstAudioStream; internalData != NULL; internalData = internalData->next) { diff --git a/src/external/mini_al.c b/src/external/mini_al.c index 2ea2cc6b..7b437851 100644 --- a/src/external/mini_al.c +++ b/src/external/mini_al.c @@ -1,2 +1,4 @@ +// The implementation of mini_al needs to #include windows.h which means it needs to go into +// it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc. #define MAL_IMPLEMENTATION -#include "mini_al.h" // <-- The implementation of mini_al.h #includes windows.h, so need to #undef some stuff. \ No newline at end of file +#include "mini_al.h" \ No newline at end of file -- cgit v1.2.3 From 3a96a66fdfd2b403be20e4f2b104baf3a25961a1 Mon Sep 17 00:00:00 2001 From: David Reid Date: Mon, 13 Nov 2017 21:46:29 +1000 Subject: Bring up to date with changes to mini_al. --- src/audio.c | 26 +-- src/external/mini_al.h | 446 +++++++++++++++++++++++++++++-------------------- 2 files changed, 275 insertions(+), 197 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 70426532..d29ad1ba 100644 --- a/src/audio.c +++ b/src/audio.c @@ -257,7 +257,7 @@ static AudioStreamData* lastAudioStream; static void AppendSound(SoundData* internalSound) { - mal_mutex_lock(&context, &soundLock); + mal_mutex_lock(&soundLock); { if (firstSound == NULL) { firstSound = internalSound; @@ -268,12 +268,12 @@ static void AppendSound(SoundData* internalSound) lastSound = internalSound; } - mal_mutex_unlock(&context, &soundLock); + mal_mutex_unlock(&soundLock); } static void RemoveSound(SoundData* internalSound) { - mal_mutex_lock(&context, &soundLock); + mal_mutex_lock(&soundLock); { if (internalSound->prev == NULL) { firstSound = internalSound->next; @@ -287,12 +287,12 @@ static void RemoveSound(SoundData* internalSound) internalSound->next->prev = internalSound->prev; } } - mal_mutex_unlock(&context, &soundLock); + mal_mutex_unlock(&soundLock); } static void AppendAudioStream(AudioStreamData* internalAudioStream) { - mal_mutex_lock(&context, &soundLock); + mal_mutex_lock(&soundLock); { if (firstAudioStream == NULL) { firstAudioStream = internalAudioStream; @@ -303,12 +303,12 @@ static void AppendAudioStream(AudioStreamData* internalAudioStream) lastAudioStream = internalAudioStream; } - mal_mutex_unlock(&context, &soundLock); + mal_mutex_unlock(&soundLock); } static void RemoveAudioStream(AudioStreamData* internalAudioStream) { - mal_mutex_lock(&context, &soundLock); + mal_mutex_lock(&soundLock); { if (internalAudioStream->prev == NULL) { firstAudioStream = internalAudioStream->next; @@ -322,7 +322,7 @@ static void RemoveAudioStream(AudioStreamData* internalAudioStream) internalAudioStream->next->prev = internalAudioStream->prev; } } - mal_mutex_unlock(&context, &soundLock); + mal_mutex_unlock(&soundLock); } @@ -343,7 +343,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC // Using a mutex here for thread-safety which makes things not real-time. This is unlikely to be necessary for this project, but may // want to consider how you might want to avoid this. - mal_mutex_lock(&context, &soundLock); + mal_mutex_lock(&soundLock); { float* pFramesOutF = (float*)pFramesOut; // <-- Just for convenience. @@ -454,7 +454,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC } } } - mal_mutex_unlock(&context, &soundLock); + mal_mutex_unlock(&soundLock); return frameCount; // We always output the same number of frames that were originally requested. } @@ -493,7 +493,7 @@ void InitAudioDevice(void) // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. - if (!mal_mutex_create(&context, &soundLock)) + if (mal_mutex_init(&context, &soundLock) != MAL_SUCCESS) { TraceLog(LOG_ERROR, "Failed to create mutex for audio mixing"); mal_device_uninit(&device); @@ -550,7 +550,7 @@ void CloseAudioDevice(void) return; } - mal_mutex_delete(&context, &soundLock); + mal_mutex_uninit(&soundLock); mal_device_uninit(&device); mal_context_uninit(&context); #else @@ -1573,7 +1573,7 @@ float GetMusicTimePlayed(Music music) } -static mal_uint32 UpdateAudioStream_OnDSPRead(mal_uint32 frameCount, void* pFramesOut, void* pUserData) +static mal_uint32 UpdateAudioStream_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { AudioStreamData* internalData = (AudioStreamData*)pUserData; diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 49347e42..abb52295 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -292,21 +292,80 @@ typedef void* mal_handle; typedef void* mal_ptr; typedef void (* mal_proc)(); +typedef struct mal_context mal_context; +typedef struct mal_device mal_device; + +typedef struct +{ + mal_context* pContext; + + union + { #ifdef MAL_WIN32 - typedef mal_handle mal_thread; - typedef mal_handle mal_mutex; - typedef mal_handle mal_event; -#else - typedef pthread_t mal_thread; - typedef pthread_mutex_t mal_mutex; - typedef struct + struct + { + /*HANDLE*/ mal_handle hThread; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_t thread; + } posix; +#endif + + int _unused; + }; +} mal_thread; + +typedef struct +{ + mal_context* pContext; + + union { - pthread_mutex_t mutex; - pthread_cond_t condition; - mal_uint32 value; - } mal_event; +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hMutex; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_mutex_t mutex; + } posix; +#endif + + int _unused; + }; +} mal_mutex; + +typedef struct +{ + mal_context* pContext; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hEvent; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_mutex_t mutex; + pthread_cond_t condition; + mal_uint32 value; + } posix; #endif + int _unused; + }; +} mal_event; + #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) typedef mal_uint16 wchar_t; #endif @@ -393,9 +452,6 @@ typedef int mal_result; #define MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS -4096 #define MAL_WINMM_FAILED_TO_GET_SUPPORTED_FORMATS -4097 -typedef struct mal_context mal_context; -typedef struct mal_device mal_device; - typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message); typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples); typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples); @@ -481,7 +537,7 @@ typedef struct typedef struct mal_src mal_src; -typedef mal_uint32 (* mal_src_read_proc)(mal_uint32 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read. +typedef mal_uint32 (* mal_src_read_proc)(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read. typedef enum { @@ -530,7 +586,7 @@ struct mal_src }; typedef struct mal_dsp mal_dsp; -typedef mal_uint32 (* mal_dsp_read_proc)(mal_uint32 frameCount, void* pSamplesOut, void* pUserData); +typedef mal_uint32 (* mal_dsp_read_proc)(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData); typedef struct { @@ -1032,7 +1088,6 @@ mal_result mal_context_uninit(mal_context* pContext); // application ensures mutal exclusion to the output buffer at their level. // // Efficiency: LOW -// This API dynamically links to backend DLLs/SOs (such as dsound.dll). mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo); // Initializes a device. @@ -1332,10 +1387,19 @@ mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 chann // /////////////////////////////////////////////////////////////////////////////// -mal_bool32 mal_mutex_create(mal_context* pContext, mal_mutex* pMutex); -void mal_mutex_delete(mal_context* pContext, mal_mutex* pMutex); -void mal_mutex_lock(mal_context* pContext, mal_mutex* pMutex); -void mal_mutex_unlock(mal_context* pContext, mal_mutex* pMutex); +// Creates a mutex. +// +// A mutex must be created from a valid context. A mutex is initially unlocked. +mal_result mal_mutex_init(mal_context* pContext, mal_mutex* pMutex); + +// Deletes a mutex. +void mal_mutex_uninit(mal_mutex* pMutex); + +// Locks a mutex with an infinite timeout. +void mal_mutex_lock(mal_mutex* pMutex); + +// Unlocks a mutex. +void mal_mutex_unlock(mal_mutex* pMutex); @@ -1913,23 +1977,21 @@ mal_proc mal_dlsym(mal_handle handle, const char* symbol) // /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_WIN32 -mal_bool32 mal_thread_create__win32(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) +mal_result mal_thread_create__win32(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) { (void)pContext; - *pThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL); - if (*pThread == NULL) { - return MAL_FALSE; + pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL); + if (pThread->win32.hThread == NULL) { + return MAL_FAILED_TO_CREATE_THREAD; } - return MAL_TRUE; + return MAL_SUCCESS; } -void mal_thread_wait__win32(mal_context* pContext, mal_thread* pThread) +void mal_thread_wait__win32(mal_thread* pThread) { - (void)pContext; - - WaitForSingleObject(*pThread, INFINITE); + WaitForSingleObject(pThread->win32.hThread, INFINITE); } void mal_sleep__win32(mal_uint32 milliseconds) @@ -1938,71 +2000,59 @@ void mal_sleep__win32(mal_uint32 milliseconds) } -mal_bool32 mal_mutex_create__win32(mal_context* pContext, mal_mutex* pMutex) +mal_result mal_mutex_init__win32(mal_context* pContext, mal_mutex* pMutex) { (void)pContext; - *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return MAL_FALSE; + pMutex->win32.hMutex = CreateEventA(NULL, FALSE, TRUE, NULL); + if (pMutex->win32.hMutex == NULL) { + return MAL_FAILED_TO_CREATE_MUTEX; } - return MAL_TRUE; + return MAL_SUCCESS; } -void mal_mutex_delete__win32(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_uninit__win32(mal_mutex* pMutex) { - (void)pContext; - - CloseHandle(*pMutex); + CloseHandle(pMutex->win32.hMutex); } -void mal_mutex_lock__win32(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_lock__win32(mal_mutex* pMutex) { - (void)pContext; - - WaitForSingleObject(*pMutex, INFINITE); + WaitForSingleObject(pMutex->win32.hMutex, INFINITE); } -void mal_mutex_unlock__win32(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_unlock__win32(mal_mutex* pMutex) { - (void)pContext; - - SetEvent(*pMutex); + SetEvent(pMutex->win32.hMutex); } -mal_bool32 mal_event_create__win32(mal_context* pContext, mal_event* pEvent) +mal_result mal_event_init__win32(mal_context* pContext, mal_event* pEvent) { (void)pContext; - *pEvent = CreateEventW(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return MAL_FALSE; + pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (pEvent->win32.hEvent == NULL) { + return MAL_FAILED_TO_CREATE_EVENT; } - return MAL_TRUE; + return MAL_SUCCESS; } -void mal_event_delete__win32(mal_context* pContext, mal_event* pEvent) +void mal_event_uninit__win32(mal_event* pEvent) { - (void)pContext; - - CloseHandle(*pEvent); + CloseHandle(pEvent->win32.hEvent); } -mal_bool32 mal_event_wait__win32(mal_context* pContext, mal_event* pEvent) +mal_bool32 mal_event_wait__win32(mal_event* pEvent) { - (void)pContext; - - return WaitForSingleObject(*pEvent, INFINITE) == WAIT_OBJECT_0; + return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0; } -mal_bool32 mal_event_signal__win32(mal_context* pContext, mal_event* pEvent) +mal_bool32 mal_event_signal__win32(mal_event* pEvent) { - (void)pContext; - - return SetEvent(*pEvent); + return SetEvent(pEvent->win32.hEvent); } #endif @@ -2021,12 +2071,17 @@ typedef int (* mal_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pt mal_bool32 mal_thread_create__posix(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) { - return ((mal_pthread_create_proc)pContext->posix.pthread_create)(pThread, NULL, entryProc, pData) == 0; + int result = ((mal_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, NULL, entryProc, pData); + if (result != 0) { + return MAL_FAILED_TO_CREATE_THREAD; + } + + return MAL_SUCCESS; } -void mal_thread_wait__posix(mal_context* pContext, mal_thread* pThread) +void mal_thread_wait__posix(mal_thread* pThread) { - ((mal_pthread_join_proc)pContext->posix.pthread_join)(*pThread, NULL); + ((mal_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL); } void mal_sleep__posix(mal_uint32 milliseconds) @@ -2035,78 +2090,85 @@ void mal_sleep__posix(mal_uint32 milliseconds) } -mal_bool32 mal_mutex_create__posix(mal_context* pContext, mal_mutex* pMutex) +mal_result mal_mutex_init__posix(mal_context* pContext, mal_mutex* pMutex) { - return ((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(pMutex, NULL) == 0; + int result = ((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pMutex->posix.mutex, NULL); + if (result != 0) { + return MAL_FAILED_TO_CREATE_MUTEX; + } + + return MAL_SUCCESS; } -void mal_mutex_delete__posix(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_uninit__posix(mal_mutex* pMutex) { - ((mal_pthread_mutex_destroy_proc)pContext->posix.pthread_mutex_destroy)(pMutex); + ((mal_pthread_mutex_destroy_proc)pMutex->pContext->posix.pthread_mutex_destroy)(&pMutex->posix.mutex); } -void mal_mutex_lock__posix(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_lock__posix(mal_mutex* pMutex) { - ((mal_pthread_mutex_lock_proc)pContext->posix.pthread_mutex_lock)(pMutex); + ((mal_pthread_mutex_lock_proc)pMutex->pContext->posix.pthread_mutex_lock)(&pMutex->posix.mutex); } -void mal_mutex_unlock__posix(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_unlock__posix(mal_mutex* pMutex) { - ((mal_pthread_mutex_unlock_proc)pContext->posix.pthread_mutex_unlock)(pMutex); + ((mal_pthread_mutex_unlock_proc)pMutex->pContext->posix.pthread_mutex_unlock)(&pMutex->posix.mutex); } -mal_bool32 mal_event_create__posix(mal_context* pContext, mal_event* pEvent) +mal_result mal_event_init__posix(mal_context* pContext, mal_event* pEvent) { - if (((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->mutex, NULL) != 0) { - return MAL_FALSE; + if (((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL) != 0) { + return MAL_FAILED_TO_CREATE_MUTEX; } - if (((mal_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->condition, NULL) != 0) { - return MAL_FALSE; + if (((mal_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL) != 0) { + return MAL_FAILED_TO_CREATE_EVENT; } - pEvent->value = 0; - return MAL_TRUE; + pEvent->posix.value = 0; + return MAL_SUCCESS; } -void mal_event_delete__posix(mal_context* pContext, mal_event* pEvent) +void mal_event_uninit__posix(mal_event* pEvent) { - ((mal_pthread_cond_destroy_proc)pContext->posix.pthread_cond_destroy)(&pEvent->condition); - ((mal_pthread_mutex_destroy_proc)pContext->posix.pthread_mutex_destroy)(&pEvent->mutex); + ((mal_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition); + ((mal_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex); } -mal_bool32 mal_event_wait__posix(mal_context* pContext, mal_event* pEvent) +mal_bool32 mal_event_wait__posix(mal_event* pEvent) { - ((mal_pthread_mutex_lock_proc)pContext->posix.pthread_mutex_lock)(&pEvent->mutex); + ((mal_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex); { - while (pEvent->value == 0) { - ((mal_pthread_cond_wait_proc)pContext->posix.pthread_cond_wait)(&pEvent->condition, &pEvent->mutex); + while (pEvent->posix.value == 0) { + ((mal_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex); } - pEvent->value = 0; // Auto-reset. + pEvent->posix.value = 0; // Auto-reset. } - ((mal_pthread_mutex_unlock_proc)pContext->posix.pthread_mutex_unlock)(&pEvent->mutex); + ((mal_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex); return MAL_TRUE; } -mal_bool32 mal_event_signal__posix(mal_context* pContext, mal_event* pEvent) +mal_bool32 mal_event_signal__posix(mal_event* pEvent) { - ((mal_pthread_mutex_lock_proc)pContext->posix.pthread_mutex_lock)(&pEvent->mutex); + ((mal_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex); { - pEvent->value = 1; - ((mal_pthread_cond_signal_proc)pContext->posix.pthread_cond_signal)(&pEvent->condition); + pEvent->posix.value = 1; + ((mal_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition); } - ((mal_pthread_mutex_unlock_proc)pContext->posix.pthread_mutex_unlock)(&pEvent->mutex); + ((mal_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex); return MAL_TRUE; } #endif -mal_bool32 mal_thread_create(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) +mal_result mal_thread_create(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) { - if (pThread == NULL || entryProc == NULL) return MAL_FALSE; + if (pContext == NULL || pThread == NULL || entryProc == NULL) return MAL_FALSE; + + pThread->pContext = pContext; #ifdef MAL_WIN32 return mal_thread_create__win32(pContext, pThread, entryProc, pData); @@ -2116,15 +2178,15 @@ mal_bool32 mal_thread_create(mal_context* pContext, mal_thread* pThread, mal_thr #endif } -void mal_thread_wait(mal_context* pContext, mal_thread* pThread) +void mal_thread_wait(mal_thread* pThread) { if (pThread == NULL) return; #ifdef MAL_WIN32 - mal_thread_wait__win32(pContext, pThread); + mal_thread_wait__win32(pThread); #endif #ifdef MAL_POSIX - mal_thread_wait__posix(pContext, pThread); + mal_thread_wait__posix(pThread); #endif } @@ -2139,100 +2201,104 @@ void mal_sleep(mal_uint32 milliseconds) } -mal_bool32 mal_mutex_create(mal_context* pContext, mal_mutex* pMutex) +mal_result mal_mutex_init(mal_context* pContext, mal_mutex* pMutex) { - if (pMutex == NULL) return MAL_FALSE; + if (pContext == NULL || pMutex == NULL) return MAL_INVALID_ARGS; + + pMutex->pContext = pContext; #ifdef MAL_WIN32 - return mal_mutex_create__win32(pContext, pMutex); + return mal_mutex_init__win32(pContext, pMutex); #endif #ifdef MAL_POSIX - return mal_mutex_create__posix(pContext, pMutex); + return mal_mutex_init__posix(pContext, pMutex); #endif } -void mal_mutex_delete(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_uninit(mal_mutex* pMutex) { - if (pMutex == NULL) return; + if (pMutex == NULL || pMutex->pContext == NULL) return; #ifdef MAL_WIN32 - mal_mutex_delete__win32(pContext, pMutex); + mal_mutex_uninit__win32(pMutex); #endif #ifdef MAL_POSIX - mal_mutex_delete__posix(pContext, pMutex); + mal_mutex_uninit__posix(pMutex); #endif } -void mal_mutex_lock(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_lock(mal_mutex* pMutex) { - if (pMutex == NULL) return; + if (pMutex == NULL || pMutex->pContext == NULL) return; #ifdef MAL_WIN32 - mal_mutex_lock__win32(pContext, pMutex); + mal_mutex_lock__win32(pMutex); #endif #ifdef MAL_POSIX - mal_mutex_lock__posix(pContext, pMutex); + mal_mutex_lock__posix(pMutex); #endif } -void mal_mutex_unlock(mal_context* pContext, mal_mutex* pMutex) +void mal_mutex_unlock(mal_mutex* pMutex) { - if (pMutex == NULL) return; + if (pMutex == NULL || pMutex->pContext == NULL) return; #ifdef MAL_WIN32 - mal_mutex_unlock__win32(pContext, pMutex); + mal_mutex_unlock__win32(pMutex); #endif #ifdef MAL_POSIX - mal_mutex_unlock__posix(pContext, pMutex); + mal_mutex_unlock__posix(pMutex); #endif } -mal_bool32 mal_event_create(mal_context* pContext, mal_event* pEvent) +mal_result mal_event_init(mal_context* pContext, mal_event* pEvent) { - if (pEvent == NULL) return MAL_FALSE; + if (pContext == NULL || pEvent == NULL) return MAL_FALSE; + + pEvent->pContext = pContext; #ifdef MAL_WIN32 - return mal_event_create__win32(pContext, pEvent); + return mal_event_init__win32(pContext, pEvent); #endif #ifdef MAL_POSIX - return mal_event_create__posix(pContext, pEvent); + return mal_event_init__posix(pContext, pEvent); #endif } -void mal_event_delete(mal_context* pContext, mal_event* pEvent) +void mal_event_uninit(mal_event* pEvent) { - if (pEvent == NULL) return; + if (pEvent == NULL || pEvent->pContext == NULL) return; #ifdef MAL_WIN32 - mal_event_delete__win32(pContext, pEvent); + mal_event_uninit__win32(pEvent); #endif #ifdef MAL_POSIX - mal_event_delete__posix(pContext, pEvent); + mal_event_uninit__posix(pEvent); #endif } -mal_bool32 mal_event_wait(mal_context* pContext, mal_event* pEvent) +mal_bool32 mal_event_wait(mal_event* pEvent) { - if (pEvent == NULL) return MAL_FALSE; + if (pEvent == NULL || pEvent->pContext == NULL) return MAL_FALSE; #ifdef MAL_WIN32 - return mal_event_wait__win32(pContext, pEvent); + return mal_event_wait__win32(pEvent); #endif #ifdef MAL_POSIX - return mal_event_wait__posix(pContext, pEvent); + return mal_event_wait__posix(pEvent); #endif } -mal_bool32 mal_event_signal(mal_context* pContext, mal_event* pEvent) +mal_bool32 mal_event_signal(mal_event* pEvent) { - if (pEvent == NULL) return MAL_FALSE; + if (pEvent == NULL || pEvent->pContext == NULL) return MAL_FALSE; #ifdef MAL_WIN32 - return mal_event_signal__win32(pContext, pEvent); + return mal_event_signal__win32(pEvent); #endif #ifdef MAL_POSIX - return mal_event_signal__posix(pContext, pEvent); + return mal_event_signal__posix(pEvent); #endif } @@ -2340,8 +2406,10 @@ static void mal_get_default_channel_mapping(mal_backend backend, mal_uint32 chan // The callback for reading from the client -> DSP -> device. -static inline mal_uint32 mal_device__on_read_from_client(mal_uint32 frameCount, void* pFramesOut, void* pUserData) +static inline mal_uint32 mal_device__on_read_from_client(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { + (void)pDSP; + mal_device* pDevice = (mal_device*)pUserData; mal_assert(pDevice != NULL); @@ -2354,8 +2422,10 @@ static inline mal_uint32 mal_device__on_read_from_client(mal_uint32 frameCount, } // The callback for reading from the device -> DSP -> client. -static inline mal_uint32 mal_device__on_read_from_device(mal_uint32 frameCount, void* pFramesOut, void* pUserData) +static inline mal_uint32 mal_device__on_read_from_device(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { + (void)pDSP; + mal_device* pDevice = (mal_device*)pUserData; mal_assert(pDevice != NULL); @@ -2705,7 +2775,7 @@ static mal_result mal_device__main_loop__null(mal_device* pDevice) // The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We // define our own implementation in this case. -#ifndef _WAVEFORMATEXTENSIBLE_ +#if defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_) typedef struct { WAVEFORMATEX Format; @@ -7452,7 +7522,7 @@ mal_result mal_context_init__openal(mal_context* pContext) libName = "libopenal.so"; #endif #ifdef MAL_APPLE - // I don't own a Mac so a contribution here would be much appreciated! Just don't know what the library is called... + libName = "OpenAL.framework/OpenAL"; #endif if (libName == NULL) { return MAL_NO_BACKEND; // Don't know what the library name is called. @@ -8230,10 +8300,10 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) // Let the other threads know that the device has stopped. mal_device__set_state(pDevice, MAL_STATE_STOPPED); - mal_event_signal(pDevice->pContext, &pDevice->stopEvent); + mal_event_signal(&pDevice->stopEvent); // We use an event to wait for a request to wake up. - mal_event_wait(pDevice->pContext, &pDevice->wakeupEvent); + mal_event_wait(&pDevice->wakeupEvent); // Default result code. pDevice->workResult = MAL_SUCCESS; @@ -8250,21 +8320,21 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) pDevice->workResult = mal_device__start_backend(pDevice); if (pDevice->workResult != MAL_SUCCESS) { - mal_event_signal(pDevice->pContext, &pDevice->startEvent); + mal_event_signal(&pDevice->startEvent); continue; } // The thread that requested the device to start playing is waiting for this thread to start the // device for real, which is now. mal_device__set_state(pDevice, MAL_STATE_STARTED); - mal_event_signal(pDevice->pContext, &pDevice->startEvent); + mal_event_signal(&pDevice->startEvent); // Now we just enter the main loop. The main loop can be broken with mal_device__break_main_loop(). mal_device__main_loop(pDevice); } // Make sure we aren't continuously waiting on a stop event. - mal_event_signal(pDevice->pContext, &pDevice->stopEvent); // <-- Is this still needed? + mal_event_signal(&pDevice->stopEvent); // <-- Is this still needed? #ifdef MAL_WIN32 mal_CoUninitialize(pDevice->pContext); @@ -8334,7 +8404,8 @@ mal_result mal_context_init_backend_apis__nix(mal_context* pContext) // pthread const char* libpthreadFileNames[] = { "libpthread.so", - "libpthread.so.0" + "libpthread.so.0", + "libpthread.dylib" }; for (size_t i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) { @@ -8691,7 +8762,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi pDevice->internalSampleRate = pDevice->sampleRate; mal_copy_memory(pDevice->internalChannelMap, pDevice->channelMap, sizeof(pDevice->channelMap)); - if (!mal_mutex_create(pContext, &pDevice->lock)) { + if (mal_mutex_init(pContext, &pDevice->lock) != MAL_SUCCESS) { return mal_post_error(pDevice, "Failed to create mutex.", MAL_FAILED_TO_CREATE_MUTEX); } @@ -8700,19 +8771,19 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi // // Each of these semaphores is released internally by the worker thread when the work is completed. The start // semaphore is also used to wake up the worker thread. - if (!mal_event_create(pContext, &pDevice->wakeupEvent)) { - mal_mutex_delete(pContext, &pDevice->lock); + if (mal_event_init(pContext, &pDevice->wakeupEvent) != MAL_SUCCESS) { + mal_mutex_uninit(&pDevice->lock); return mal_post_error(pDevice, "Failed to create worker thread wakeup event.", MAL_FAILED_TO_CREATE_EVENT); } - if (!mal_event_create(pContext, &pDevice->startEvent)) { - mal_event_delete(pContext, &pDevice->wakeupEvent); - mal_mutex_delete(pContext, &pDevice->lock); + if (mal_event_init(pContext, &pDevice->startEvent) != MAL_SUCCESS) { + mal_event_uninit(&pDevice->wakeupEvent); + mal_mutex_uninit(&pDevice->lock); return mal_post_error(pDevice, "Failed to create worker thread start event.", MAL_FAILED_TO_CREATE_EVENT); } - if (!mal_event_create(pContext, &pDevice->stopEvent)) { - mal_event_delete(pContext, &pDevice->startEvent); - mal_event_delete(pContext, &pDevice->wakeupEvent); - mal_mutex_delete(pContext, &pDevice->lock); + if (mal_event_init(pContext, &pDevice->stopEvent) != MAL_SUCCESS) { + mal_event_uninit(&pDevice->startEvent); + mal_event_uninit(&pDevice->wakeupEvent); + mal_mutex_uninit(&pDevice->lock); return mal_post_error(pDevice, "Failed to create worker thread stop event.", MAL_FAILED_TO_CREATE_EVENT); } @@ -8809,13 +8880,13 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi // Some backends don't require the worker thread. if (pContext->backend != mal_backend_opensl) { // The worker thread. - if (!mal_thread_create(pContext, &pDevice->thread, mal_worker_thread, pDevice)) { + if (mal_thread_create(pContext, &pDevice->thread, mal_worker_thread, pDevice) != MAL_SUCCESS) { mal_device_uninit(pDevice); return mal_post_error(pDevice, "Failed to create worker thread.", MAL_FAILED_TO_CREATE_THREAD); } // Wait for the worker thread to put the device into it's stopped state for real. - mal_event_wait(pContext, &pDevice->stopEvent); + mal_event_wait(&pDevice->stopEvent); } else { mal_device__set_state(pDevice, MAL_STATE_STOPPED); } @@ -8841,14 +8912,14 @@ void mal_device_uninit(mal_device* pDevice) // Wake up the worker thread and wait for it to properly terminate. if (pDevice->pContext->backend != mal_backend_opensl) { - mal_event_signal(pDevice->pContext, &pDevice->wakeupEvent); - mal_thread_wait(pDevice->pContext, &pDevice->thread); + mal_event_signal(&pDevice->wakeupEvent); + mal_thread_wait(&pDevice->thread); } - mal_event_delete(pDevice->pContext, &pDevice->stopEvent); - mal_event_delete(pDevice->pContext, &pDevice->startEvent); - mal_event_delete(pDevice->pContext, &pDevice->wakeupEvent); - mal_mutex_delete(pDevice->pContext, &pDevice->lock); + mal_event_uninit(&pDevice->stopEvent); + mal_event_uninit(&pDevice->startEvent); + mal_event_uninit(&pDevice->wakeupEvent); + mal_mutex_uninit(&pDevice->lock); #ifdef MAL_ENABLE_WASAPI if (pDevice->pContext->backend == mal_backend_wasapi) { @@ -8918,22 +8989,22 @@ mal_result mal_device_start(mal_device* pDevice) if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); mal_result result = MAL_ERROR; - mal_mutex_lock(pDevice->pContext, &pDevice->lock); + mal_mutex_lock(&pDevice->lock); { // Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely // a bug with the application. if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) { - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return mal_post_error(pDevice, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING); } if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return mal_post_error(pDevice, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED); } // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy. if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return mal_post_error(pDevice, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY); } @@ -8948,15 +9019,15 @@ mal_result mal_device_start(mal_device* pDevice) #endif // Synchronous backends. { - mal_event_signal(pDevice->pContext, &pDevice->wakeupEvent); + mal_event_signal(&pDevice->wakeupEvent); // Wait for the worker thread to finish starting the device. Note that the worker thread will be the one // who puts the device into the started state. Don't call mal_device__set_state() here. - mal_event_wait(pDevice->pContext, &pDevice->startEvent); + mal_event_wait(&pDevice->startEvent); result = pDevice->workResult; } } - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return result; } @@ -8967,22 +9038,22 @@ mal_result mal_device_stop(mal_device* pDevice) if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); mal_result result = MAL_ERROR; - mal_mutex_lock(pDevice->pContext, &pDevice->lock); + mal_mutex_lock(&pDevice->lock); { // Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely // a bug with the application. if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) { - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return mal_post_error(pDevice, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING); } if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) { - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return mal_post_error(pDevice, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED); } // The device needs to be in a started state. If it's not, we just let the caller know the device is busy. if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) { - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return mal_post_error(pDevice, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY); } @@ -9004,11 +9075,11 @@ mal_result mal_device_stop(mal_device* pDevice) // We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be // the one who puts the device into the stopped state. Don't call mal_device__set_state() here. - mal_event_wait(pDevice->pContext, &pDevice->stopEvent); + mal_event_wait(&pDevice->stopEvent); result = MAL_SUCCESS; } } - mal_mutex_unlock(pDevice->pContext, &pDevice->lock); + mal_mutex_unlock(&pDevice->lock); return result; } @@ -9186,7 +9257,7 @@ mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCoun framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; } - pCache->cachedFrameCount = pCache->pSRC->onRead(framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); + pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); } else { // A format conversion is required which means we need to use an intermediary buffer. mal_uint8 pIntermediaryBuffer[sizeof(pCache->pCachedFrames)]; @@ -9195,7 +9266,7 @@ mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCoun framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; } - pCache->cachedFrameCount = pCache->pSRC->onRead(framesToReadFromClient, pIntermediaryBuffer, pCache->pSRC->pUserData); + pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pIntermediaryBuffer, pCache->pSRC->pUserData); // Convert to f32. mal_pcm_convert(pCache->pCachedFrames, mal_format_f32, pIntermediaryBuffer, pCache->pSRC->config.formatIn, pCache->cachedFrameCount * channels); @@ -9263,7 +9334,7 @@ mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, // Fast path. No need for data conversion - just pass right through. if (pSRC->config.formatIn == pSRC->config.formatOut) { - return pSRC->onRead(frameCount, pFramesOut, pSRC->pUserData); + return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); } // Slower path. Need to do a format conversion. @@ -9276,7 +9347,7 @@ mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, framesToRead = frameCount; } - mal_uint32 framesRead = pSRC->onRead(framesToRead, pStagingBuffer, pSRC->pUserData); + mal_uint32 framesRead = pSRC->onRead(pSRC, framesToRead, pStagingBuffer, pSRC->pUserData); if (framesRead == 0) { break; } @@ -9791,12 +9862,14 @@ static void mal_dsp_mix_channels(float* pFramesOut, mal_uint32 channelsOut, cons } -mal_uint32 mal_dsp__src_on_read(mal_uint32 frameCount, void* pFramesOut, void* pUserData) +mal_uint32 mal_dsp__src_on_read(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { + (void)pSRC; + mal_dsp* pDSP = (mal_dsp*)pUserData; mal_assert(pDSP != NULL); - return pDSP->onRead(frameCount, pFramesOut, pDSP->pUserDataForOnRead); + return pDSP->onRead(pDSP, frameCount, pFramesOut, pDSP->pUserDataForOnRead); } mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* pUserData, mal_dsp* pDSP) @@ -9899,7 +9972,7 @@ mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFram // Fast path. if (pDSP->isPassthrough) { - return pDSP->onRead(frameCount, pFramesOut, pDSP->pUserDataForOnRead); + return pDSP->onRead(pDSP, frameCount, pFramesOut, pDSP->pUserDataForOnRead); } @@ -9923,7 +9996,7 @@ mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFram framesRead = mal_src_read_frames(&pDSP->src, framesToRead, pFrames[iFrames]); pFramesFormat[iFrames] = pDSP->src.config.formatOut; // Should always be f32. } else { - framesRead = pDSP->onRead(framesToRead, pFrames[iFrames], pDSP->pUserDataForOnRead); + framesRead = pDSP->onRead(pDSP, framesToRead, pFrames[iFrames], pDSP->pUserDataForOnRead); pFramesFormat[iFrames] = pDSP->config.formatIn; } @@ -9990,8 +10063,10 @@ typedef struct mal_uint32 iNextFrame; } mal_convert_frames__data; -mal_uint32 mal_convert_frames__on_read(mal_uint32 frameCount, void* pFramesOut, void* pUserData) +mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { + (void)pDSP; + mal_convert_frames__data* pData = (mal_convert_frames__data*)pUserData; mal_assert(pData != NULL); mal_assert(pData->totalFrameCount >= pData->iNextFrame); @@ -10333,9 +10408,12 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) // ================ // // v0.x - 2017-xx-xx +// - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll +// need to update. +// - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively. +// - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent. // - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of // audio data to a different format. -// - Expose the mutex APIs. // // v0.5 - 2017-11-11 // - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for -- cgit v1.2.3 From 88d2810fa3d6c87eeec673c54788a6f39e15f77a Mon Sep 17 00:00:00 2001 From: David Reid Date: Tue, 14 Nov 2017 21:15:50 +1000 Subject: Bug fixes for Music with mini_al. --- src/audio.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/external/mini_al.h | 54 ++++++++++++++++++++++++++++++++++++++++---------- src/raylib.h | 2 ++ 3 files changed, 100 insertions(+), 10 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index d29ad1ba..48e33d19 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1426,6 +1426,13 @@ void UpdateMusicStream(Music music) music->loopCount--; // Decrease loop count PlayMusicStream(music); // Play again } + else + { + if (music->loopCount == -1) + { + PlayMusicStream(music); + } + } } else { @@ -1506,6 +1513,13 @@ void UpdateMusicStream(Music music) music->loopCount--; // Decrease loop count PlayMusicStream(music); // Play again } + else + { + if (music->loopCount == -1) + { + PlayMusicStream(music); + } + } } else { @@ -1537,13 +1551,21 @@ bool IsMusicPlaying(Music music) // Set volume for music void SetMusicVolume(Music music, float volume) { +#if USE_MINI_AL + SetAudioStreamVolume(music->stream, volume); +#else alSourcef(music->stream.source, AL_GAIN, volume); +#endif } // Set pitch for music void SetMusicPitch(Music music, float pitch) { +#if USE_MINI_AL + SetAudioStreamPitch(music->stream, pitch); +#else alSourcef(music->stream.source, AL_PITCH, pitch); +#endif } // Set music loop count (loop repeats) @@ -1964,6 +1986,38 @@ void StopAudioStream(AudioStream stream) #endif } +void SetAudioStreamVolume(AudioStream stream, float volume) +{ +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return; + } + + internalData->volume = volume; +#else + alSourcef(stream.source, AL_GAIN, volume); +#endif +} + +void SetAudioStreamPitch(AudioStream stream, float pitch) +{ +#if USE_MINI_AL + AudioStreamData* internalData = (AudioStreamData*)stream.handle; + if (internalData == NULL) + { + TraceLog(LOG_ERROR, "Invalid audio stream"); + return; + } + + internalData->pitch = pitch; +#else + alSourcef(stream.source, AL_PITCH, pitch); +#endif +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/external/mini_al.h b/src/external/mini_al.h index abb52295..58f542cf 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -1358,6 +1358,13 @@ mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* // Returns the number of frames actually read. mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut); +// The same mal_src_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. +// +// Internally there exists a buffer that keeps track of the previous and next samples for sample rate conversion. The simple +// version of this function does _not_ flush this buffer because otherwise it causes clitches for streaming based conversion +// pipelines. The problem, however, is that sometimes you need those last few samples (such as if you're doing a bulk conversion +// of a static file). Enabling flushing will fix this for you. +mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); /////////////////////////////////////////////////////////////////////////////// @@ -1370,8 +1377,16 @@ mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFram mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* pUserData, mal_dsp* pDSP); // Reads a number of frames and runs them through the DSP processor. +// +// This this _not_ flush the internal buffers which means you may end up with a few less frames than you may expect. Look at +// mal_dsp_read_frames_ex() if you want to flush the buffers at the end of the read. mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut); +// The same mal_dsp_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. +// +// See documentation for mal_src_read_frames_ex() for an explanation on flushing. +mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); + // High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to // determine the required size of the output buffer. // @@ -9283,8 +9298,8 @@ mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCoun } -mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut); -mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut); +mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); +mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC) { @@ -9314,24 +9329,31 @@ mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* } mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut) +{ + return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE); +} + +mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) { if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; // Could just use a function pointer instead of a switch for this... switch (pSRC->config.algorithm) { - case mal_src_algorithm_none: return mal_src_read_frames_passthrough(pSRC, frameCount, pFramesOut); - case mal_src_algorithm_linear: return mal_src_read_frames_linear(pSRC, frameCount, pFramesOut); + case mal_src_algorithm_none: return mal_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); + case mal_src_algorithm_linear: return mal_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); default: return 0; } } -mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut) +mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) { mal_assert(pSRC != NULL); mal_assert(frameCount > 0); mal_assert(pFramesOut != NULL); + (void)flush; // Passthrough need not care about flushing. + // Fast path. No need for data conversion - just pass right through. if (pSRC->config.formatIn == pSRC->config.formatOut) { return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); @@ -9362,7 +9384,7 @@ mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, return totalFramesRead; } -mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut) +mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) { mal_assert(pSRC != NULL); mal_assert(frameCount > 0); @@ -9414,7 +9436,14 @@ mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void pNextFrame[j] = 0; } - pSRC->linear.isNextFramesLoaded = MAL_FALSE; + if (pSRC->linear.isNextFramesLoaded) { + pSRC->linear.isNextFramesLoaded = MAL_FALSE; + } else { + if (flush) { + pSRC->linear.isPrevFramesLoaded = MAL_FALSE; + } + } + break; } } @@ -9426,7 +9455,7 @@ mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void totalFramesRead += 1; // If there's no frames available we need to get out of this loop. - if (!pSRC->linear.isNextFramesLoaded) { + if (!pSRC->linear.isNextFramesLoaded && (!flush || !pSRC->linear.isPrevFramesLoaded)) { break; } } @@ -9967,6 +9996,11 @@ mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* } mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut) +{ + return mal_dsp_read_frames_ex(pDSP, frameCount, pFramesOut, MAL_FALSE); +} + +mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) { if (pDSP == NULL || pFramesOut == NULL) return 0; @@ -9993,7 +10027,7 @@ mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFram // The initial filling of sample data depends on whether or not we are using SRC. mal_uint32 framesRead = 0; if (pDSP->isSRCRequired) { - framesRead = mal_src_read_frames(&pDSP->src, framesToRead, pFrames[iFrames]); + framesRead = mal_src_read_frames_ex(&pDSP->src, framesToRead, pFrames[iFrames], flush); pFramesFormat[iFrames] = pDSP->src.config.formatOut; // Should always be f32. } else { framesRead = pDSP->onRead(pDSP, framesToRead, pFrames[iFrames], pDSP->pUserDataForOnRead); @@ -10116,7 +10150,7 @@ mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 chann return 0; } - return mal_dsp_read_frames(&dsp, frameCountOut, pOut); + return mal_dsp_read_frames_ex(&dsp, frameCountOut, pOut, MAL_TRUE); } diff --git a/src/raylib.h b/src/raylib.h index 725eb23b..1f138c2b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1165,6 +1165,8 @@ RLAPI void PauseAudioStream(AudioStream stream); // Pause a RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream +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) #ifdef __cplusplus } -- cgit v1.2.3 From 322d868841493e1da6455a7153b4930b284c46df Mon Sep 17 00:00:00 2001 From: David Reid Date: Tue, 14 Nov 2017 21:44:57 +1000 Subject: Fix minor errors with the OpenAL backend. --- src/audio.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 48e33d19..f159222d 100644 --- a/src/audio.c +++ b/src/audio.c @@ -82,9 +82,9 @@ #include "utils.h" // Required for: fopen() Android mapping #endif -//#if USE_MINI_AL - #include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. -//#else +#include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. + +#if !defined(USE_MINI_AL) || USE_MINI_AL == 0 #if defined(__APPLE__) #include "OpenAL/al.h" // OpenAL basic header #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) @@ -96,7 +96,7 @@ // OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples // OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) -//#endif +#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strncmp() @@ -176,22 +176,6 @@ typedef struct MusicData { unsigned int samplesLeft; // Number of samples left to end } MusicData; -// AudioStreamData -typedef struct AudioStreamData AudioStreamData; -struct AudioStreamData { - mal_dsp dsp; // AudioStream data needs to flow through a persistent conversion pipeline. Not doing this will result in glitches between buffer updates. - float volume; - float pitch; - bool playing; - bool paused; - bool isSubBufferProcessed[2]; - unsigned int frameCursorPos; - unsigned int bufferSizeInFrames; - AudioStreamData* next; - AudioStreamData* prev; - unsigned char buffer[1]; -}; - #if defined(AUDIO_STANDALONE) typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; #endif @@ -245,6 +229,22 @@ struct SoundData mal_uint8 data[1]; // Raw audio data. }; +// AudioStreamData +typedef struct AudioStreamData AudioStreamData; +struct AudioStreamData { + mal_dsp dsp; // AudioStream data needs to flow through a persistent conversion pipeline. Not doing this will result in glitches between buffer updates. + float volume; + float pitch; + bool playing; + bool paused; + bool isSubBufferProcessed[2]; + unsigned int frameCursorPos; + unsigned int bufferSizeInFrames; + AudioStreamData* next; + AudioStreamData* prev; + unsigned char buffer[1]; +}; + static mal_context context; static mal_device device; static mal_bool32 isAudioInitialized = MAL_FALSE; @@ -1594,7 +1594,7 @@ float GetMusicTimePlayed(Music music) return secondsPlayed; } - +#if USE_MINI_AL static mal_uint32 UpdateAudioStream_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { AudioStreamData* internalData = (AudioStreamData*)pUserData; @@ -1652,6 +1652,7 @@ static mal_uint32 UpdateAudioStream_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCou return frameCount; } +#endif // Init audio stream (to stream audio pcm data) AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) -- cgit v1.2.3 From e03afbf2fa4c3bba129a748c9f25efad46687528 Mon Sep 17 00:00:00 2001 From: David Reid Date: Wed, 15 Nov 2017 22:04:23 +1000 Subject: Add support for pitch shifting. This commit should bring the mini_al backend up to feature parity with the OpenAL backend. --- src/audio.c | 189 ++++++++++++++++++++++++++++++++++++------------- src/external/mini_al.h | 93 +++++++++++++++++++++--- 2 files changed, 223 insertions(+), 59 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index f159222d..d20380d0 100644 --- a/src/audio.c +++ b/src/audio.c @@ -72,7 +72,9 @@ #define SUPPORT_FILEFORMAT_MOD //------------------------------------------------- +#ifndef USE_MINI_AL #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. +#endif #if defined(AUDIO_STANDALONE) #include "audio.h" @@ -214,25 +216,24 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo typedef struct SoundData SoundData; struct SoundData { - mal_format format; - mal_uint32 channels; - mal_uint32 sampleRate; - mal_uint32 frameCount; - mal_uint32 frameCursorPos; // Keeps track of the next frame to read when mixing + mal_dsp dsp; // Necessary for pitch shift. This is an optimized passthrough when the pitch == 1. float volume; float pitch; bool playing; bool paused; bool looping; + unsigned int frameCursorPos; // Keeps track of the next frame to read when mixing + unsigned int bufferSizeInFrames; SoundData* next; SoundData* prev; - mal_uint8 data[1]; // Raw audio data. + unsigned char data[1]; // Raw audio data. }; // AudioStreamData typedef struct AudioStreamData AudioStreamData; -struct AudioStreamData { - mal_dsp dsp; // AudioStream data needs to flow through a persistent conversion pipeline. Not doing this will result in glitches between buffer updates. +struct AudioStreamData +{ + mal_dsp dsp; // AudioStream data needs to flow through a persistent conversion pipeline. Not doing this will result in glitches between buffer updates. float volume; float pitch; bool playing; @@ -250,7 +251,7 @@ static mal_device device; static mal_bool32 isAudioInitialized = MAL_FALSE; static float masterVolume = 1; static mal_mutex soundLock; -static SoundData* firstSound; // Sounds are tracked in a linked list. +static SoundData* firstSound; // Sounds are tracked in a linked list. static SoundData* lastSound; static AudioStreamData* firstAudioStream; static AudioStreamData* lastAudioStream; @@ -286,6 +287,9 @@ static void RemoveSound(SoundData* internalSound) } else { internalSound->next->prev = internalSound->prev; } + + internalSound->prev = NULL; + internalSound->next = NULL; } mal_mutex_unlock(&soundLock); } @@ -321,6 +325,9 @@ static void RemoveAudioStream(AudioStreamData* internalAudioStream) } else { internalAudioStream->next->prev = internalAudioStream->prev; } + + internalAudioStream->prev = NULL; + internalAudioStream->next = NULL; } mal_mutex_unlock(&soundLock); } @@ -333,6 +340,21 @@ static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* me TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors. } +// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation. +// +// framesOut is both an input and an output. It will be initially filled with zeros outside of this function. +static void MixFrames(float* framesOut, const float* framesIn, mal_uint32 frameCount, float localVolume) +{ + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { + for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { + float* frameOut = framesOut + (iFrame * device.channels); + float* frameIn = framesIn + (iFrame * device.channels); + + frameOut[iChannel] += frameIn[iChannel] * masterVolume * localVolume; + } + } +} + static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameCount, void* pFramesOut) { // This is where all of the mixing takes place. @@ -345,7 +367,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC // want to consider how you might want to avoid this. mal_mutex_lock(&soundLock); { - float* pFramesOutF = (float*)pFramesOut; // <-- Just for convenience. + float* framesOutF = (float*)pFramesOut; // <-- Just for convenience. // Sounds. for (SoundData* internalSound = firstSound; internalSound != NULL; internalSound = internalSound->next) @@ -365,33 +387,48 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC break; } - // Keep reading until the end of the buffer, or we've already read as much as is allowed. + // Just read as much data we can from the stream. mal_uint32 framesToRead = (frameCount - framesRead); - mal_uint32 framesRemaining = (internalSound->frameCount - internalSound->frameCursorPos); - if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; - } - - // This is where the real mixing takes place. This can be optimized. This assumes the device and sound are of the same format. - // - // TODO: Implement pitching. - for (mal_uint32 iFrame = 0; iFrame < framesToRead; ++iFrame) { - float* pFrameOut = pFramesOutF + ((framesRead+iFrame) * device.channels); - float* pFrameIn = ((float*)internalSound->data) + ((internalSound->frameCursorPos+iFrame) * device.channels); + while (framesToRead > 0) { + float tempBuffer[1024]; // 512 frames for stereo. - for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { - pFrameOut[iChannel] += pFrameIn[iChannel] * masterVolume * internalSound->volume; + mal_uint32 framesToReadRightNow = framesToRead; + if (framesToReadRightNow > sizeof(tempBuffer)/DEVICE_CHANNELS) { + framesToReadRightNow = sizeof(tempBuffer)/DEVICE_CHANNELS; } - } - framesRead += framesToRead; - internalSound->frameCursorPos += framesToRead; + // If we're not looping, we need to make sure we flush the internal buffers of the DSP pipeline to ensure we get the + // last few samples. + mal_bool32 flushDSP = !internalSound->looping; - // If we've reached the end of the sound's internal buffer we do one of two things: loop back to the start, or just stop. - if (framesToRead == framesRemaining) { - if (!internalSound->looping) { - break; + mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&internalSound->dsp, framesToReadRightNow, tempBuffer, flushDSP); + if (framesJustRead > 0) { + float* framesOut = framesOutF + (framesRead * device.channels); + float* framesIn = tempBuffer; + MixFrames(framesOut, framesIn, framesJustRead, internalSound->volume); + + framesToRead -= framesJustRead; + framesRead += framesJustRead; } + + // If we weren't able to read all the frames we requested, break. + if (framesJustRead < framesToReadRightNow) { + if (!internalSound->looping) { + internalSound->playing = MAL_FALSE; + internalSound->frameCursorPos = 0; + break; + } else { + // Should never get here, but just for safety, move the cursor position back to the start and continue the loop. + internalSound->frameCursorPos = 0; + continue; + } + } + } + + // If for some reason we weren't able to read every frame we'll need to break from the loop. Not doing this could + // theoretically put us into an infinite loop. + if (framesToRead > 0) { + break; } } } @@ -427,17 +464,9 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC mal_uint32 framesJustRead = mal_dsp_read_frames(&internalData->dsp, framesToReadRightNow, tempBuffer); if (framesJustRead > 0) { - // This is where the real mixing takes place. This can be optimized. This assumes the device and sound are of the same format. - // - // TODO: Implement pitching. - for (mal_uint32 iFrame = 0; iFrame < framesToRead; ++iFrame) { - float* pFrameOut = pFramesOutF + ((framesRead+iFrame) * device.channels); - float* pFrameIn = tempBuffer + (iFrame * device.channels); - - for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { - pFrameOut[iChannel] += pFrameIn[iChannel] * masterVolume * internalData->volume; - } - } + float* framesOut = framesOutF + (framesRead * device.channels); + float* framesIn = tempBuffer; + MixFrames(framesOut, framesIn, framesJustRead, internalData->volume); framesToRead -= framesJustRead; framesRead += framesJustRead; @@ -667,6 +696,39 @@ Sound LoadSound(const char *fileName) return sound; } +#if USE_MINI_AL +static mal_uint32 Sound_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + SoundData* internalData = (SoundData*)pUserData; + + mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(internalData->dsp.config.formatIn)*internalData->dsp.config.channelsIn; + + // Just keep reading as much as we can. Do not zero fill excess data in the output buffer. + mal_uint32 framesRead = 0; + while (framesRead < frameCount) + { + mal_uint32 framesRemaining = internalData->bufferSizeInFrames - internalData->frameCursorPos; + mal_uint32 framesToRead = (frameCount - framesRead); + if (framesToRead > framesRemaining) { + framesToRead = framesRemaining; + } + + memcpy((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), internalData->data + (internalData->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); + internalData->frameCursorPos += framesToRead; + framesRead += framesToRead; + + // If we've reached the end of the buffer but we're not looping, return. + if (framesToRead == framesRemaining) { + if (!internalData->looping) { + break; + } + } + } + + return framesRead; +} +#endif + // Load sound from wave data // NOTE: Wave data must be unallocated manually Sound LoadSoundFromWave(Wave wave) @@ -702,16 +764,28 @@ Sound LoadSoundFromWave(Wave wave) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed."); } - internalSound->format = DEVICE_FORMAT; - internalSound->channels = DEVICE_CHANNELS; - internalSound->sampleRate = DEVICE_SAMPLE_RATE; - internalSound->frameCount = frameCount; - internalSound->frameCursorPos = 0; + // We run audio data through a sample rate converter in order to support pitch shift. By default this will use an optimized passthrough + // algorithm, but when the application changes the pitch it will change to a less optimal linear SRC. + mal_dsp_config dspConfig; + memset(&dspConfig, 0, sizeof(dspConfig)); + dspConfig.formatIn = DEVICE_FORMAT; + dspConfig.formatOut = DEVICE_FORMAT; + dspConfig.channelsIn = DEVICE_CHANNELS; + dspConfig.channelsOut = DEVICE_CHANNELS; + dspConfig.sampleRateIn = DEVICE_SAMPLE_RATE; + dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE; + mal_result resultMAL = mal_dsp_init(&dspConfig, Sound_OnDSPRead, internalSound, &internalSound->dsp); + if (resultMAL != MAL_SUCCESS) { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create data conversion pipeline"); + } + internalSound->volume = 1; internalSound->pitch = 1; internalSound->playing = 0; internalSound->paused = 0; internalSound->looping = 0; + internalSound->bufferSizeInFrames = frameCount; + internalSound->frameCursorPos = 0; AppendSound(internalSound); sound.handle = (void*)internalSound; @@ -816,9 +890,8 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) internalSound->paused = false; internalSound->frameCursorPos = 0; - // TODO: May want to lock/unlock this since this data buffer is read at mixing time. However, this puts a mutex in - // in the mixing code which makes it no longer real-time. This is likely not a critical issue for this project, though. - memcpy(internalSound->data, data, samplesCount*internalSound->channels*mal_get_sample_size_in_bytes(internalSound->format)); + // TODO: May want to lock/unlock this since this data buffer is read at mixing time. + memcpy(internalSound->data, data, samplesCount*internalSound->dsp.config.channelsIn*mal_get_sample_size_in_bytes(internalSound->dsp.config.formatIn)); #else ALint sampleRate, sampleSize, channels; alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); @@ -986,6 +1059,11 @@ void SetSoundPitch(Sound sound, float pitch) } internalSound->pitch = pitch; + + // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches + // will make the sound faster; lower pitches make it slower. + mal_uint32 newOutputSampleRate = (mal_uint32)((((float)internalSound->dsp.config.sampleRateOut / (float)internalSound->dsp.config.sampleRateIn) / pitch) * internalSound->dsp.config.sampleRateIn); + mal_dsp_set_output_sample_rate(&internalSound->dsp, newOutputSampleRate); #else alSourcef(sound.source, AL_PITCH, pitch); #endif @@ -2013,7 +2091,18 @@ void SetAudioStreamPitch(AudioStream stream, float pitch) return; } + if (pitch == 0) + { + TraceLog(LOG_ERROR, "Attempting to set pitch to 0"); + return; + } + internalData->pitch = pitch; + + // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches + // will make the sound faster; lower pitches make it slower. + mal_uint32 newOutputSampleRate = (mal_uint32)((((float)internalData->dsp.config.sampleRateOut / (float)internalData->dsp.config.sampleRateIn) / pitch) * internalData->dsp.config.sampleRateIn); + mal_dsp_set_output_sample_rate(&internalData->dsp, newOutputSampleRate); #else alSourcef(stream.source, AL_PITCH, pitch); #endif diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 58f542cf..fad9952f 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -570,7 +570,6 @@ struct mal_src mal_src_config config; mal_src_read_proc onRead; void* pUserData; - float ratio; float bin[256]; mal_src_cache cache; // <-- For simplifying and optimizing client -> memory reading. @@ -1353,6 +1352,12 @@ static inline mal_device_config mal_device_config_init_playback(mal_format forma // Initializes a sample rate conversion object. mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC); +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut); + // Reads a number of frames. // // Returns the number of frames actually read. @@ -1376,6 +1381,12 @@ mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pF // Initializes a DSP object. mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* pUserData, mal_dsp* pDSP); +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); + // Reads a number of frames and runs them through the DSP processor. // // This this _not_ flush the internal buffers which means you may end up with a few less frames than you may expect. Look at @@ -9313,21 +9324,27 @@ mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pSRC->onRead = onRead; pSRC->pUserData = pUserData; - // If the in and out sample rates are the same, fall back to the passthrough algorithm. - if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) { - pSRC->config.algorithm = mal_src_algorithm_none; - } - if (pSRC->config.cacheSizeInFrames > MAL_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) { pSRC->config.cacheSizeInFrames = MAL_SRC_CACHE_SIZE_IN_FRAMES; } - pSRC->ratio = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; - mal_src_cache_init(pSRC, &pSRC->cache); return MAL_SUCCESS; } +mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut) +{ + if (pSRC == NULL) return MAL_INVALID_ARGS; + + // Must have a sample rate of > 0. + if (sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + pSRC->config.sampleRateOut = sampleRateOut; + return MAL_SUCCESS; +} + mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut) { return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE); @@ -9337,6 +9354,13 @@ mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pF { if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; + mal_src_algorithm algorithm = pSRC->config.algorithm; + + // Always use passthrough if the sample rates are the same. + if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) { + algorithm = mal_src_algorithm_none; + } + // Could just use a function pointer instead of a switch for this... switch (pSRC->config.algorithm) { @@ -9408,7 +9432,7 @@ mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void pSRC->linear.isNextFramesLoaded = MAL_TRUE; } - float factor = pSRC->ratio; + float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; mal_uint32 totalFramesRead = 0; while (frameCount > 0) { @@ -9995,6 +10019,57 @@ mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* return MAL_SUCCESS; } +mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut) +{ + if (pDSP == NULL) return MAL_INVALID_ARGS; + + // Must have a sample rate of > 0. + if (sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + pDSP->config.sampleRateOut = sampleRateOut; + + // If we already have an SRC pipeline initialized we do _not_ want to re-create it. Instead we adjust it. If we didn't previously + // have an SRC pipeline in place we'll need to initialize it. + if (pDSP->isSRCRequired) { + if (pDSP->config.sampleRateIn != pDSP->config.sampleRateOut) { + mal_src_set_output_sample_rate(&pDSP->src, sampleRateOut); + } else { + pDSP->isSRCRequired = MAL_FALSE; + } + } else { + // We may need a new SRC pipeline. + if (pDSP->config.sampleRateIn != pDSP->config.sampleRateOut) { + pDSP->isSRCRequired = MAL_TRUE; + + mal_src_config srcConfig; + srcConfig.sampleRateIn = pDSP->config.sampleRateIn; + srcConfig.sampleRateOut = pDSP->config.sampleRateOut; + srcConfig.formatIn = pDSP->config.formatIn; + srcConfig.formatOut = mal_format_f32; + srcConfig.channels = pDSP->config.channelsIn; + srcConfig.algorithm = mal_src_algorithm_linear; + srcConfig.cacheSizeInFrames = pDSP->config.cacheSizeInFrames; + mal_result result = mal_src_init(&srcConfig, mal_dsp__src_on_read, pDSP, &pDSP->src); + if (result != MAL_SUCCESS) { + return result; + } + } else { + pDSP->isSRCRequired = MAL_FALSE; + } + } + + // Update whether or not the pipeline is a passthrough. + if (pDSP->config.formatIn == pDSP->config.formatOut && pDSP->config.channelsIn == pDSP->config.channelsOut && pDSP->config.sampleRateIn == pDSP->config.sampleRateOut && !pDSP->isChannelMappingRequired) { + pDSP->isPassthrough = MAL_TRUE; + } else { + pDSP->isPassthrough = MAL_FALSE; + } + + return MAL_SUCCESS; +} + mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut) { return mal_dsp_read_frames_ex(pDSP, frameCount, pFramesOut, MAL_FALSE); -- cgit v1.2.3 From 60d7215b2af12614e0dd99b30ac4330168100fe8 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 18 Nov 2017 08:42:14 +1000 Subject: mini_al: Unify the buffer system for Sounds and AudioStreams. --- src/audio.c | 832 ++++++++++++++++++++++++++--------------------------------- src/raylib.h | 4 +- 2 files changed, 369 insertions(+), 467 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index d20380d0..83538f06 100644 --- a/src/audio.c +++ b/src/audio.c @@ -213,126 +213,75 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo #define DEVICE_CHANNELS 2 #define DEVICE_SAMPLE_RATE 44100 -typedef struct SoundData SoundData; -struct SoundData -{ - mal_dsp dsp; // Necessary for pitch shift. This is an optimized passthrough when the pitch == 1. - float volume; - float pitch; - bool playing; - bool paused; - bool looping; - unsigned int frameCursorPos; // Keeps track of the next frame to read when mixing - unsigned int bufferSizeInFrames; - SoundData* next; - SoundData* prev; - unsigned char data[1]; // Raw audio data. -}; +typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage; -// AudioStreamData -typedef struct AudioStreamData AudioStreamData; -struct AudioStreamData +typedef struct AudioBuffer AudioBuffer; +struct AudioBuffer { - mal_dsp dsp; // AudioStream data needs to flow through a persistent conversion pipeline. Not doing this will result in glitches between buffer updates. + mal_dsp dsp; // For format conversion. float volume; float pitch; bool playing; bool paused; + bool looping; // Always true for AudioStreams. + AudioBufferUsage usage; // Slightly different logic is used when feeding data to the playback device depending on whether or not data is streamed. bool isSubBufferProcessed[2]; unsigned int frameCursorPos; unsigned int bufferSizeInFrames; - AudioStreamData* next; - AudioStreamData* prev; + AudioBuffer* next; + AudioBuffer* prev; unsigned char buffer[1]; }; +void StopAudioBuffer(AudioBuffer* audioBuffer); + + static mal_context context; static mal_device device; static mal_bool32 isAudioInitialized = MAL_FALSE; static float masterVolume = 1; static mal_mutex soundLock; -static SoundData* firstSound; // Sounds are tracked in a linked list. -static SoundData* lastSound; -static AudioStreamData* firstAudioStream; -static AudioStreamData* lastAudioStream; - -static void AppendSound(SoundData* internalSound) -{ - mal_mutex_lock(&soundLock); - { - if (firstSound == NULL) { - firstSound = internalSound; - } else { - lastSound->next = internalSound; - internalSound->prev = lastSound; - } - - lastSound = internalSound; - } - mal_mutex_unlock(&soundLock); -} - -static void RemoveSound(SoundData* internalSound) -{ - mal_mutex_lock(&soundLock); - { - if (internalSound->prev == NULL) { - firstSound = internalSound->next; - } else { - internalSound->prev->next = internalSound->next; - } - - if (internalSound->next == NULL) { - lastSound = internalSound->prev; - } else { - internalSound->next->prev = internalSound->prev; - } +static AudioBuffer* firstAudioBuffer = NULL; // Audio buffers are tracked in a linked list. +static AudioBuffer* lastAudioBuffer = NULL; - internalSound->prev = NULL; - internalSound->next = NULL; - } - mal_mutex_unlock(&soundLock); -} - -static void AppendAudioStream(AudioStreamData* internalAudioStream) +static void TrackAudioBuffer(AudioBuffer* audioBuffer) { mal_mutex_lock(&soundLock); { - if (firstAudioStream == NULL) { - firstAudioStream = internalAudioStream; + if (firstAudioBuffer == NULL) { + firstAudioBuffer = audioBuffer; } else { - lastAudioStream->next = internalAudioStream; - internalAudioStream->prev = lastAudioStream; + lastAudioBuffer->next = audioBuffer; + audioBuffer->prev = lastAudioBuffer; } - lastAudioStream = internalAudioStream; + lastAudioBuffer = audioBuffer; } mal_mutex_unlock(&soundLock); } -static void RemoveAudioStream(AudioStreamData* internalAudioStream) +static void UntrackAudioBuffer(AudioBuffer* audioBuffer) { mal_mutex_lock(&soundLock); { - if (internalAudioStream->prev == NULL) { - firstAudioStream = internalAudioStream->next; + if (audioBuffer->prev == NULL) { + firstAudioBuffer = audioBuffer->next; } else { - internalAudioStream->prev->next = internalAudioStream->next; + audioBuffer->prev->next = audioBuffer->next; } - if (internalAudioStream->next == NULL) { - lastAudioStream = internalAudioStream->prev; + if (audioBuffer->next == NULL) { + lastAudioBuffer = audioBuffer->prev; } else { - internalAudioStream->next->prev = internalAudioStream->prev; + audioBuffer->next->prev = audioBuffer->prev; } - internalAudioStream->prev = NULL; - internalAudioStream->next = NULL; + audioBuffer->prev = NULL; + audioBuffer->next = NULL; } mal_mutex_unlock(&soundLock); } - static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* message) { (void)pContext; @@ -347,8 +296,8 @@ static void MixFrames(float* framesOut, const float* framesIn, mal_uint32 frameC { for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { - float* frameOut = framesOut + (iFrame * device.channels); - float* frameIn = framesIn + (iFrame * device.channels); + float* frameOut = framesOut + (iFrame * device.channels); + const float* frameIn = framesIn + (iFrame * device.channels); frameOut[iChannel] += frameIn[iChannel] * masterVolume * localVolume; } @@ -367,27 +316,24 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC // want to consider how you might want to avoid this. mal_mutex_lock(&soundLock); { - float* framesOutF = (float*)pFramesOut; // <-- Just for convenience. - - // Sounds. - for (SoundData* internalSound = firstSound; internalSound != NULL; internalSound = internalSound->next) + for (AudioBuffer* audioBuffer = firstAudioBuffer; audioBuffer != NULL; audioBuffer = audioBuffer->next) { // Ignore stopped or paused sounds. - if (!internalSound->playing || internalSound->paused) { + if (!audioBuffer->playing || audioBuffer->paused) { continue; } mal_uint32 framesRead = 0; for (;;) { if (framesRead > frameCount) { - TraceLog(LOG_DEBUG, "Mixed too many frames from sound"); + TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer"); break; } if (framesRead == frameCount) { break; } - // Just read as much data we can from the stream. + // Just read as much data as we can from the stream. mal_uint32 framesToRead = (frameCount - framesRead); while (framesToRead > 0) { float tempBuffer[1024]; // 512 frames for stereo. @@ -399,13 +345,13 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC // If we're not looping, we need to make sure we flush the internal buffers of the DSP pipeline to ensure we get the // last few samples. - mal_bool32 flushDSP = !internalSound->looping; + mal_bool32 flushDSP = !audioBuffer->looping; - mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&internalSound->dsp, framesToReadRightNow, tempBuffer, flushDSP); + mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, flushDSP); if (framesJustRead > 0) { - float* framesOut = framesOutF + (framesRead * device.channels); + float* framesOut = (float*)pFramesOut + (framesRead * device.channels); float* framesIn = tempBuffer; - MixFrames(framesOut, framesIn, framesJustRead, internalSound->volume); + MixFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume); framesToRead -= framesJustRead; framesRead += framesJustRead; @@ -413,13 +359,12 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC // If we weren't able to read all the frames we requested, break. if (framesJustRead < framesToReadRightNow) { - if (!internalSound->looping) { - internalSound->playing = MAL_FALSE; - internalSound->frameCursorPos = 0; + if (!audioBuffer->looping) { + StopAudioBuffer(audioBuffer); break; } else { // Should never get here, but just for safety, move the cursor position back to the start and continue the loop. - internalSound->frameCursorPos = 0; + audioBuffer->frameCursorPos = 0; continue; } } @@ -432,56 +377,6 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC } } } - - // AudioStreams. These are handled slightly differently to sounds because we do data conversion at mixing time rather than - // load time. - for (AudioStreamData* internalData = firstAudioStream; internalData != NULL; internalData = internalData->next) - { - // Ignore stopped or paused streams. - if (!internalData->playing || internalData->paused) { - continue; - } - - mal_uint32 framesRead = 0; - for (;;) { - if (framesRead > frameCount) { - TraceLog(LOG_DEBUG, "Mixed too many frames from sound"); - break; - } - if (framesRead == frameCount) { - break; - } - - // Just read as much data we can from the stream. - mal_uint32 framesToRead = (frameCount - framesRead); - while (framesToRead > 0) { - float tempBuffer[1024]; // 512 frames for stereo. - - mal_uint32 framesToReadRightNow = framesToRead; - if (framesToReadRightNow > sizeof(tempBuffer)/DEVICE_CHANNELS) { - framesToReadRightNow = sizeof(tempBuffer)/DEVICE_CHANNELS; - } - - mal_uint32 framesJustRead = mal_dsp_read_frames(&internalData->dsp, framesToReadRightNow, tempBuffer); - if (framesJustRead > 0) { - float* framesOut = framesOutF + (framesRead * device.channels); - float* framesIn = tempBuffer; - MixFrames(framesOut, framesIn, framesJustRead, internalData->volume); - - framesToRead -= framesJustRead; - framesRead += framesJustRead; - } else { - break; // Avoid an infinite loop. - } - } - - // If for some reason we weren't able to read every frame we'll need to break from the loop. Not doing this could - // theoretically put us into an infinite loop. - if (framesToRead > 0) { - break; - } - } - } } mal_mutex_unlock(&soundLock); @@ -630,6 +525,262 @@ void SetMasterVolume(float volume) #endif } + +//---------------------------------------------------------------------------------- +// Audio Buffer +//---------------------------------------------------------------------------------- +#if USE_MINI_AL +static mal_uint32 AudioBuffer_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + AudioBuffer* audioBuffer = (AudioBuffer*)pUserData; + + mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames / 2; + mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos / subBufferSizeInFrames; + if (currentSubBufferIndex > 1) { + TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); + return 0; + } + + // Another thread can update the processed state of buffers so we just take a copy here to try and avoid potential synchronization problems. + bool isSubBufferProcessed[2]; + isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; + isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; + + mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn) * audioBuffer->dsp.config.channelsIn; + + // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. + mal_uint32 framesRead = 0; + for (;;) + { + // We break from this loop differently depending on the buffer's usage. For static buffers, we simply fill as much data as we can. For + // streaming buffers we only fill the halves of the buffer that are processed. Unprocessed halves must keep their audio data in-tact. + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { + if (framesRead >= frameCount) { + break; + } + } else { + if (isSubBufferProcessed[currentSubBufferIndex]) { + break; + } + } + + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining == 0) { + break; + } + + mal_uint32 framesRemainingInOutputBuffer; + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { + framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos; + } else { + mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; + framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); + } + + + + mal_uint32 framesToRead = totalFramesRemaining; + if (framesToRead > framesRemainingInOutputBuffer) { + framesToRead = framesRemainingInOutputBuffer; + } + + memcpy((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); + audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead) % audioBuffer->bufferSizeInFrames; + framesRead += framesToRead; + + // If we've read to the end of the buffer, mark it as processed. + if (framesToRead == framesRemainingInOutputBuffer) { + audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; + isSubBufferProcessed[currentSubBufferIndex] = true; + + currentSubBufferIndex = (currentSubBufferIndex + 1) % 2; + + // We need to break from this loop if we're not looping. + if (!audioBuffer->looping) { + StopAudioBuffer(audioBuffer); + break; + } + } + } + + // Zero-fill excess. + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining > 0) { + memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); + + // For static buffers we can fill the remaining frames with silence for safety, but we don't want + // to report those frames as "read". The reason for this is that the caller uses the return value + // to know whether or not a non-looping sound has finished playback. + if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) { + framesRead += totalFramesRemaining; + } + } + + return framesRead; +} + +// Create a new audio buffer. Initially filled with silence. +AudioBuffer* CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) +{ + AudioBuffer* audioBuffer = (AudioBuffer*)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*mal_get_sample_size_in_bytes(format)), 1); + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to allocate memory for audio buffer"); + return NULL; + } + + // We run audio data through a format converter. + mal_dsp_config dspConfig; + memset(&dspConfig, 0, sizeof(dspConfig)); + dspConfig.formatIn = format; + dspConfig.formatOut = DEVICE_FORMAT; + dspConfig.channelsIn = channels; + dspConfig.channelsOut = DEVICE_CHANNELS; + dspConfig.sampleRateIn = sampleRate; + dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE; + mal_result resultMAL = mal_dsp_init(&dspConfig, AudioBuffer_OnDSPRead, audioBuffer, &audioBuffer->dsp); + if (resultMAL != MAL_SUCCESS) { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create data conversion pipeline"); + free(audioBuffer); + return NULL; + } + + audioBuffer->volume = 1; + audioBuffer->pitch = 1; + audioBuffer->playing = 0; + audioBuffer->paused = 0; + audioBuffer->looping = 0; + audioBuffer->usage = usage; + audioBuffer->bufferSizeInFrames = bufferSizeInFrames; + audioBuffer->frameCursorPos = 0; + + // Buffers should be marked as processed by default so that a call to UpdateAudioStream() immediately after initialization works correctly. + audioBuffer->isSubBufferProcessed[0] = true; + audioBuffer->isSubBufferProcessed[1] = true; + + TrackAudioBuffer(audioBuffer); + + return audioBuffer; +} + +// Delete an audio buffer. +void DeleteAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + UntrackAudioBuffer(audioBuffer); + free(audioBuffer); +} + +// Check if an audio buffer is playing. +bool IsAudioBufferPlaying(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return false; + } + + return audioBuffer->playing && !audioBuffer->paused; +} + +// Play an audio buffer. +// +// This will restart the buffer from the start. Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position +// should be maintained. +void PlayAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->playing = true; + audioBuffer->paused = false; + audioBuffer->frameCursorPos = 0; +} + +// Stop an audio buffer. +void StopAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + // Don't do anything if the audio buffer is already stopped. + if (!IsAudioBufferPlaying(audioBuffer)) + { + return; + } + + audioBuffer->playing = false; + audioBuffer->paused = false; + audioBuffer->frameCursorPos = 0; + audioBuffer->isSubBufferProcessed[0] = true; + audioBuffer->isSubBufferProcessed[1] = true; +} + +// Pause an audio buffer. +void PauseAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->paused = true; +} + +// Resume an audio buffer. +void ResumeAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->paused = false; +} + +// Set volume for an audio buffer. +void SetAudioBufferVolume(AudioBuffer* audioBuffer, float volume) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->volume = volume; +} + +// Set pitch for an audio buffer. +void SetAudioBufferPitch(AudioBuffer* audioBuffer, float pitch) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->pitch = pitch; + + // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches + // will make the sound faster; lower pitches make it slower. + mal_uint32 newOutputSampleRate = (mal_uint32)((((float)audioBuffer->dsp.config.sampleRateOut / (float)audioBuffer->dsp.config.sampleRateIn) / pitch) * audioBuffer->dsp.config.sampleRateIn); + mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate); +} +#endif + //---------------------------------------------------------------------------------- // Module Functions Definition - Sounds loading and playing (.WAV) //---------------------------------------------------------------------------------- @@ -696,39 +847,6 @@ Sound LoadSound(const char *fileName) return sound; } -#if USE_MINI_AL -static mal_uint32 Sound_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) -{ - SoundData* internalData = (SoundData*)pUserData; - - mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(internalData->dsp.config.formatIn)*internalData->dsp.config.channelsIn; - - // Just keep reading as much as we can. Do not zero fill excess data in the output buffer. - mal_uint32 framesRead = 0; - while (framesRead < frameCount) - { - mal_uint32 framesRemaining = internalData->bufferSizeInFrames - internalData->frameCursorPos; - mal_uint32 framesToRead = (frameCount - framesRead); - if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; - } - - memcpy((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), internalData->data + (internalData->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); - internalData->frameCursorPos += framesToRead; - framesRead += framesToRead; - - // If we've reached the end of the buffer but we're not looping, return. - if (framesToRead == framesRemaining) { - if (!internalData->looping) { - break; - } - } - } - - return framesRead; -} -#endif - // Load sound from wave data // NOTE: Wave data must be unallocated manually Sound LoadSoundFromWave(Wave wave) @@ -751,44 +869,24 @@ Sound LoadSoundFromWave(Wave wave) mal_uint32 frameCount = mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn); if (frameCount == 0) { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion."); + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion"); } - SoundData* internalSound = (SoundData*)calloc(sizeof(*internalSound) + (frameCount*DEVICE_CHANNELS*4), 1); // <-- Make sure this is initialized to zero for safety. - if (internalSound == NULL) { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to allocate memory for internal buffer"); - } - frameCount = mal_convert_frames(internalSound->data, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); - if (frameCount == 0) { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed."); + AudioBuffer* audioBuffer = CreateAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC); + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create audio buffer"); } - // We run audio data through a sample rate converter in order to support pitch shift. By default this will use an optimized passthrough - // algorithm, but when the application changes the pitch it will change to a less optimal linear SRC. - mal_dsp_config dspConfig; - memset(&dspConfig, 0, sizeof(dspConfig)); - dspConfig.formatIn = DEVICE_FORMAT; - dspConfig.formatOut = DEVICE_FORMAT; - dspConfig.channelsIn = DEVICE_CHANNELS; - dspConfig.channelsOut = DEVICE_CHANNELS; - dspConfig.sampleRateIn = DEVICE_SAMPLE_RATE; - dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE; - mal_result resultMAL = mal_dsp_init(&dspConfig, Sound_OnDSPRead, internalSound, &internalSound->dsp); - if (resultMAL != MAL_SUCCESS) { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create data conversion pipeline"); - } - internalSound->volume = 1; - internalSound->pitch = 1; - internalSound->playing = 0; - internalSound->paused = 0; - internalSound->looping = 0; - internalSound->bufferSizeInFrames = frameCount; - internalSound->frameCursorPos = 0; - AppendSound(internalSound); + frameCount = mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.config.formatIn, audioBuffer->dsp.config.channelsIn, audioBuffer->dsp.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); + if (frameCount == 0) + { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed"); + } - sound.handle = (void*)internalSound; + sound.audioBuffer = audioBuffer; #else ALenum format = 0; @@ -861,9 +959,7 @@ void UnloadWave(Wave wave) void UnloadSound(Sound sound) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - RemoveSound(internalSound); - free(internalSound); + DeleteAudioBuffer((AudioBuffer*)sound.audioBuffer); #else alSourceStop(sound.source); @@ -879,19 +975,17 @@ void UnloadSound(Sound sound) void UpdateSound(Sound sound, const void *data, int samplesCount) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) + AudioBuffer* audioBuffer = (AudioBuffer*)sound.audioBuffer; + if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound"); + TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); return; } - internalSound->playing = false; - internalSound->paused = false; - internalSound->frameCursorPos = 0; + StopAudioBuffer(audioBuffer); // TODO: May want to lock/unlock this since this data buffer is read at mixing time. - memcpy(internalSound->data, data, samplesCount*internalSound->dsp.config.channelsIn*mal_get_sample_size_in_bytes(internalSound->dsp.config.formatIn)); + memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.config.channelsIn*mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn)); #else ALint sampleRate, sampleSize, channels; alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); @@ -921,16 +1015,7 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) void PlaySound(Sound sound) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) - { - TraceLog(LOG_ERROR, "PlaySound() : Invalid sound"); - return; - } - - internalSound->playing = 1; - internalSound->paused = 0; - internalSound->frameCursorPos = 0; + PlayAudioBuffer((AudioBuffer*)sound.audioBuffer); #else alSourcePlay(sound.source); // Play the sound #endif @@ -955,14 +1040,7 @@ void PlaySound(Sound sound) void PauseSound(Sound sound) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) - { - TraceLog(LOG_ERROR, "PauseSound() : Invalid sound"); - return; - } - - internalSound->paused = true; + PauseAudioBuffer((AudioBuffer*)sound.audioBuffer); #else alSourcePause(sound.source); #endif @@ -972,14 +1050,7 @@ void PauseSound(Sound sound) void ResumeSound(Sound sound) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) - { - TraceLog(LOG_ERROR, "ResumeSound() : Invalid sound"); - return; - } - - internalSound->paused = false; + ResumeAudioBuffer((AudioBuffer*)sound.audioBuffer); #else ALenum state; @@ -993,15 +1064,7 @@ void ResumeSound(Sound sound) void StopSound(Sound sound) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) - { - TraceLog(LOG_ERROR, "StopSound() : Invalid sound"); - return; - } - - internalSound->playing = false; - internalSound->paused = false; + StopAudioBuffer((AudioBuffer*)sound.audioBuffer); #else alSourceStop(sound.source); #endif @@ -1011,14 +1074,7 @@ void StopSound(Sound sound) bool IsSoundPlaying(Sound sound) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) - { - TraceLog(LOG_ERROR, "IsSoundPlaying() : Invalid sound"); - return false; - } - - return internalSound->playing && !internalSound->paused; + return IsAudioBufferPlaying((AudioBuffer*)sound.audioBuffer); #else bool playing = false; ALint state; @@ -1034,14 +1090,7 @@ bool IsSoundPlaying(Sound sound) void SetSoundVolume(Sound sound, float volume) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) - { - TraceLog(LOG_ERROR, "SetSoundVolume() : Invalid sound"); - return; - } - - internalSound->volume = volume; + SetAudioBufferVolume((AudioBuffer*)sound.audioBuffer, volume); #else alSourcef(sound.source, AL_GAIN, volume); #endif @@ -1051,19 +1100,7 @@ void SetSoundVolume(Sound sound, float volume) void SetSoundPitch(Sound sound, float pitch) { #if USE_MINI_AL - SoundData* internalSound = (SoundData*)sound.handle; - if (internalSound == NULL) - { - TraceLog(LOG_ERROR, "SetSoundPitch() : Invalid sound"); - return; - } - - internalSound->pitch = pitch; - - // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches - // will make the sound faster; lower pitches make it slower. - mal_uint32 newOutputSampleRate = (mal_uint32)((((float)internalSound->dsp.config.sampleRateOut / (float)internalSound->dsp.config.sampleRateIn) / pitch) * internalSound->dsp.config.sampleRateIn); - mal_dsp_set_output_sample_rate(&internalSound->dsp, newOutputSampleRate); + SetAudioBufferPitch((AudioBuffer*)sound.audioBuffer, pitch); #else alSourcef(sound.source, AL_PITCH, pitch); #endif @@ -1361,7 +1398,22 @@ void UnloadMusicStream(Music music) void PlayMusicStream(Music music) { #if USE_MINI_AL - PlayAudioStream(music->stream); + AudioBuffer* audioBuffer = (AudioBuffer*)music->stream.audioBuffer; + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); + return; + } + + // For music streams, we need to make sure we maintain the frame cursor position. This is hack for this section of code in UpdateMusicStream() + // // NOTE: In case window is minimized, music stream is stopped, + // // just make sure to play again on window restore + // if (IsMusicPlaying(music)) PlayMusicStream(music); + mal_uint32 frameCursorPos = audioBuffer->frameCursorPos; + { + PlayAudioStream(music->stream); // <-- This resets the cursor position. + } + audioBuffer->frameCursorPos = frameCursorPos; #else alSourcePlay(music->stream.source); #endif @@ -1672,65 +1724,6 @@ float GetMusicTimePlayed(Music music) return secondsPlayed; } -#if USE_MINI_AL -static mal_uint32 UpdateAudioStream_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) -{ - AudioStreamData* internalData = (AudioStreamData*)pUserData; - - mal_uint32 subBufferSizeInFrames = AUDIO_BUFFER_SIZE; - mal_uint32 currentSubBufferIndex = internalData->frameCursorPos / subBufferSizeInFrames; - if (currentSubBufferIndex > 1) { - TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); - return 0; - } - - // Another thread can update the processed state of buffers so we just take a copy here to try and avoid potential synchronization problems. - bool isSubBufferProcessed[2]; - isSubBufferProcessed[0] = internalData->isSubBufferProcessed[0]; - isSubBufferProcessed[1] = internalData->isSubBufferProcessed[1]; - - mal_uint32 channels = internalData->dsp.config.channelsIn; - mal_uint32 sampleSizeInBytes = mal_get_sample_size_in_bytes(internalData->dsp.config.formatIn); - mal_uint32 frameSizeInBytes = sampleSizeInBytes*channels; - - // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. - mal_uint32 framesRead = 0; - while (!isSubBufferProcessed[currentSubBufferIndex]) - { - mal_uint32 totalFramesRemaining = (frameCount - framesRead); - if (totalFramesRemaining == 0) { - break; - } - - mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; - mal_uint32 framesRemainingInThisSubBuffer = subBufferSizeInFrames - (internalData->frameCursorPos - firstFrameIndexOfThisSubBuffer); - - mal_uint32 framesToRead = totalFramesRemaining; - if (framesToRead > framesRemainingInThisSubBuffer) { - framesToRead = framesRemainingInThisSubBuffer; - } - - memcpy((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), internalData->buffer + (internalData->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); - - framesRead += framesToRead; - internalData->frameCursorPos = (internalData->frameCursorPos + framesToRead) % internalData->bufferSizeInFrames; - - // If we've read to the end of the buffer, mark it as processed. - if (framesToRead == framesRemainingInThisSubBuffer) { - internalData->isSubBufferProcessed[currentSubBufferIndex] = true; - currentSubBufferIndex = (currentSubBufferIndex + 1) % 2; - } - } - - // Zero-fill excess. - mal_uint32 totalFramesRemaining = (frameCount - framesRead); - if (totalFramesRemaining > 0) { - memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); - } - - return frameCount; -} -#endif // Init audio stream (to stream audio pcm data) AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) @@ -1750,38 +1743,17 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)calloc(1, sizeof(*internalData) + (AUDIO_BUFFER_SIZE*2 * stream.channels*(stream.sampleSize/8))); - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Failed to allocate buffer for audio stream"); - return stream; - } + mal_format formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); - mal_dsp_config config; - memset(&config, 0, sizeof(config)); - config.formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); - config.channelsIn = stream.channels; - config.sampleRateIn = stream.sampleRate; - config.formatOut = DEVICE_FORMAT; - config.channelsOut = DEVICE_CHANNELS; - config.sampleRateOut = DEVICE_SAMPLE_RATE; - mal_result result = mal_dsp_init(&config, UpdateAudioStream_OnDSPRead, internalData, &internalData->dsp); - if (result != MAL_SUCCESS) + AudioBuffer* audioBuffer = CreateAudioBuffer(formatIn, stream.channels, stream.sampleRate, AUDIO_BUFFER_SIZE*2, AUDIO_BUFFER_USAGE_STREAM); + if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "InitAudioStream() : Failed to initialize data conversion pipeline"); - free(internalData); + TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer"); return stream; } - // Buffers should be marked as processed by default so that a call to UpdateAudioStream() immediately after initialization works correctly. - internalData->isSubBufferProcessed[0] = true; - internalData->isSubBufferProcessed[1] = true; - internalData->bufferSizeInFrames = AUDIO_BUFFER_SIZE*2; - internalData->volume = 1; - internalData->pitch = 1; - AppendAudioStream(internalData); - - stream.handle = internalData; + audioBuffer->looping = true; // Always loop for streaming buffers. + stream.audioBuffer = audioBuffer; #else // Setup OpenAL format if (stream.channels == 1) @@ -1838,9 +1810,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un void CloseAudioStream(AudioStream stream) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - RemoveAudioStream(internalData); - free(internalData); + DeleteAudioBuffer((AudioBuffer*)stream.audioBuffer); #else // Stop playing channel alSourceStop(stream.source); @@ -1871,32 +1841,30 @@ void CloseAudioStream(AudioStream stream) void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) + AudioBuffer* audioBuffer = (AudioBuffer*)stream.audioBuffer; + if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "Invalid audio stream"); + TraceLog(LOG_ERROR, "UpdateAudioStream() : No audio buffer"); return; } - // We need to determine which half of the buffer needs updating. If the stream is not started and the cursor position is - // at the front of the buffer, update the first subbuffer. - if (internalData->isSubBufferProcessed[0] || internalData->isSubBufferProcessed[1]) + if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) { mal_uint32 subBufferToUpdate; - if (internalData->isSubBufferProcessed[0] && internalData->isSubBufferProcessed[1]) + if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) { // Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front. subBufferToUpdate = 0; - internalData->frameCursorPos = 0; + audioBuffer->frameCursorPos = 0; } else { // Just update whichever sub-buffer is processed. - subBufferToUpdate = (internalData->isSubBufferProcessed[0]) ? 0 : 1; + subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0]) ? 0 : 1; } - mal_uint32 subBufferSizeInFrames = AUDIO_BUFFER_SIZE; - unsigned char *subBuffer = internalData->buffer + ((subBufferSizeInFrames * stream.channels * (stream.sampleSize/8)) * subBufferToUpdate); + mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; + unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames * stream.channels * (stream.sampleSize/8)) * subBufferToUpdate); // Does this API expect a whole buffer to be updated in one go? Assuming so, but if not will need to change this logic. if (subBufferSizeInFrames >= (mal_uint32)samplesCount) @@ -1915,21 +1883,19 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) memset(subBuffer + bytesToWrite, 0, leftoverFrameCount * stream.channels * (stream.sampleSize/8)); } - internalData->isSubBufferProcessed[subBufferToUpdate] = false; + audioBuffer->isSubBufferProcessed[subBufferToUpdate] = false; } else { - TraceLog(LOG_ERROR, "[AUD ID %i] UpdateAudioStream() : Attempting to write too many frames to buffer"); + TraceLog(LOG_ERROR, "UpdateAudioStream() : Attempting to write too many frames to buffer"); return; } } else { - TraceLog(LOG_ERROR, "[AUD ID %i] Audio buffer not available for updating"); + TraceLog(LOG_ERROR, "Audio buffer not available for updating"); return; } - - #else ALuint buffer = 0; alSourceUnqueueBuffers(stream.source, 1, &buffer); @@ -1948,14 +1914,14 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) bool IsAudioBufferProcessed(AudioStream stream) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) + AudioBuffer* audioBuffer = (AudioBuffer*)stream.audioBuffer; + if (audioBuffer == NULL) { - TraceLog(LOG_ERROR, "Invalid audio stream"); + TraceLog(LOG_ERROR, "IsAudioBufferProcessed() : No audio buffer"); return false; } - return internalData->isSubBufferProcessed[0] || internalData->isSubBufferProcessed[1]; + return audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]; #else ALint processed = 0; @@ -1970,14 +1936,7 @@ bool IsAudioBufferProcessed(AudioStream stream) void PlayAudioStream(AudioStream stream) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Invalid audio stream"); - return; - } - - internalData->playing = true; + PlayAudioBuffer((AudioBuffer*)stream.audioBuffer); #else alSourcePlay(stream.source); #endif @@ -1987,14 +1946,7 @@ void PlayAudioStream(AudioStream stream) void PauseAudioStream(AudioStream stream) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Invalid audio stream"); - return; - } - - internalData->paused = true; + PauseAudioBuffer((AudioBuffer*)stream.audioBuffer); #else alSourcePause(stream.source); #endif @@ -2004,14 +1956,7 @@ void PauseAudioStream(AudioStream stream) void ResumeAudioStream(AudioStream stream) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Invalid audio stream"); - return; - } - - internalData->paused = false; + ResumeAudioBuffer((AudioBuffer*)stream.audioBuffer); #else ALenum state; alGetSourcei(stream.source, AL_SOURCE_STATE, &state); @@ -2024,14 +1969,7 @@ void ResumeAudioStream(AudioStream stream) bool IsAudioStreamPlaying(AudioStream stream) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Invalid audio stream"); - return false; - } - - return internalData->playing; + return IsAudioBufferPlaying((AudioBuffer*)stream.audioBuffer); #else bool playing = false; ALint state; @@ -2048,18 +1986,7 @@ bool IsAudioStreamPlaying(AudioStream stream) void StopAudioStream(AudioStream stream) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Invalid audio stream"); - return; - } - - internalData->playing = 0; - internalData->paused = 0; - internalData->frameCursorPos = 0; - internalData->isSubBufferProcessed[0] = true; - internalData->isSubBufferProcessed[1] = true; + StopAudioBuffer((AudioBuffer*)stream.audioBuffer); #else alSourceStop(stream.source); #endif @@ -2068,14 +1995,7 @@ void StopAudioStream(AudioStream stream) void SetAudioStreamVolume(AudioStream stream, float volume) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Invalid audio stream"); - return; - } - - internalData->volume = volume; + SetAudioBufferVolume((AudioBuffer*)stream.audioBuffer, volume); #else alSourcef(stream.source, AL_GAIN, volume); #endif @@ -2084,25 +2004,7 @@ void SetAudioStreamVolume(AudioStream stream, float volume) void SetAudioStreamPitch(AudioStream stream, float pitch) { #if USE_MINI_AL - AudioStreamData* internalData = (AudioStreamData*)stream.handle; - if (internalData == NULL) - { - TraceLog(LOG_ERROR, "Invalid audio stream"); - return; - } - - if (pitch == 0) - { - TraceLog(LOG_ERROR, "Attempting to set pitch to 0"); - return; - } - - internalData->pitch = pitch; - - // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches - // will make the sound faster; lower pitches make it slower. - mal_uint32 newOutputSampleRate = (mal_uint32)((((float)internalData->dsp.config.sampleRateOut / (float)internalData->dsp.config.sampleRateIn) / pitch) * internalData->dsp.config.sampleRateIn); - mal_dsp_set_output_sample_rate(&internalData->dsp, newOutputSampleRate); + SetAudioBufferPitch((AudioBuffer*)stream.audioBuffer, pitch); #else alSourcef(stream.source, AL_PITCH, pitch); #endif diff --git a/src/raylib.h b/src/raylib.h index 1f138c2b..331817cf 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -486,7 +486,7 @@ typedef struct Wave { // Sound source type typedef struct Sound { - void* handle; // A pointer to internal data used by the audio system. + void* audioBuffer; // A pointer to internal data used by the audio system. unsigned int source; // OpenAL audio source id unsigned int buffer; // OpenAL audio buffer id @@ -504,7 +504,7 @@ typedef struct AudioStream { unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) unsigned int channels; // Number of channels (1-mono, 2-stereo) - void* handle; // A pointer to internal data used by the audio system. + void* audioBuffer; // A pointer to internal data used by the audio system. int format; // OpenAL audio format specifier unsigned int source; // OpenAL audio source id -- cgit v1.2.3 From b9bb80d004d93aac0fe71b56b0efa71416a2e97e Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 18 Nov 2017 10:27:44 +1000 Subject: Add some logging and update mini_al. --- src/audio.c | 5 +++++ src/external/mini_al.h | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 83538f06..5bff9a0c 100644 --- a/src/audio.c +++ b/src/audio.c @@ -425,6 +425,11 @@ void InitAudioDevice(void) return; } + TraceLog(LOG_INFO, "Audio device initialized successfully"); + TraceLog(LOG_INFO, "Audio backend: %s", mal_get_backend_name(context.backend)); + TraceLog(LOG_INFO, "Audio format: %s", mal_get_format_name(device.format)); + TraceLog(LOG_INFO, "Audio channels: %d", device.channels); + TraceLog(LOG_INFO, "Audio sample rate: %d", device.sampleRate); isAudioInitialized = MAL_TRUE; #else diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 18dd3b40..2dcf3e71 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -195,6 +195,9 @@ extern "C" { #ifdef __ANDROID__ #define MAL_ANDROID #endif + #ifdef __EMSCRIPTEN__ + #define MAL_EMSCRIPTEN + #endif #endif // Some backends are only supported on certain platforms. @@ -226,7 +229,7 @@ extern "C" { #if defined(MAL_ANDROID) #define MAL_SUPPORT_OPENSL #endif - #if !defined(MAL_LINUX) && !defined(MAL_APPLE) && !defined(MAL_ANDROID) + #if !defined(MAL_LINUX) && !defined(MAL_APPLE) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN) #define MAL_SUPPORT_OSS #endif #endif @@ -1435,6 +1438,12 @@ void mal_mutex_unlock(mal_mutex* pMutex); // /////////////////////////////////////////////////////////////////////////////// +// Retrieves a friendly name for a backend. +const char* mal_get_backend_name(mal_backend backend); + +// Retrieves a friendly name for a format. +const char* mal_get_format_name(mal_format format); + // Blends two frames in floating point format. void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels); @@ -10287,6 +10296,39 @@ mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 chann ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* mal_get_backend_name(mal_backend backend) +{ + switch (backend) + { + case mal_backend_null: return "Null"; + case mal_backend_wasapi: return "WASAPI"; + case mal_backend_dsound: return "DirectSound"; + case mal_backend_winmm: return "WinMM"; + case mal_backend_alsa: return "ALSA"; + //case mal_backend_pulse: return "PulseAudio"; + //case mal_backend_jack: return "JACK"; + //case mal_backend_coreaudio: return "Core Audio"; + case mal_backend_oss: return "OSS"; + case mal_backend_opensl: return "OpenSL|ES"; + case mal_backend_openal: return "OpenAL"; + default: return "Unknown"; + } +} + +const char* mal_get_format_name(mal_format format) +{ + switch (format) + { + case mal_format_unknown: return "Unknown"; + case mal_format_u8: return "8-bit Unsigned Integer"; + case mal_format_s16: return "16-bit Signed Integer"; + case mal_format_s24: return "24-bit Signed Integer (Tightly Packed)"; + case mal_format_s32: return "32-bit Signed Integer"; + case mal_format_f32: return "32-bit IEEE Floating Point"; + default: return "Invalid"; + } +} + void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels) { for (mal_uint32 i = 0; i < channels; ++i) { -- cgit v1.2.3 From f9144ac5b0bc59e69d87ee8b67047888873418fc Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 18 Nov 2017 12:15:48 +1000 Subject: Rename a variable for consistency. --- src/audio.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 5bff9a0c..5209cc56 100644 --- a/src/audio.c +++ b/src/audio.c @@ -240,13 +240,13 @@ static mal_context context; static mal_device device; static mal_bool32 isAudioInitialized = MAL_FALSE; static float masterVolume = 1; -static mal_mutex soundLock; +static mal_mutex audioLock; static AudioBuffer* firstAudioBuffer = NULL; // Audio buffers are tracked in a linked list. static AudioBuffer* lastAudioBuffer = NULL; static void TrackAudioBuffer(AudioBuffer* audioBuffer) { - mal_mutex_lock(&soundLock); + mal_mutex_lock(&audioLock); { if (firstAudioBuffer == NULL) { firstAudioBuffer = audioBuffer; @@ -257,12 +257,12 @@ static void TrackAudioBuffer(AudioBuffer* audioBuffer) lastAudioBuffer = audioBuffer; } - mal_mutex_unlock(&soundLock); + mal_mutex_unlock(&audioLock); } static void UntrackAudioBuffer(AudioBuffer* audioBuffer) { - mal_mutex_lock(&soundLock); + mal_mutex_lock(&audioLock); { if (audioBuffer->prev == NULL) { firstAudioBuffer = audioBuffer->next; @@ -279,7 +279,7 @@ static void UntrackAudioBuffer(AudioBuffer* audioBuffer) audioBuffer->prev = NULL; audioBuffer->next = NULL; } - mal_mutex_unlock(&soundLock); + mal_mutex_unlock(&audioLock); } static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* message) @@ -314,7 +314,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC // Using a mutex here for thread-safety which makes things not real-time. This is unlikely to be necessary for this project, but may // want to consider how you might want to avoid this. - mal_mutex_lock(&soundLock); + mal_mutex_lock(&audioLock); { for (AudioBuffer* audioBuffer = firstAudioBuffer; audioBuffer != NULL; audioBuffer = audioBuffer->next) { @@ -378,7 +378,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC } } } - mal_mutex_unlock(&soundLock); + mal_mutex_unlock(&audioLock); return frameCount; // We always output the same number of frames that were originally requested. } @@ -417,7 +417,7 @@ void InitAudioDevice(void) // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. - if (mal_mutex_init(&context, &soundLock) != MAL_SUCCESS) + if (mal_mutex_init(&context, &audioLock) != MAL_SUCCESS) { TraceLog(LOG_ERROR, "Failed to create mutex for audio mixing"); mal_device_uninit(&device); @@ -479,7 +479,7 @@ void CloseAudioDevice(void) return; } - mal_mutex_uninit(&soundLock); + mal_mutex_uninit(&audioLock); mal_device_uninit(&device); mal_context_uninit(&context); #else -- cgit v1.2.3 From 3238cba47c698dd7bbeba35d5b2ec81e9ce0b974 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 19 Nov 2017 16:36:24 +1000 Subject: Log the name of the playback device. --- src/audio.c | 2 +- src/external/mini_al.h | 176 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 171 insertions(+), 7 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 5209cc56..2c2aedac 100644 --- a/src/audio.c +++ b/src/audio.c @@ -425,7 +425,7 @@ void InitAudioDevice(void) return; } - TraceLog(LOG_INFO, "Audio device initialized successfully"); + TraceLog(LOG_INFO, "Audio device initialized successfully: %s", device.name); TraceLog(LOG_INFO, "Audio backend: %s", mal_get_backend_name(context.backend)); TraceLog(LOG_INFO, "Audio format: %s", mal_get_format_name(device.format)); TraceLog(LOG_INFO, "Audio channels: %d", device.channels); diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 0b324a2b..f1d7ffdc 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -1,4 +1,4 @@ -// Mini audio library. Public domain. See "unlicense" statement at the end of this file. +// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. // mini_al - v0.x - 2017-xx-xx // // David Reid - davidreidsoftware@gmail.com @@ -872,13 +872,13 @@ struct mal_context mal_handle hSDL; // SDL mal_proc SDL_InitSubSystem; mal_proc SDL_QuitSubSystem; + mal_proc SDL_CloseAudio; + mal_proc SDL_OpenAudio; + mal_proc SDL_PauseAudio; mal_proc SDL_GetNumAudioDevices; mal_proc SDL_GetAudioDeviceName; - mal_proc SDL_CloseAudio; mal_proc SDL_CloseAudioDevice; - mal_proc SDL_OpenAudio; mal_proc SDL_OpenAudioDevice; - mal_proc SDL_PauseAudio; mal_proc SDL_PauseAudioDevice; mal_bool32 usingSDL1; @@ -944,6 +944,7 @@ struct mal_device mal_send_proc onSend; mal_stop_proc onStop; void* pUserData; // Application defined data. + char name[256]; mal_mutex lock; mal_event wakeupEvent; mal_event startEvent; @@ -1095,6 +1096,7 @@ struct mal_device // - OSS // - OpenSL|ES // - OpenAL +// - SDL // - Null // // The onLog callback is used for posting log messages back to the client for diagnostics, debugging, @@ -1156,8 +1158,8 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma // data. It's tied to the buffer size, so as an example, if your buffer size is equivalent to 10 // milliseconds and you have 2 periods, the CPU will wake up approximately every 5 milliseconds. // -// Consider using mal_device_config_init(), mal_device_config_init_playback(), etc. to make it easier -// to initialize a mal_device_config object. +// Use mal_device_config_init(), mal_device_config_init_playback(), etc. to initialize a +// mal_device_config object. // // When compiling for UWP you must ensure you call this function on the main UI thread because the // operating system may need to present the user with a message asking for permissions. Please refer @@ -1630,6 +1632,8 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form #endif #ifdef MAL_ENABLE_SDL #define MAL_HAS_SDL + + // SDL headers are necessary if using compile-time linking. #ifdef MAL_NO_RUNTIME_LINKING #ifdef __has_include #ifdef MAL_EMSCRIPTEN @@ -2667,6 +2671,140 @@ static inline mal_uint32 mal_device__get_state(mal_device* pDevice) #endif +// Generic function for retrieving the name of a device by it's ID. +// +// This function simply enumerates every device and then retrieves the name of the first device that has the same ID. +static mal_result mal_context__try_get_device_name_by_id(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, char* pName, size_t nameBufferSize) +{ + mal_assert(pContext != NULL); + mal_assert(pName != NULL); + + if (pDeviceID == NULL) { + return MAL_NO_DEVICE; + } + + mal_uint32 deviceCount; + mal_result result = mal_enumerate_devices(pContext, type, &deviceCount, NULL); + if (result != MAL_SUCCESS) { + return result; + } + + mal_device_info* pInfos = (mal_device_info*)mal_malloc(sizeof(*pInfos) * deviceCount); + if (pInfos == NULL) { + return MAL_OUT_OF_MEMORY; + } + + result = mal_enumerate_devices(pContext, type, &deviceCount, pInfos); + if (result != MAL_SUCCESS) { + mal_free(pInfos); + return result; + } + + mal_bool32 found = MAL_FALSE; + for (mal_uint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + // Prefer backend specific comparisons for efficiency and accuracy, but fall back to a generic method if a backend-specific comparison + // is not implemented. + switch (pContext->backend) + { + #ifdef MAL_HAS_WASAPI + case mal_backend_wasapi: + { + if (memcmp(pDeviceID->wasapi, &pInfos[iDevice].id.wasapi, sizeof(pDeviceID->wasapi)) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_DSOUND + case mal_backend_dsound: + { + if (memcmp(pDeviceID->dsound, &pInfos[iDevice].id.dsound, sizeof(pDeviceID->dsound)) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_WINMM + case mal_backend_winmm: + { + if (pInfos[iDevice].id.winmm == pDeviceID->winmm) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_ALSA + case mal_backend_alsa: + { + if (mal_strcmp(pInfos[iDevice].id.alsa, pDeviceID->alsa) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_COREAUDIO + case mal_backend_coreaudio + { + // TODO: Implement me. + } break; + #endif + #ifdef MAL_HAS_OSS + case mal_backend_oss: + { + if (mal_strcmp(pInfos[iDevice].id.oss, pDeviceID->oss) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_OPENSL + case mal_backend_opensl: + { + if (pInfos[iDevice].id.opensl == pDeviceID->opensl) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_OPENAL + case mal_backend_openal: + { + if (mal_strcmp(pInfos[iDevice].id.openal, pDeviceID->openal) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_SDL + case mal_backend_sdl: + { + if (pInfos[iDevice].id.sdl == pDeviceID->sdl) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_NULL + case mal_backend_null: + { + if (pInfos[iDevice].id.nullbackend == pDeviceID->nullbackend) { + found = MAL_TRUE; + } + } break; + #endif + + // Fall back to a generic memory comparison. + default: + { + if (memcmp(pDeviceID, &pInfos[iDevice].id, sizeof(*pDeviceID)) == 0) { + found = MAL_TRUE; + } + } break; + } + + if (found) { + mal_strncpy_s(pName, nameBufferSize, pInfos[iDevice].name, (size_t)-1); + result = MAL_SUCCESS; + break; + } + } + + mal_free(pInfos); + return result; +} + /////////////////////////////////////////////////////////////////////////////// // @@ -9181,6 +9319,11 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma { if (pCount == NULL) return mal_post_error(NULL, "mal_enumerate_devices() called with invalid arguments (pCount == 0).", MAL_INVALID_ARGS); + // The output buffer needs to be initialized to zero. + if (pInfo != NULL) { + mal_zero_memory(pInfo, (*pCount) * sizeof(*pInfo)); + } + switch (pContext->backend) { #ifdef MAL_HAS_WASAPI @@ -9409,6 +9552,27 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi if (result != MAL_SUCCESS) { return MAL_NO_BACKEND; // The error message will have been posted with mal_post_error() by the source of the error so don't bother calling it here. } + + + // If the backend did not fill out a name for the device, try a generic method. + if (pDevice->name[0] == '\0') { + if (mal_context__try_get_device_name_by_id(pContext, type, pDeviceID, pDevice->name, sizeof(pDevice->name)) != MAL_SUCCESS) { + // We failed to get the device name, so fall back to some generic names. + if (pDeviceID == NULL) { + if (type == mal_device_type_playback) { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Default Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Default Capture Device", (size_t)-1); + } + } else { + if (type == mal_device_type_playback) { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Capture Device", (size_t)-1); + } + } + } + } // We need a DSP object which is where samples are moved through in order to convert them to the -- cgit v1.2.3 From 8b3694a34d91682e05fb74f625bbd0f2dbb01645 Mon Sep 17 00:00:00 2001 From: David Reid Date: Wed, 22 Nov 2017 18:36:48 +1000 Subject: Fix a crash in audio mixing code. --- src/audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 2c2aedac..6065bf00 100644 --- a/src/audio.c +++ b/src/audio.c @@ -339,8 +339,8 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC float tempBuffer[1024]; // 512 frames for stereo. mal_uint32 framesToReadRightNow = framesToRead; - if (framesToReadRightNow > sizeof(tempBuffer)/DEVICE_CHANNELS) { - framesToReadRightNow = sizeof(tempBuffer)/DEVICE_CHANNELS; + if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS) { + framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS; } // If we're not looping, we need to make sure we flush the internal buffers of the DSP pipeline to ensure we get the -- cgit v1.2.3 From 5463e14886456e362594250d9d5aa548a462eb20 Mon Sep 17 00:00:00 2001 From: David Reid Date: Fri, 24 Nov 2017 21:54:00 +1000 Subject: Audio: Fix a bug with AudioStreams. This bug is a result of the buffer of an AudioStream being smaller than that of a period of the backend playback device. In this situation, AudioStream's would have pauses between buffer updates because the backend is not able to re-fill the AudioStream buffer's quick enough due to it's periods being longer than the AudioStream buffer. --- src/audio.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 6065bf00..1e8d09e7 100644 --- a/src/audio.c +++ b/src/audio.c @@ -426,10 +426,11 @@ void InitAudioDevice(void) } TraceLog(LOG_INFO, "Audio device initialized successfully: %s", device.name); - TraceLog(LOG_INFO, "Audio backend: %s", mal_get_backend_name(context.backend)); - TraceLog(LOG_INFO, "Audio format: %s", mal_get_format_name(device.format)); - TraceLog(LOG_INFO, "Audio channels: %d", device.channels); - TraceLog(LOG_INFO, "Audio sample rate: %d", device.sampleRate); + TraceLog(LOG_INFO, "Audio backend: mini_al / %s", mal_get_backend_name(context.backend)); + TraceLog(LOG_INFO, "Audio format: %s -> %s", mal_get_format_name(device.format), mal_get_format_name(device.internalFormat)); + TraceLog(LOG_INFO, "Audio channels: %d -> %d", device.channels, device.internalChannels); + TraceLog(LOG_INFO, "Audio sample rate: %d -> %d", device.sampleRate, device.internalSampleRate); + TraceLog(LOG_INFO, "Audio buffer size: %d", device.bufferSizeInFrames); isAudioInitialized = MAL_TRUE; #else @@ -1501,14 +1502,16 @@ void UpdateMusicStream(Music music) #if USE_MINI_AL bool streamEnding = false; + unsigned int subBufferSizeInFrames = ((AudioBuffer*)music->stream.audioBuffer)->bufferSizeInFrames / 2; + // NOTE: Using dynamic allocation because it could require more than 16KB - void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); + void *pcm = calloc(subBufferSizeInFrames*music->stream.sampleSize/8*music->stream.channels, 1); int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts while (IsAudioBufferProcessed(music->stream)) { - if (music->samplesLeft >= AUDIO_BUFFER_SIZE) samplesCount = AUDIO_BUFFER_SIZE; + if (music->samplesLeft >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames; else samplesCount = music->samplesLeft; // TODO: Really don't like ctxType thingy... @@ -1750,7 +1753,14 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un #if USE_MINI_AL mal_format formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); - AudioBuffer* audioBuffer = CreateAudioBuffer(formatIn, stream.channels, stream.sampleRate, AUDIO_BUFFER_SIZE*2, AUDIO_BUFFER_USAGE_STREAM); + // The size of a streaming buffer must be at least double the size of a period. + unsigned int periodSize = device.bufferSizeInFrames / device.periods; + unsigned int subBufferSize = AUDIO_BUFFER_SIZE; + if (subBufferSize < periodSize) { + subBufferSize = periodSize; + } + + AudioBuffer* audioBuffer = CreateAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer"); -- cgit v1.2.3 From a0d9913c7cf2dce11f8d45e38923c69eae5d6f5e Mon Sep 17 00:00:00 2001 From: David Reid Date: Fri, 24 Nov 2017 22:13:33 +1000 Subject: Potential fixes for audio on RPI and Emscripten builds. --- src/audio.c | 10 ++++++++++ src/external/mini_al.h | 11 +++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 1e8d09e7..497deaf8 100644 --- a/src/audio.c +++ b/src/audio.c @@ -393,14 +393,23 @@ void InitAudioDevice(void) mal_result result = mal_context_init(NULL, 0, &contextConfig, &context); if (result != MAL_SUCCESS) { + TraceLog(LOG_ERROR, "Failed to initialize audio context"); return; } // Device. Using the default device. Format is floating point because it simplifies mixing. mal_device_config deviceConfig = mal_device_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, OnSendAudioDataToDevice); + + // Special case for PLATFORM_RPI. +#if defined(PLATFORM_RPI) + deviceConfig.alsa.noMMap = MAL_TRUE; + deviceConfig.bufferSizeInFrames = 2048; +#endif + result = mal_device_init(&context, mal_device_type_playback, NULL, &deviceConfig, NULL, &device); if (result != MAL_SUCCESS) { + TraceLog(LOG_ERROR, "Failed to initialize audio playback device"); mal_context_uninit(&context); return; } @@ -410,6 +419,7 @@ void InitAudioDevice(void) result = mal_device_start(&device); if (result != MAL_SUCCESS) { + TraceLog(LOG_ERROR, "Failed to start audio playback device"); mal_device_uninit(&device); mal_context_uninit(&context); return; diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 164709d9..1779e707 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -8634,7 +8634,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, mal // SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need // to explicitly clamp this because it will be easy to overflow. - mal_uint32 bufferSize = pConfig->bufferSizeInFrames * pConfig->periods * pConfig->channels; + mal_uint32 bufferSize = pConfig->bufferSizeInFrames; if (bufferSize > 32768) { bufferSize = 32768; } else { @@ -8696,7 +8696,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, mal pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); pDevice->internalChannels = obtainedSpec.channels; pDevice->internalSampleRate = (mal_uint32)obtainedSpec.freq; - pDevice->bufferSizeInFrames = obtainedSpec.samples / obtainedSpec.channels; + pDevice->bufferSizeInFrames = obtainedSpec.samples; pDevice->periods = 1; // SDL doesn't seem to tell us what the period count is. Just set this 1. #if 0 @@ -10980,6 +10980,7 @@ const char* mal_get_backend_name(mal_backend backend) case mal_backend_oss: return "OSS"; case mal_backend_opensl: return "OpenSL|ES"; case mal_backend_openal: return "OpenAL"; + case mal_backend_sdl: return "SDL"; default: return "Unknown"; } } @@ -11197,7 +11198,8 @@ void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count) for (unsigned int i = 0; i < count; ++i) { int x = pIn[i]; double t; - t = (double)(x + 2147483648LL); + t = (double)(x + 2147483647); + t = t + 1; t = t * 0.0000000004656612873077392578125; r = (float)(t - 1); pOut[i] = (float)r; @@ -11255,7 +11257,8 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); c = c + 1; t = (mal_int64)(c * 2147483647.5); - r = (int)(t - 2147483648LL); + t = t - 2147483647; + r = (int)(t - 1); pOut[i] = (int)r; } } -- cgit v1.2.3 From 82f88e5df9ec7384b1f6a97a144371c37f6768eb Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 3 Dec 2017 11:20:02 +1000 Subject: Potential fixes for Raspberry Pi. --- src/audio.c | 8 +- src/external/mini_al.h | 277 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 250 insertions(+), 35 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 497deaf8..d397b064 100644 --- a/src/audio.c +++ b/src/audio.c @@ -401,10 +401,10 @@ void InitAudioDevice(void) mal_device_config deviceConfig = mal_device_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, OnSendAudioDataToDevice); // Special case for PLATFORM_RPI. -#if defined(PLATFORM_RPI) - deviceConfig.alsa.noMMap = MAL_TRUE; - deviceConfig.bufferSizeInFrames = 2048; -#endif +//#if defined(PLATFORM_RPI) +// deviceConfig.alsa.noMMap = MAL_TRUE; +// deviceConfig.bufferSizeInFrames = 2048; +//#endif result = mal_device_init(&context, mal_device_type_playback, NULL, &deviceConfig, NULL, &device); if (result != MAL_SUCCESS) diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 1779e707..fd395832 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -244,12 +244,13 @@ extern "C" { #endif #endif +#define MAL_SUPPORT_SDL // All platforms support SDL. + // Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now. #if !defined(MAL_EMSCRIPTEN) #define MAL_SUPPORT_OPENAL #define MAL_SUPPORT_NULL // All platforms support the null backend. #endif -#define MAL_SUPPORT_SDL // All platforms support SDL. #if !defined(MAL_NO_WASAPI) && defined(MAL_SUPPORT_WASAPI) @@ -758,6 +759,9 @@ struct mal_context mal_proc snd_pcm_avail; mal_proc snd_pcm_avail_update; mal_proc snd_pcm_wait; + mal_proc snd_pcm_info; + mal_proc snd_pcm_info_sizeof; + mal_proc snd_pcm_info_get_name; } alsa; #endif #ifdef MAL_SUPPORT_COREAUDIO @@ -862,8 +866,9 @@ struct mal_context mal_proc alGetBuffer3i; mal_proc alGetBufferiv; - mal_uint32 isFloat32Supported : 1; - mal_uint32 isMCFormatsSupported : 1; + mal_bool32 isEnumerationSupported : 1; + mal_bool32 isFloat32Supported : 1; + mal_bool32 isMCFormatsSupported : 1; } openal; #endif #ifdef MAL_SUPPORT_SDL @@ -1457,6 +1462,8 @@ mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pF // This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint32 frameCountIn); +// Helper for initializing a mal_dsp_config object. +mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut); /////////////////////////////////////////////////////////////////////////////// // @@ -1583,7 +1590,7 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form // Disable run-time linking on certain backends. -#ifndef MAL_NO_RUNTIME_LINLING +#ifndef MAL_NO_RUNTIME_LINKING #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN) #define MAL_NO_RUNTIME_LINKING #endif @@ -5472,6 +5479,33 @@ static mal_result mal_device__main_loop__winmm(mal_device* pDevice) #ifdef MAL_HAS_ALSA #include +// This array allows mini_al to control device-specific default buffer sizes. This uses a scaling factor. Order is important. If +// any part of the string is present in the device's name, the associated scale will be used. +struct +{ + const char* name; + float scale; +} g_malDefaultBufferSizeScalesALSA[] = { + {"bcm2835 IEC958/HDMI", 32}, + {"bcm2835 ALSA", 32} +}; + +static float mal_find_default_buffer_size_scale__alsa(const char* deviceName) +{ + if (deviceName == NULL) { + return 1; + } + + for (size_t i = 0; i < mal_countof(g_malDefaultBufferSizeScalesALSA); ++i) { + if (strstr(g_malDefaultBufferSizeScalesALSA[i].name, deviceName) != NULL) { + return g_malDefaultBufferSizeScalesALSA[i].scale; + } + } + + return 1; +} + + typedef int (* mal_snd_pcm_open_proc) (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode); typedef int (* mal_snd_pcm_close_proc) (snd_pcm_t *pcm); typedef size_t (* mal_snd_pcm_hw_params_sizeof_proc) (void); @@ -5515,6 +5549,9 @@ typedef snd_pcm_sframes_t (* mal_snd_pcm_writei_proc) (sn typedef snd_pcm_sframes_t (* mal_snd_pcm_avail_proc) (snd_pcm_t *pcm); typedef snd_pcm_sframes_t (* mal_snd_pcm_avail_update_proc) (snd_pcm_t *pcm); typedef int (* mal_snd_pcm_wait_proc) (snd_pcm_t *pcm, int timeout); +typedef int (* mal_snd_pcm_info) (snd_pcm_t *pcm, snd_pcm_info_t* info); +typedef size_t (* mal_snd_pcm_info_sizeof) (); +typedef const char* (* mal_snd_pcm_info_get_name) (const snd_pcm_info_t* info); static snd_pcm_format_t g_mal_ALSAFormats[] = { SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown @@ -5630,6 +5667,9 @@ mal_result mal_context_init__alsa(mal_context* pContext) pContext->alsa.snd_pcm_avail = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail"); pContext->alsa.snd_pcm_avail_update = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail_update"); pContext->alsa.snd_pcm_wait = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_info = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_get_name"); return MAL_SUCCESS; } @@ -6300,6 +6340,57 @@ static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type t } } + // We may need to scale the size of the buffer depending on the device. + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScale = 1; + + snd_pcm_info_t* pInfo = (snd_pcm_info_t*)alloca(((mal_snd_pcm_info_sizeof)pContext->alsa.snd_pcm_info_sizeof)()); + mal_zero_memory(pInfo, ((mal_snd_pcm_info_sizeof)pContext->alsa.snd_pcm_info_sizeof)()); + + if (((mal_snd_pcm_info)pContext->alsa.snd_pcm_info)((snd_pcm_t*)pDevice->alsa.pPCM, pInfo) == 0) { + const char* deviceName = ((mal_snd_pcm_info_get_name)pContext->alsa.snd_pcm_info_get_name)(pInfo); + if (deviceName != NULL) { + if (strcmp(deviceName, "default") == 0) { + // It's the default device. We need to use DESC from snd_device_name_hint(). + char** ppDeviceHints; + if (((mal_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) { + return MAL_NO_BACKEND; + } + + char** ppNextDeviceHint = ppDeviceHints; + while (*ppNextDeviceHint != NULL) { + char* NAME = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); + char* DESC = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); + char* IOID = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); + + mal_bool32 foundDevice = MAL_FALSE; + if ((type == mal_device_type_playback && (IOID == NULL || strcmp(IOID, "Output") == 0)) || + (type == mal_device_type_capture && (IOID != NULL && strcmp(IOID, "Input" ) == 0))) { + if (strcmp(NAME, deviceName) == 0) { + bufferSizeScale = mal_find_default_buffer_size_scale__alsa(DESC); + foundDevice = MAL_TRUE; + } + } + + free(NAME); + free(DESC); + free(IOID); + + if (foundDevice) { + break; + } + } + + ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); + } else { + bufferSizeScale = mal_find_default_buffer_size_scale__alsa(deviceName); + } + } + + pDevice->bufferSizeInFrames = (mal_uint32)(pDevice->bufferSizeInFrames * bufferSizeScale); + } + } + // Hardware parameters. snd_pcm_hw_params_t* pHWParams = (snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); @@ -7658,6 +7749,16 @@ static mal_result mal_device__stop_backend__opensl(mal_device* pDevice) #define MAL_AL_APIENTRY #endif +#ifdef MAL_NO_RUNTIME_LINKING + #if defined(MAL_APPLE) + #include + #include + #else + #include + #include + #endif +#endif + typedef struct mal_ALCdevice_struct mal_ALCdevice; typedef struct mal_ALCcontext_struct mal_ALCcontext; typedef char mal_ALCboolean; @@ -7804,6 +7905,7 @@ mal_result mal_context_init__openal(mal_context* pContext) { mal_assert(pContext != NULL); +#ifndef MAL_NO_RUNTIME_LINKING const char* libName = NULL; #ifdef MAL_WIN32 libName = "OpenAL32.dll"; @@ -7910,13 +8012,89 @@ mal_result mal_context_init__openal(mal_context* pContext) pContext->openal.alGetBufferi = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBufferi"); pContext->openal.alGetBuffer3i = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBuffer3i"); pContext->openal.alGetBufferiv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBufferiv"); - - // We depend on the ALC_ENUMERATION_EXT extension. - if (!((MAL_LPALCISEXTENSIONPRESENT)pContext->openal.alcIsExtensionPresent)(NULL, "ALC_ENUMERATION_EXT")) { - mal_dlclose(pContext->openal.hOpenAL); - return MAL_FAILED_TO_INIT_BACKEND; - } - +#else + pContext->openal.alcCreateContext = (mal_proc)alcCreateContext; + pContext->openal.alcMakeContextCurrent = (mal_proc)alcMakeContextCurrent; + pContext->openal.alcProcessContext = (mal_proc)alcProcessContext; + pContext->openal.alcSuspendContext = (mal_proc)alcSuspendContext; + pContext->openal.alcDestroyContext = (mal_proc)alcDestroyContext; + pContext->openal.alcGetCurrentContext = (mal_proc)alcGetCurrentContext; + pContext->openal.alcGetContextsDevice = (mal_proc)alcGetContextsDevice; + pContext->openal.alcOpenDevice = (mal_proc)alcOpenDevice; + pContext->openal.alcCloseDevice = (mal_proc)alcCloseDevice; + pContext->openal.alcGetError = (mal_proc)alcGetError; + pContext->openal.alcIsExtensionPresent = (mal_proc)alcIsExtensionPresent; + pContext->openal.alcGetProcAddress = (mal_proc)alcGetProcAddress; + pContext->openal.alcGetEnumValue = (mal_proc)alcGetEnumValue; + pContext->openal.alcGetString = (mal_proc)alcGetString; + pContext->openal.alcGetIntegerv = (mal_proc)alcGetIntegerv; + pContext->openal.alcCaptureOpenDevice = (mal_proc)alcCaptureOpenDevice; + pContext->openal.alcCaptureCloseDevice = (mal_proc)alcCaptureCloseDevice; + pContext->openal.alcCaptureStart = (mal_proc)alcCaptureStart; + pContext->openal.alcCaptureStop = (mal_proc)alcCaptureStop; + pContext->openal.alcCaptureSamples = (mal_proc)alcCaptureSamples; + + pContext->openal.alEnable = (mal_proc)alEnable; + pContext->openal.alDisable = (mal_proc)alDisable; + pContext->openal.alIsEnabled = (mal_proc)alIsEnabled; + pContext->openal.alGetString = (mal_proc)alGetString; + pContext->openal.alGetBooleanv = (mal_proc)alGetBooleanv; + pContext->openal.alGetIntegerv = (mal_proc)alGetIntegerv; + pContext->openal.alGetFloatv = (mal_proc)alGetFloatv; + pContext->openal.alGetDoublev = (mal_proc)alGetDoublev; + pContext->openal.alGetBoolean = (mal_proc)alGetBoolean; + pContext->openal.alGetInteger = (mal_proc)alGetInteger; + pContext->openal.alGetFloat = (mal_proc)alGetFloat; + pContext->openal.alGetDouble = (mal_proc)alGetDouble; + pContext->openal.alGetError = (mal_proc)alGetError; + pContext->openal.alIsExtensionPresent = (mal_proc)alIsExtensionPresent; + pContext->openal.alGetProcAddress = (mal_proc)alGetProcAddress; + pContext->openal.alGetEnumValue = (mal_proc)alGetEnumValue; + pContext->openal.alGenSources = (mal_proc)alGenSources; + pContext->openal.alDeleteSources = (mal_proc)alDeleteSources; + pContext->openal.alIsSource = (mal_proc)alIsSource; + pContext->openal.alSourcef = (mal_proc)alSourcef; + pContext->openal.alSource3f = (mal_proc)alSource3f; + pContext->openal.alSourcefv = (mal_proc)alSourcefv; + pContext->openal.alSourcei = (mal_proc)alSourcei; + pContext->openal.alSource3i = (mal_proc)alSource3i; + pContext->openal.alSourceiv = (mal_proc)alSourceiv; + pContext->openal.alGetSourcef = (mal_proc)alGetSourcef; + pContext->openal.alGetSource3f = (mal_proc)alGetSource3f; + pContext->openal.alGetSourcefv = (mal_proc)alGetSourcefv; + pContext->openal.alGetSourcei = (mal_proc)alGetSourcei; + pContext->openal.alGetSource3i = (mal_proc)alGetSource3i; + pContext->openal.alGetSourceiv = (mal_proc)alGetSourceiv; + pContext->openal.alSourcePlayv = (mal_proc)alSourcePlayv; + pContext->openal.alSourceStopv = (mal_proc)alSourceStopv; + pContext->openal.alSourceRewindv = (mal_proc)alSourceRewindv; + pContext->openal.alSourcePausev = (mal_proc)alSourcePausev; + pContext->openal.alSourcePlay = (mal_proc)alSourcePlay; + pContext->openal.alSourceStop = (mal_proc)alSourceStop; + pContext->openal.alSourceRewind = (mal_proc)alSourceRewind; + pContext->openal.alSourcePause = (mal_proc)alSourcePause; + pContext->openal.alSourceQueueBuffers = (mal_proc)alSourceQueueBuffers; + pContext->openal.alSourceUnqueueBuffers = (mal_proc)alSourceUnqueueBuffers; + pContext->openal.alGenBuffers = (mal_proc)alGenBuffers; + pContext->openal.alDeleteBuffers = (mal_proc)alDeleteBuffers; + pContext->openal.alIsBuffer = (mal_proc)alIsBuffer; + pContext->openal.alBufferData = (mal_proc)alBufferData; + pContext->openal.alBufferf = (mal_proc)alBufferf; + pContext->openal.alBuffer3f = (mal_proc)alBuffer3f; + pContext->openal.alBufferfv = (mal_proc)alBufferfv; + pContext->openal.alBufferi = (mal_proc)alBufferi; + pContext->openal.alBuffer3i = (mal_proc)alBuffer3i; + pContext->openal.alBufferiv = (mal_proc)alBufferiv; + pContext->openal.alGetBufferf = (mal_proc)alGetBufferf; + pContext->openal.alGetBuffer3f = (mal_proc)alGetBuffer3f; + pContext->openal.alGetBufferfv = (mal_proc)alGetBufferfv; + pContext->openal.alGetBufferi = (mal_proc)alGetBufferi; + pContext->openal.alGetBuffer3i = (mal_proc)alGetBuffer3i; + pContext->openal.alGetBufferiv = (mal_proc)alGetBufferiv; +#endif + + // We depend on the ALC_ENUMERATION_EXT extension for enumeration. If this is not supported we fall back to default devices. + pContext->openal.isEnumerationSupported = ((MAL_LPALCISEXTENSIONPRESENT)pContext->openal.alcIsExtensionPresent)(NULL, "ALC_ENUMERATION_EXT"); pContext->openal.isFloat32Supported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_float32"); pContext->openal.isMCFormatsSupported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_MCFORMATS"); @@ -7928,7 +8106,10 @@ mal_result mal_context_uninit__openal(mal_context* pContext) mal_assert(pContext != NULL); mal_assert(pContext->backend == mal_backend_openal); +#ifndef MAL_NO_RUNTIME_LINKING mal_dlclose(pContext->openal.hOpenAL); +#endif + return MAL_SUCCESS; } @@ -7937,35 +8118,55 @@ mal_result mal_enumerate_devices__openal(mal_context* pContext, mal_device_type mal_uint32 infoSize = *pCount; *pCount = 0; - const mal_ALCchar* pDeviceNames = ((MAL_LPALCGETSTRING)pContext->openal.alcGetString)(NULL, (type == mal_device_type_playback) ? MAL_ALC_DEVICE_SPECIFIER : MAL_ALC_CAPTURE_DEVICE_SPECIFIER); - if (pDeviceNames == NULL) { - return MAL_NO_DEVICE; - } + if (pContext->openal.isEnumerationSupported) { + const mal_ALCchar* pDeviceNames = ((MAL_LPALCGETSTRING)pContext->openal.alcGetString)(NULL, (type == mal_device_type_playback) ? MAL_ALC_DEVICE_SPECIFIER : MAL_ALC_CAPTURE_DEVICE_SPECIFIER); + if (pDeviceNames == NULL) { + return MAL_NO_DEVICE; + } - // Each device is stored in pDeviceNames, separated by a null-terminator. The string itself is double-null-terminated. - const mal_ALCchar* pNextDeviceName = pDeviceNames; - while (pNextDeviceName[0] != '\0') { + // Each device is stored in pDeviceNames, separated by a null-terminator. The string itself is double-null-terminated. + const mal_ALCchar* pNextDeviceName = pDeviceNames; + while (pNextDeviceName[0] != '\0') { + if (pInfo != NULL) { + if (infoSize > 0) { + mal_strncpy_s(pInfo->id.openal, sizeof(pInfo->id.openal), (const char*)pNextDeviceName, (size_t)-1); + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)pNextDeviceName, (size_t)-1); + + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + + // Move to the next device name. + while (*pNextDeviceName != '\0') { + pNextDeviceName += 1; + } + + // Skip past the null terminator. + pNextDeviceName += 1; + }; + } else { + // Enumeration is not supported. Use default devices. if (pInfo != NULL) { if (infoSize > 0) { - mal_strncpy_s(pInfo->id.openal, sizeof(pInfo->id.openal), (const char*)pNextDeviceName, (size_t)-1); - mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)pNextDeviceName, (size_t)-1); + if (type == mal_device_type_playback) { + pInfo->id.sdl = 0; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); + } else { + pInfo->id.sdl = 0; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); + } pInfo += 1; - infoSize -= 1; *pCount += 1; } } else { *pCount += 1; } - - // Move to the next device name. - while (*pNextDeviceName != '\0') { - pNextDeviceName += 1; - } - - // Skip past the null terminator. - pNextDeviceName += 1; - }; + } return MAL_SUCCESS; } @@ -8043,7 +8244,7 @@ mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, } if (formatAL == 0) { - return MAL_FORMAT_NOT_SUPPORTED; + return mal_context_post_error(pContext, NULL, "[OpenAL] Format not supported.", MAL_FORMAT_NOT_SUPPORTED); } bufferSizeInSamplesAL *= channelsAL; @@ -10954,6 +11155,20 @@ mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 chann return mal_dsp_read_frames_ex(&dsp, frameCountOut, pOut, MAL_TRUE); } +mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut) +{ + mal_dsp_config config; + mal_zero_object(&config); + config.formatIn = formatIn; + config.channelsIn = channelsIn; + config.sampleRateIn = sampleRateIn; + config.formatOut = formatOut; + config.channelsOut = channelsOut; + config.sampleRateOut = sampleRateOut; + + return config; +} + -- cgit v1.2.3 From a6f9cc5629841b7adf3d9ff21eaa2aaabb3e4bc1 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 11 Dec 2017 11:55:02 +0100 Subject: Remove rres support Let the user choose if using rres external library --- src/audio.c | 13 -- src/external/tinfl.c | 592 --------------------------------------------------- src/raylib.h | 16 -- src/rres.h | 483 ----------------------------------------- src/text.c | 29 +-- src/textures.c | 13 +- src/utils.c | 3 - src/utils.h | 2 - 8 files changed, 2 insertions(+), 1149 deletions(-) delete mode 100644 src/external/tinfl.c delete mode 100644 src/rres.h (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index d397b064..15f50a24 100644 --- a/src/audio.c +++ b/src/audio.c @@ -812,19 +812,6 @@ Wave LoadWave(const char *fileName) #endif #if defined(SUPPORT_FILEFORMAT_FLAC) else if (IsFileExtension(fileName, ".flac")) wave = LoadFLAC(fileName); -#endif -#if !defined(AUDIO_STANDALONE) - else if (IsFileExtension(fileName, ".rres")) - { - RRES rres = LoadResource(fileName, 0); - - // NOTE: Parameters for RRES_TYPE_WAVE are: sampleCount, sampleRate, sampleSize, channels - - if (rres[0].type == RRES_TYPE_WAVE) wave = LoadWaveEx(rres[0].data, rres[0].param1, rres[0].param2, rres[0].param3, rres[0].param4); - else TraceLog(LOG_WARNING, "[%s] Resource file does not contain wave data", fileName); - - UnloadResource(rres); - } #endif else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); diff --git a/src/external/tinfl.c b/src/external/tinfl.c deleted file mode 100644 index a17a156b..00000000 --- a/src/external/tinfl.c +++ /dev/null @@ -1,592 +0,0 @@ -/* tinfl.c v1.11 - public domain inflate with zlib header parsing/adler32 checking (inflate-only subset of miniz.c) - See "unlicense" statement at the end of this file. - Rich Geldreich , last updated May 20, 2011 - Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt - - The entire decompressor coroutine is implemented in tinfl_decompress(). The other functions are optional high-level helpers. -*/ -#ifndef TINFL_HEADER_INCLUDED -#define TINFL_HEADER_INCLUDED - -#include - -typedef unsigned char mz_uint8; -typedef signed short mz_int16; -typedef unsigned short mz_uint16; -typedef unsigned int mz_uint32; -typedef unsigned int mz_uint; -typedef unsigned long long mz_uint64; - -#if defined(_M_IX86) || defined(_M_X64) -// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster). -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 -// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. -#define MINIZ_LITTLE_ENDIAN 1 -#endif - -#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) -// Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator) -#define MINIZ_HAS_64BIT_REGISTERS 1 -#endif - -// Works around MSVC's spammy "warning C4127: conditional expression is constant" message. -#ifdef _MSC_VER - #define MZ_MACRO_END while (0, 0) -#else - #define MZ_MACRO_END while (0) -#endif - -// Decompression flags used by tinfl_decompress(). -// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. -// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. -// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). -// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. -enum -{ - TINFL_FLAG_PARSE_ZLIB_HEADER = 1, - TINFL_FLAG_HAS_MORE_INPUT = 2, - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, - TINFL_FLAG_COMPUTE_ADLER32 = 8 -}; - -// High level decompression functions: -// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. -// On return: -// Function returns a pointer to the decompressed data, or NULL on failure. -// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must free() the returned block when it's no longer needed. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. -// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. -#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. -// Returns 1 on success or 0 on failure. -typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; - -// Max size of LZ dictionary. -#define TINFL_LZ_DICT_SIZE 32768 - -// Return status. -typedef enum -{ - TINFL_STATUS_BAD_PARAM = -3, - TINFL_STATUS_ADLER32_MISMATCH = -2, - TINFL_STATUS_FAILED = -1, - TINFL_STATUS_DONE = 0, - TINFL_STATUS_NEEDS_MORE_INPUT = 1, - TINFL_STATUS_HAS_MORE_OUTPUT = 2 -} tinfl_status; - -// Initializes the decompressor to its initial state. -#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END -#define tinfl_get_adler32(r) (r)->m_check_adler32 - -// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. -// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); - -// Internal/private bits follow. -enum -{ - TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, - TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS -}; - -typedef struct -{ - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; -} tinfl_huff_table; - -#if MINIZ_HAS_64BIT_REGISTERS - #define TINFL_USE_64BIT_BITBUF 1 -#endif - -#if TINFL_USE_64BIT_BITBUF - typedef mz_uint64 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (64) -#else - typedef mz_uint32 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (32) -#endif - -struct tinfl_decompressor_tag -{ - mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; - tinfl_bit_buf_t m_bit_buf; - size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; - mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; -}; - -#endif // #ifdef TINFL_HEADER_INCLUDED - -// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) - -#ifndef TINFL_HEADER_FILE_ONLY - -#include - -// MZ_MALLOC, etc. are only used by the optional high-level helper functions. -#ifdef MINIZ_NO_MALLOC - #define MZ_MALLOC(x) NULL - #define MZ_FREE(x) x, ((void)0) - #define MZ_REALLOC(p, x) NULL -#else - #define MZ_MALLOC(x) malloc(x) - #define MZ_FREE(x) free(x) - #define MZ_REALLOC(p, x) realloc(p, x) -#endif - -#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) -#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) -#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) - #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) -#else - #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) - #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) -#endif - -#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) -#define TINFL_MEMSET(p, c, l) memset(p, c, l) - -#define TINFL_CR_BEGIN switch(r->m_state) { case 0: -#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END -#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END -#define TINFL_CR_FINISH } - -// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never -// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. -#define TINFL_GET_BYTE(state_index, c) do { \ - if (pIn_buf_cur >= pIn_buf_end) { \ - for ( ; ; ) { \ - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ - TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ - if (pIn_buf_cur < pIn_buf_end) { \ - c = *pIn_buf_cur++; \ - break; \ - } \ - } else { \ - c = 0; \ - break; \ - } \ - } \ - } else c = *pIn_buf_cur++; } MZ_MACRO_END - -#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) -#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END -#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END - -// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. -// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a -// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the -// bit buffer contains >=15 bits (deflate's max. Huffman code size). -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ - do { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ - if (temp >= 0) { \ - code_len = temp >> 9; \ - if ((code_len) && (num_bits >= code_len)) \ - break; \ - } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ - code_len = TINFL_FAST_LOOKUP_BITS; \ - do { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ - } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ - } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ - } while (num_bits < 15); - -// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read -// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully -// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. -// The slow path is only executed at the very end of the input buffer. -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ - int temp; mz_uint code_len, c; \ - if (num_bits < 15) { \ - if ((pIn_buf_end - pIn_buf_cur) < 2) { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ - } else { \ - bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ - } \ - } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ - code_len = temp >> 9, temp &= 511; \ - else { \ - code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ - } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END - -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) -{ - static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; - static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; - - tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; - const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; - size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; - - // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). - if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } - - num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; - TINFL_CR_BEGIN - - bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); - counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); - if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); - if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } - } - - do - { - TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; - if (r->m_type == 0) - { - TINFL_SKIP_BITS(5, num_bits & 7); - for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } - if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } - while ((counter) && (num_bits)) - { - TINFL_GET_BITS(51, dist, 8); - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)dist; - counter--; - } - while (counter) - { - size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } - while (pIn_buf_cur >= pIn_buf_end) - { - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) - { - TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); - } - else - { - TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); - } - } - n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); - TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; - } - } - else if (r->m_type == 3) - { - TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); - } - else - { - if (r->m_type == 1) - { - mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; - r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); - for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; - } - else - { - for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } - r->m_table_sizes[2] = 19; - } - for ( ; (int)r->m_type >= 0; r->m_type--) - { - int tree_next, tree_cur; tinfl_huff_table *pTable; - mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); - for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; - used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; - for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } - if ((65536 != total) && (used_syms > 1)) - { - TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); - } - for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) - { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; - cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); - if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } - rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); - for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) - { - tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; - } - tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; - } - if (r->m_type == 2) - { - for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) - { - mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } - if ((dist == 16) && (!counter)) - { - TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); - } - num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; - TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; - } - if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) - { - TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); - } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); - } - } - for ( ; ; ) - { - mz_uint8 *pSrc; - for ( ; ; ) - { - if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) - { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); - if (counter >= 256) - break; - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)counter; - } - else - { - int sym2; mz_uint code_len; -#if TINFL_USE_64BIT_BITBUF - if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } -#else - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - counter = sym2; bit_buf >>= code_len; num_bits -= code_len; - if (counter & 256) - break; - -#if !TINFL_USE_64BIT_BITBUF - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - bit_buf >>= code_len; num_bits -= code_len; - - pOut_buf_cur[0] = (mz_uint8)counter; - if (sym2 & 256) - { - pOut_buf_cur++; - counter = sym2; - break; - } - pOut_buf_cur[1] = (mz_uint8)sym2; - pOut_buf_cur += 2; - } - } - if ((counter &= 511) == 256) break; - - num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } - - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); - num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } - - dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; - if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - { - TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); - } - - pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); - - if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) - { - while (counter--) - { - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; - } - continue; - } -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - else if ((counter >= 9) && (counter <= dist)) - { - const mz_uint8 *pSrc_end = pSrc + (counter & ~7); - do - { - ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; - ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; - pOut_buf_cur += 8; - } while ((pSrc += 8) < pSrc_end); - if ((counter &= 7) < 3) - { - if (counter) - { - pOut_buf_cur[0] = pSrc[0]; - if (counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - continue; - } - } -#endif - do - { - pOut_buf_cur[0] = pSrc[0]; - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur[2] = pSrc[2]; - pOut_buf_cur += 3; pSrc += 3; - } while ((int)(counter -= 3) > 2); - if ((int)counter > 0) - { - pOut_buf_cur[0] = pSrc[0]; - if ((int)counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - } - } - } while (!(r->m_final & 1)); - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } - } - TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - TINFL_CR_FINISH - -common_exit: - r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; - *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; - if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) - { - const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; - mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; - while (buf_len) - { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) - { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; - } - return status; -} - -// Higher level helper functions. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; - *pOut_len = 0; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, - (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - src_buf_ofs += src_buf_size; - *pOut_len += dst_buf_size; - if (status == TINFL_STATUS_DONE) break; - new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; - pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); - if (!pNew_buf) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; - } - return pBuf; -} - -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); - status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; -} - -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - int result = 0; - tinfl_decompressor decomp; - mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; - if (!pDict) - return TINFL_STATUS_FAILED; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, - (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); - in_buf_ofs += in_buf_size; - if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) - break; - if (status != TINFL_STATUS_HAS_MORE_OUTPUT) - { - result = (status == TINFL_STATUS_DONE); - break; - } - dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); - } - MZ_FREE(pDict); - *pIn_buf_size = in_buf_ofs; - return result; -} - -#endif // #ifndef TINFL_HEADER_FILE_ONLY - -/* - This is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - For more information, please refer to -*/ diff --git a/src/raylib.h b/src/raylib.h index a3914c4d..ac739830 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -495,22 +495,6 @@ typedef struct AudioStream { unsigned int buffers[2]; // OpenAL audio buffers (double buffering) } AudioStream; -// rRES data returned when reading a resource, -// it contains all required data for user (24 byte) -typedef struct RRESData { - unsigned int type; // Resource type (4 byte) - - unsigned int param1; // Resouce parameter 1 (4 byte) - unsigned int param2; // Resouce parameter 2 (4 byte) - unsigned int param3; // Resouce parameter 3 (4 byte) - unsigned int param4; // Resouce parameter 4 (4 byte) - - void *data; // Resource data pointer (4 byte) -} RRESData; - -// RRES type (pointer to RRESData array) -typedef struct RRESData *RRES; - // Head-Mounted-Display device parameters typedef struct VrDeviceInfo { int hResolution; // HMD horizontal resolution in pixels diff --git a/src/rres.h b/src/rres.h deleted file mode 100644 index 75faf640..00000000 --- a/src/rres.h +++ /dev/null @@ -1,483 +0,0 @@ -/********************************************************************************************** -* -* rres v1.0 - raylib resource (rRES) custom fileformat management functions -* -* CONFIGURATION: -* -* #define RREM_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* DEPENDENCIES: -* tinfl - DEFLATE decompression functions -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2016-2017 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -/* -References: - RIFF file-format: http://www.johnloomis.org/cpe102/asgn/asgn1/riff.html - ZIP file-format: https://en.wikipedia.org/wiki/Zip_(file_format) - http://www.onicos.com/staff/iz/formats/zip.html - XNB file-format: http://xbox.create.msdn.com/en-US/sample/xnb_format -*/ - -#ifndef RRES_H -#define RRES_H - -#if !defined(RRES_STANDALONE) - #include "raylib.h" -#endif - -//#define RRES_STATIC -#ifdef RRES_STATIC - #define RRESDEF static // Functions just visible to module including this file -#else - #ifdef __cplusplus - #define RRESDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define RRESDEF extern // Functions visible from other files - #endif -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define MAX_RESOURCES_SUPPORTED 256 - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -// NOTE: Some types are required for RAYGUI_STANDALONE usage -//---------------------------------------------------------------------------------- -#if defined(RRES_STANDALONE) - // rRES data returned when reading a resource, it contains all required data for user (24 byte) - // NOTE: Using void *data pointer, so we can load to image.data, wave.data, mesh.*, (unsigned char *) - typedef struct RRESData { - unsigned int type; // Resource type (4 byte) - - unsigned int param1; // Resouce parameter 1 (4 byte) - unsigned int param2; // Resouce parameter 2 (4 byte) - unsigned int param3; // Resouce parameter 3 (4 byte) - unsigned int param4; // Resouce parameter 4 (4 byte) - - void *data; // Resource data pointer (4 byte) - } RRESData; - - // RRES type (pointer to RRESData array) - typedef struct RRESData *RRES; // Resource pointer - - // RRESData type - typedef enum { - RRES_TYPE_RAW = 0, - RRES_TYPE_IMAGE, - RRES_TYPE_WAVE, - RRES_TYPE_VERTEX, - RRES_TYPE_TEXT, - RRES_TYPE_FONT_IMAGE, - RRES_TYPE_FONT_CHARDATA, // CharInfo { int value, recX, recY, recWidth, recHeight, offsetX, offsetY, xAdvance } - RRES_TYPE_DIRECTORY - } RRESDataType; - -// Parameters information depending on resource type - -// RRES_TYPE_RAW params: -// RRES_TYPE_IMAGE params: width, height, mipmaps, format -// RRES_TYPE_WAVE params: sampleCount, sampleRate, sampleSize, channels -// RRES_TYPE_VERTEX params: vertexCount, vertexType, vertexFormat // Use masks instead? -// RRES_TYPE_TEXT params: charsCount, cultureCode -// RRES_TYPE_FONT_IMAGE params: width, height, format, mipmaps; -// RRES_TYPE_FONT_CHARDATA params: charsCount, baseSize -// RRES_TYPE_DIRECTORY params: fileCount, directoryCount - -// SpriteFont = RRES_TYPE_FONT_IMAGE chunk + RRES_TYPE_FONT_DATA chunk -// Mesh = multiple RRES_TYPE_VERTEX chunks - - -#endif - -//---------------------------------------------------------------------------------- -// Global variables -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -//RRESDEF RRESData LoadResourceData(const char *rresFileName, int rresId, int part); -RRESDEF RRES LoadResource(const char *fileName, int rresId); -RRESDEF void UnloadResource(RRES rres); - -/* -QUESTION: How to load each type of data from RRES ? - -rres->type == RRES_TYPE_RAW -unsigned char data = (unsigned char *)rres[0]->data; - -rres->type == RRES_TYPE_IMAGE -Image image; -image.data = rres[0]->data; // Be careful, duplicate pointer -image.width = rres[0]->param1; -image.height = rres[0]->param2; -image.mipmaps = rres[0]->param3; -image.format = rres[0]->format; - -rres->type == RRES_TYPE_WAVE -Wave wave; -wave.data = rres[0]->data; -wave.sampleCount = rres[0]->param1; -wave.sampleRate = rres[0]->param2; -wave.sampleSize = rres[0]->param3; -wave.channels = rres[0]->param4; - -rres->type == RRES_TYPE_VERTEX (multiple parts) -Mesh mesh; -mesh.vertexCount = rres[0]->param1; -mesh.vertices = (float *)rres[0]->data; -mesh.texcoords = (float *)rres[1]->data; -mesh.normals = (float *)rres[2]->data; -mesh.tangents = (float *)rres[3]->data; -mesh.tangents = (unsigned char *)rres[4]->data; - -rres->type == RRES_TYPE_TEXT -unsigned char *text = (unsigned char *)rres->data; -Shader shader = LoadShaderText(text, rres->param1); Shader LoadShaderText(const char *shdrText, int length); - -rres->type == RRES_TYPE_FONT_IMAGE (multiple parts) -rres->type == RRES_TYPE_FONT_CHARDATA -SpriteFont font; -font.texture = LoadTextureFromImage(image); // rres[0] -font.chars = (CharInfo *)rres[1]->data; -font.charsCount = rres[1]->param1; -font.baseSize = rres[1]->param2; - -rres->type == RRES_TYPE_DIRECTORY -unsigned char *fileNames = (unsigned char *)rres[0]->data; // fileNames separed by \n -int filesCount = rres[0]->param1; -*/ - -#endif // RRES_H - - -/*********************************************************************************** -* -* RRES IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RRES_IMPLEMENTATION) - -#include // Required for: FILE, fopen(), fclose() - -// Check if custom malloc/free functions defined, if not, using standard ones -#if !defined(RRES_MALLOC) - #include // Required for: malloc(), free() - - #define RRES_MALLOC(size) malloc(size) - #define RRES_FREE(ptr) free(ptr) -#endif - -#include "external/tinfl.c" // Required for: tinfl_decompress_mem_to_mem() - // NOTE: DEFLATE algorythm data decompression - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - -// rRES file header (8 byte) -typedef struct { - char id[4]; // File identifier: rRES (4 byte) - unsigned short version; // File version and subversion (2 byte) - unsigned short count; // Number of resources in this file (2 byte) -} RRESFileHeader; - -// rRES info header, every resource includes this header (16 byte + 16 byte) -typedef struct { - unsigned int id; // Resource unique identifier (4 byte) - unsigned char dataType; // Resource data type (1 byte) - unsigned char compType; // Resource data compression type (1 byte) - unsigned char cryptoType; // Resource data encryption type (1 byte) - unsigned char partsCount; // Resource data parts count, used for splitted data (1 byte) - unsigned int dataSize; // Resource data size (compressed or not, only DATA) (4 byte) - unsigned int uncompSize; // Resource data size (uncompressed, only DATA) (4 byte) - - unsigned int param1; // Resouce parameter 1 (4 byte) - unsigned int param2; // Resouce parameter 2 (4 byte) - unsigned int param3; // Resouce parameter 3 (4 byte) - unsigned int param4; // Resouce parameter 4 (4 byte) -} RRESInfoHeader; - -// Compression types -typedef enum { - RRES_COMP_NONE = 0, // No data compression - RRES_COMP_DEFLATE, // DEFLATE compression - RRES_COMP_LZ4, // LZ4 compression - RRES_COMP_LZMA, // LZMA compression - RRES_COMP_BROTLI, // BROTLI compression - // gzip, zopfli, lzo, zstd // Other compression algorythms... -} RRESCompressionType; - -// Encryption types -typedef enum { - RRES_CRYPTO_NONE = 0, // No data encryption - RRES_CRYPTO_XOR, // XOR (128 bit) encryption - RRES_CRYPTO_AES, // RIJNDAEL (128 bit) encryption (AES) - RRES_CRYPTO_TDES, // Triple DES encryption - RRES_CRYPTO_BLOWFISH, // BLOWFISH encryption - RRES_CRYPTO_XTEA, // XTEA encryption - // twofish, RC5, RC6 // Other encryption algorythm... -} RRESEncryptionType; - -// Image/Texture data type -typedef enum { - RRES_IM_UNCOMP_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - RRES_IM_UNCOMP_GRAY_ALPHA, // 16 bpp (2 channels) - RRES_IM_UNCOMP_R5G6B5, // 16 bpp - RRES_IM_UNCOMP_R8G8B8, // 24 bpp - RRES_IM_UNCOMP_R5G5B5A1, // 16 bpp (1 bit alpha) - RRES_IM_UNCOMP_R4G4B4A4, // 16 bpp (4 bit alpha) - RRES_IM_UNCOMP_R8G8B8A8, // 32 bpp - RRES_IM_COMP_DXT1_RGB, // 4 bpp (no alpha) - RRES_IM_COMP_DXT1_RGBA, // 4 bpp (1 bit alpha) - RRES_IM_COMP_DXT3_RGBA, // 8 bpp - RRES_IM_COMP_DXT5_RGBA, // 8 bpp - RRES_IM_COMP_ETC1_RGB, // 4 bpp - RRES_IM_COMP_ETC2_RGB, // 4 bpp - RRES_IM_COMP_ETC2_EAC_RGBA, // 8 bpp - RRES_IM_COMP_PVRT_RGB, // 4 bpp - RRES_IM_COMP_PVRT_RGBA, // 4 bpp - RRES_IM_COMP_ASTC_4x4_RGBA, // 8 bpp - RRES_IM_COMP_ASTC_8x8_RGBA // 2 bpp - //... -} RRESImageFormat; - -// Vertex data type -typedef enum { - RRES_VERT_POSITION, - RRES_VERT_TEXCOORD1, - RRES_VERT_TEXCOORD2, - RRES_VERT_TEXCOORD3, - RRES_VERT_TEXCOORD4, - RRES_VERT_NORMAL, - RRES_VERT_TANGENT, - RRES_VERT_COLOR, - RRES_VERT_INDEX, - //... -} RRESVertexType; - -// Vertex data format type -typedef enum { - RRES_VERT_BYTE, - RRES_VERT_SHORT, - RRES_VERT_INT, - RRES_VERT_HFLOAT, - RRES_VERT_FLOAT, - //... -} RRESVertexFormat; - -#if defined(RRES_STANDALONE) -typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; -#endif - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -static void *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize); - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- - -// Load resource from file by id (could be multiple parts) -// NOTE: Returns uncompressed data with parameters, search resource by id -RRESDEF RRES LoadResource(const char *fileName, int rresId) -{ - RRES rres = { 0 }; - - RRESFileHeader fileHeader; - RRESInfoHeader infoHeader; - - FILE *rresFile = fopen(fileName, "rb"); - - if (rresFile == NULL) TraceLog(LOG_WARNING, "[%s] rRES raylib resource file could not be opened", fileName); - else - { - // Read rres file info header - fread(&fileHeader.id[0], sizeof(char), 1, rresFile); - fread(&fileHeader.id[1], sizeof(char), 1, rresFile); - fread(&fileHeader.id[2], sizeof(char), 1, rresFile); - fread(&fileHeader.id[3], sizeof(char), 1, rresFile); - fread(&fileHeader.version, sizeof(short), 1, rresFile); - fread(&fileHeader.count, sizeof(short), 1, rresFile); - - // Verify "rRES" identifier - if ((fileHeader.id[0] != 'r') && (fileHeader.id[1] != 'R') && (fileHeader.id[2] != 'E') && (fileHeader.id[3] != 'S')) - { - TraceLog(LOG_WARNING, "[%s] This is not a valid raylib resource file", fileName); - } - else - { - for (int i = 0; i < fileHeader.count; i++) - { - // Read resource info and parameters - fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile); - - if (infoHeader.id == rresId) - { - rres = (RRES)malloc(sizeof(RRESData)*infoHeader.partsCount); - - // Load all required resources parts - for (int k = 0; k < infoHeader.partsCount; k++) - { - // TODO: Verify again that rresId is the same in every part - - // Register data type and parameters - rres[k].type = infoHeader.dataType; - rres[k].param1 = infoHeader.param1; - rres[k].param2 = infoHeader.param2; - rres[k].param3 = infoHeader.param3; - rres[k].param4 = infoHeader.param4; - - // Read resource data block - void *data = RRES_MALLOC(infoHeader.dataSize); - fread(data, infoHeader.dataSize, 1, rresFile); - - if (infoHeader.compType == RRES_COMP_DEFLATE) - { - void *uncompData = DecompressData(data, infoHeader.dataSize, infoHeader.uncompSize); - - rres[k].data = uncompData; - - RRES_FREE(data); - } - else rres[k].data = data; - - if (rres[k].data != NULL) TraceLog(LOG_INFO, "[%s][ID %i] Resource data loaded successfully", fileName, (int)infoHeader.id); - - // Read next part - fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile); - } - } - else - { - // Skip required data to read next resource infoHeader - fseek(rresFile, infoHeader.dataSize, SEEK_CUR); - } - } - - if (rres[0].data == NULL) TraceLog(LOG_WARNING, "[%s][ID %i] Requested resource could not be found", fileName, (int)rresId); - } - - fclose(rresFile); - } - - return rres; -} - -// Unload resource data -RRESDEF void UnloadResource(RRES rres) -{ - // TODO: When you load resource... how many parts conform it? depends on type? --> Not clear... - - if (rres[0].data != NULL) free(rres[0].data); -} - -//---------------------------------------------------------------------------------- -// Module specific Functions Definition -//---------------------------------------------------------------------------------- - -// Data decompression function -// NOTE: Allocated data MUST be freed by user -static void *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize) -{ - int tempUncompSize; - void *uncompData; - - // Allocate buffer to hold decompressed data - uncompData = (mz_uint8 *)RRES_MALLOC((size_t)uncompSize); - - // Check correct memory allocation - if (uncompData == NULL) - { - TraceLog(LOG_WARNING, "Out of memory while decompressing data"); - } - else - { - // Decompress data - tempUncompSize = tinfl_decompress_mem_to_mem(uncompData, (size_t)uncompSize, data, compSize, 1); - - if (tempUncompSize == -1) - { - TraceLog(LOG_WARNING, "Data decompression failed"); - RRES_FREE(uncompData); - } - - if (uncompSize != (int)tempUncompSize) - { - TraceLog(LOG_WARNING, "Expected uncompressed size do not match, data may be corrupted"); - TraceLog(LOG_WARNING, " -- Expected uncompressed size: %i", uncompSize); - TraceLog(LOG_WARNING, " -- Returned uncompressed size: %i", tempUncompSize); - } - - TraceLog(LOG_INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize); - } - - return uncompData; -} - -// Some required functions for rres standalone module version -#if defined(RRES_STANDALONE) -// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) -void TraceLog(int logType, const char *text, ...) -{ - va_list args; - va_start(args, text); - - switch (msgType) - { - case LOG_INFO: fprintf(stdout, "INFO: "); break; - case LOG_ERROR: fprintf(stdout, "ERROR: "); break; - case LOG_WARNING: fprintf(stdout, "WARNING: "); break; - case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; - default: break; - } - - vfprintf(stdout, text, args); - fprintf(stdout, "\n"); - - va_end(args); - - if (msgType == LOG_ERROR) exit(1); -} -#endif - -#endif // RRES_IMPLEMENTATION diff --git a/src/text.c b/src/text.c index 8db2fc9f..0215bb5b 100644 --- a/src/text.c +++ b/src/text.c @@ -288,35 +288,8 @@ SpriteFont LoadSpriteFont(const char *fileName) SpriteFont spriteFont = { 0 }; - // Check file extension - if (IsFileExtension(fileName, ".rres")) - { - // TODO: Read multiple resource blocks from file (RRES_FONT_IMAGE, RRES_FONT_CHARDATA) - RRES rres = LoadResource(fileName, 0); - - // Load sprite font texture - if (rres[0].type == RRES_TYPE_FONT_IMAGE) - { - // NOTE: Parameters for RRES_FONT_IMAGE type are: width, height, format, mipmaps - Image image = LoadImagePro(rres[0].data, rres[0].param1, rres[0].param2, rres[0].param3); - spriteFont.texture = LoadTextureFromImage(image); - UnloadImage(image); - } - - // Load sprite characters data - if (rres[1].type == RRES_TYPE_FONT_CHARDATA) - { - // NOTE: Parameters for RRES_FONT_CHARDATA type are: fontSize, charsCount - spriteFont.baseSize = rres[1].param1; - spriteFont.charsCount = rres[1].param2; - spriteFont.chars = rres[1].data; - } - - // TODO: Do not free rres.data memory (chars info data!) - //UnloadResource(rres[0]); - } #if defined(SUPPORT_FILEFORMAT_TTF) - else if (IsFileExtension(fileName, ".ttf")) spriteFont = LoadSpriteFontEx(fileName, DEFAULT_TTF_FONTSIZE, 0, NULL); + if (IsFileExtension(fileName, ".ttf")) spriteFont = LoadSpriteFontEx(fileName, DEFAULT_TTF_FONTSIZE, 0, NULL); #endif #if defined(SUPPORT_FILEFORMAT_FNT) else if (IsFileExtension(fileName, ".fnt")) spriteFont = LoadBMFont(fileName); diff --git a/src/textures.c b/src/textures.c index 090de24b..6bd306f4 100644 --- a/src/textures.c +++ b/src/textures.c @@ -167,18 +167,7 @@ Image LoadImage(const char *fileName) { Image image = { 0 }; - if (IsFileExtension(fileName, ".rres")) - { - RRES rres = LoadResource(fileName, 0); - - // NOTE: Parameters for RRES_TYPE_IMAGE are: width, height, format, mipmaps - - if (rres[0].type == RRES_TYPE_IMAGE) image = LoadImagePro(rres[0].data, rres[0].param1, rres[0].param2, rres[0].param3); - else TraceLog(LOG_WARNING, "[%s] Resource file does not contain image data", fileName); - - UnloadResource(rres); - } - else if ((IsFileExtension(fileName, ".png")) + if ((IsFileExtension(fileName, ".png")) #if defined(SUPPORT_FILEFORMAT_BMP) || (IsFileExtension(fileName, ".bmp")) #endif diff --git a/src/utils.c b/src/utils.c index 967ed916..aaa5bdb4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -66,9 +66,6 @@ #include "external/stb_image_write.h" // Required for: stbi_write_bmp(), stbi_write_png() #endif -#define RRES_IMPLEMENTATION -#include "rres.h" - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- diff --git a/src/utils.h b/src/utils.h index 64592c73..a1801245 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,8 +32,6 @@ #include // Required for: AAssetManager #endif -#include "rres.h" - #define SUPPORT_SAVE_PNG //---------------------------------------------------------------------------------- -- cgit v1.2.3 From b63ffcfa0f7dcedac190a49ec48fd71d7fe2faf1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Dec 2017 00:34:31 +0100 Subject: Some code tweaks Audio module requires a complete formatting review.... --- src/audio.c | 31 +++++++++++++++++-------------- src/audio.h | 25 ++++++++++++++++--------- src/raylib.h | 32 ++++++++++---------------------- 3 files changed, 43 insertions(+), 45 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 15f50a24..7a8e185b 100644 --- a/src/audio.c +++ b/src/audio.c @@ -153,7 +153,12 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_AUDIO_FLAC, MUSIC_MODULE_XM, MUSIC_MODULE_MOD } MusicContextType; +typedef enum { + MUSIC_AUDIO_OGG = 0, + MUSIC_AUDIO_FLAC, + MUSIC_MODULE_XM, + MUSIC_MODULE_MOD +} MusicContextType; // Music type (file streaming from memory) typedef struct MusicData { @@ -179,7 +184,13 @@ typedef struct MusicData { } MusicData; #if defined(AUDIO_STANDALONE) -typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; +typedef enum { + LOG_INFO = 0, + LOG_ERROR, + LOG_WARNING, + LOG_DEBUG, + LOG_OTHER +} TraceLogType; #endif //---------------------------------------------------------------------------------- @@ -215,9 +226,8 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage; -typedef struct AudioBuffer AudioBuffer; -struct AudioBuffer -{ +// Audio buffer structure +typedef struct AudioBuffer { mal_dsp dsp; // For format conversion. float volume; float pitch; @@ -231,10 +241,9 @@ struct AudioBuffer AudioBuffer* next; AudioBuffer* prev; unsigned char buffer[1]; -}; - -void StopAudioBuffer(AudioBuffer* audioBuffer); +} AudioBuffer; +void StopAudioBuffer(AudioBuffer *audioBuffer); static mal_context context; static mal_device device; @@ -400,12 +409,6 @@ void InitAudioDevice(void) // Device. Using the default device. Format is floating point because it simplifies mixing. mal_device_config deviceConfig = mal_device_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, OnSendAudioDataToDevice); - // Special case for PLATFORM_RPI. -//#if defined(PLATFORM_RPI) -// deviceConfig.alsa.noMMap = MAL_TRUE; -// deviceConfig.bufferSizeInFrames = 2048; -//#endif - result = mal_device_init(&context, mal_device_type_playback, NULL, &deviceConfig, NULL, &device); if (result != MAL_SUCCESS) { diff --git a/src/audio.h b/src/audio.h index 48ef7403..4c9faf25 100644 --- a/src/audio.h +++ b/src/audio.h @@ -81,9 +81,11 @@ typedef struct Wave { // Sound source type typedef struct Sound { - unsigned int source; // OpenAL audio source id - unsigned int buffer; // OpenAL audio buffer id - int format; // OpenAL audio format specifier + void *audioBuffer; // Pointer to internal data used by the audio system + + unsigned int source; // Audio source id + unsigned int buffer; // Audio buffer id + int format; // Audio format specifier } Sound; // Music type (file streaming from memory) @@ -97,9 +99,11 @@ typedef struct AudioStream { unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) unsigned int channels; // Number of channels (1-mono, 2-stereo) - int format; // OpenAL audio format specifier - unsigned int source; // OpenAL audio source id - unsigned int buffers[2]; // OpenAL audio buffers (double buffering) + void *audioBuffer; // Pointer to internal data used by the audio system. + + int format; // Audio format specifier + unsigned int source; // Audio source id + unsigned int buffers[2]; // Audio buffers (double buffering) } AudioStream; #ifdef __cplusplus @@ -147,12 +151,12 @@ void ResumeMusicStream(Music music); // Resume playin bool IsMusicPlaying(Music music); // Check if music is playing void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) -void SetMusicLoopCount(Music music, float count); // Set music loop count (loop repeats) +void SetMusicLoopCount(Music music, int count); // Set music loop count (loop repeats) float GetMusicTimeLength(Music music); // Get music time length (in seconds) float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) -// Raw audio stream functions -AudioStream InitAudioStream(unsigned int sampleRate, +// AudioStream management functions +AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Init audio stream (to stream raw audio pcm data) void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data @@ -161,7 +165,10 @@ bool IsAudioBufferProcessed(AudioStream stream); // Check if any void PlayAudioStream(AudioStream stream); // Play audio stream void PauseAudioStream(AudioStream stream); // Pause audio stream void ResumeAudioStream(AudioStream stream); // Resume audio stream +bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing void StopAudioStream(AudioStream stream); // Stop audio stream +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) #ifdef __cplusplus } diff --git a/src/raylib.h b/src/raylib.h index 4100cee1..b0ff1fc1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -470,11 +470,11 @@ typedef struct Wave { // Sound source type typedef struct Sound { - void* audioBuffer; // A pointer to internal data used by the audio system. + void *audioBuffer; // Pointer to internal data used by the audio system - unsigned int source; // OpenAL audio source id - unsigned int buffer; // OpenAL audio buffer id - int format; // OpenAL audio format specifier + unsigned int source; // Audio source id + unsigned int buffer; // Audio buffer id + int format; // Audio format specifier } Sound; // Music type (file streaming from memory) @@ -488,11 +488,11 @@ typedef struct AudioStream { unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) unsigned int channels; // Number of channels (1-mono, 2-stereo) - void* audioBuffer; // A pointer to internal data used by the audio system. + void *audioBuffer; // Pointer to internal data used by the audio system. - int format; // OpenAL audio format specifier - unsigned int source; // OpenAL audio source id - unsigned int buffers[2]; // OpenAL audio buffers (double buffering) + int format; // Audio format specifier + unsigned int source; // Audio source id + unsigned int buffers[2]; // Audio buffers (double buffering) } AudioStream; // Head-Mounted-Display device parameters @@ -656,18 +656,6 @@ typedef enum { HMD_SONY_PSVR } VrDeviceType; -// RRESData type -typedef enum { - RRES_TYPE_RAW = 0, - RRES_TYPE_IMAGE, - RRES_TYPE_WAVE, - RRES_TYPE_VERTEX, - RRES_TYPE_TEXT, - RRES_TYPE_FONT_IMAGE, - RRES_TYPE_FONT_CHARDATA, // CharInfo data array - RRES_TYPE_DIRECTORY -} RRESDataType; - #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -1131,8 +1119,8 @@ RLAPI void PauseAudioStream(AudioStream stream); // Pause a RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream -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 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) #ifdef __cplusplus } -- cgit v1.2.3 From 1320044e94126a527e8ebe0b3833cce487566925 Mon Sep 17 00:00:00 2001 From: Ray San Date: Wed, 20 Dec 2017 11:37:43 +0100 Subject: Review code formatting --- src/audio.c | 280 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 135 insertions(+), 145 deletions(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 7a8e185b..4855333f 100644 --- a/src/audio.c +++ b/src/audio.c @@ -227,7 +227,8 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage; // Audio buffer structure -typedef struct AudioBuffer { +typedef struct AudioBuffer AudioBuffer; +struct AudioBuffer { mal_dsp dsp; // For format conversion. float volume; float pitch; @@ -238,10 +239,10 @@ typedef struct AudioBuffer { bool isSubBufferProcessed[2]; unsigned int frameCursorPos; unsigned int bufferSizeInFrames; - AudioBuffer* next; - AudioBuffer* prev; + AudioBuffer *next; + AudioBuffer *prev; unsigned char buffer[1]; -} AudioBuffer; +}; void StopAudioBuffer(AudioBuffer *audioBuffer); @@ -250,48 +251,46 @@ static mal_device device; static mal_bool32 isAudioInitialized = MAL_FALSE; static float masterVolume = 1; static mal_mutex audioLock; -static AudioBuffer* firstAudioBuffer = NULL; // Audio buffers are tracked in a linked list. -static AudioBuffer* lastAudioBuffer = NULL; +static AudioBuffer *firstAudioBuffer = NULL; // Audio buffers are tracked in a linked list. +static AudioBuffer *lastAudioBuffer = NULL; static void TrackAudioBuffer(AudioBuffer* audioBuffer) { mal_mutex_lock(&audioLock); + { - if (firstAudioBuffer == NULL) { - firstAudioBuffer = audioBuffer; - } else { + if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer; + else + { lastAudioBuffer->next = audioBuffer; audioBuffer->prev = lastAudioBuffer; } lastAudioBuffer = audioBuffer; } + mal_mutex_unlock(&audioLock); } static void UntrackAudioBuffer(AudioBuffer* audioBuffer) { mal_mutex_lock(&audioLock); + { - if (audioBuffer->prev == NULL) { - firstAudioBuffer = audioBuffer->next; - } else { - audioBuffer->prev->next = audioBuffer->next; - } + if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next; + else audioBuffer->prev->next = audioBuffer->next; - if (audioBuffer->next == NULL) { - lastAudioBuffer = audioBuffer->prev; - } else { - audioBuffer->next->prev = audioBuffer->prev; - } + if (audioBuffer->next == NULL) lastAudioBuffer = audioBuffer->prev; + else audioBuffer->next->prev = audioBuffer->prev; audioBuffer->prev = NULL; audioBuffer->next = NULL; } + mal_mutex_unlock(&audioLock); } -static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* message) +static void OnLog_MAL(mal_context *pContext, mal_device *pDevice, const char *message) { (void)pContext; (void)pDevice; @@ -303,17 +302,19 @@ static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* me // framesOut is both an input and an output. It will be initially filled with zeros outside of this function. static void MixFrames(float* framesOut, const float* framesIn, mal_uint32 frameCount, float localVolume) { - for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { - for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { - float* frameOut = framesOut + (iFrame * device.channels); - const float* frameIn = framesIn + (iFrame * device.channels); + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) + { + for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) + { + float *frameOut = framesOut + (iFrame*device.channels); + const float *frameIn = framesIn + (iFrame*device.channels); - frameOut[iChannel] += frameIn[iChannel] * masterVolume * localVolume; + frameOut[iChannel] += frameIn[iChannel]*masterVolume*localVolume; } } } -static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameCount, void* pFramesOut) +static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameCount, void *pFramesOut) { // This is where all of the mixing takes place. (void)pDevice; @@ -328,27 +329,28 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC for (AudioBuffer* audioBuffer = firstAudioBuffer; audioBuffer != NULL; audioBuffer = audioBuffer->next) { // Ignore stopped or paused sounds. - if (!audioBuffer->playing || audioBuffer->paused) { - continue; - } + if (!audioBuffer->playing || audioBuffer->paused) continue; mal_uint32 framesRead = 0; - for (;;) { - if (framesRead > frameCount) { + for (;;) + { + if (framesRead > frameCount) + { TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer"); break; } - if (framesRead == frameCount) { - break; - } + + if (framesRead == frameCount) break; // Just read as much data as we can from the stream. mal_uint32 framesToRead = (frameCount - framesRead); - while (framesToRead > 0) { + while (framesToRead > 0) + { float tempBuffer[1024]; // 512 frames for stereo. mal_uint32 framesToReadRightNow = framesToRead; - if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS) { + if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS) + { framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS; } @@ -357,9 +359,10 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC mal_bool32 flushDSP = !audioBuffer->looping; mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, flushDSP); - if (framesJustRead > 0) { - float* framesOut = (float*)pFramesOut + (framesRead * device.channels); - float* framesIn = tempBuffer; + if (framesJustRead > 0) + { + float *framesOut = (float *)pFramesOut + (framesRead*device.channels); + float *framesIn = tempBuffer; MixFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume); framesToRead -= framesJustRead; @@ -367,11 +370,15 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC } // If we weren't able to read all the frames we requested, break. - if (framesJustRead < framesToReadRightNow) { - if (!audioBuffer->looping) { + if (framesJustRead < framesToReadRightNow) + { + if (!audioBuffer->looping) + { StopAudioBuffer(audioBuffer); break; - } else { + } + else + { // Should never get here, but just for safety, move the cursor position back to the start and continue the loop. audioBuffer->frameCursorPos = 0; continue; @@ -381,12 +388,11 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameC // If for some reason we weren't able to read every frame we'll need to break from the loop. Not doing this could // theoretically put us into an infinite loop. - if (framesToRead > 0) { - break; - } + if (framesToRead > 0) break; } } } + mal_mutex_unlock(&audioLock); return frameCount; // We always output the same number of frames that were originally requested. @@ -488,7 +494,8 @@ void InitAudioDevice(void) void CloseAudioDevice(void) { #if USE_MINI_AL - if (!isAudioInitialized) { + if (!isAudioInitialized) + { TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); return; } @@ -551,11 +558,13 @@ void SetMasterVolume(float volume) #if USE_MINI_AL static mal_uint32 AudioBuffer_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) { - AudioBuffer* audioBuffer = (AudioBuffer*)pUserData; + AudioBuffer *audioBuffer = (AudioBuffer *)pUserData; - mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames / 2; - mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos / subBufferSizeInFrames; - if (currentSubBufferIndex > 1) { + mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; + mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; + + if (currentSubBufferIndex > 1) + { TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); return 0; } @@ -565,7 +574,7 @@ static mal_uint32 AudioBuffer_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, vo isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; - mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn) * audioBuffer->dsp.config.channelsIn; + mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn)*audioBuffer->dsp.config.channelsIn; // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. mal_uint32 framesRead = 0; @@ -573,49 +582,47 @@ static mal_uint32 AudioBuffer_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, vo { // We break from this loop differently depending on the buffer's usage. For static buffers, we simply fill as much data as we can. For // streaming buffers we only fill the halves of the buffer that are processed. Unprocessed halves must keep their audio data in-tact. - if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { - if (framesRead >= frameCount) { - break; - } - } else { - if (isSubBufferProcessed[currentSubBufferIndex]) { - break; - } + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) + { + if (framesRead >= frameCount) break; + } + else + { + if (isSubBufferProcessed[currentSubBufferIndex]) break; } mal_uint32 totalFramesRemaining = (frameCount - framesRead); - if (totalFramesRemaining == 0) { - break; - } + if (totalFramesRemaining == 0) break; mal_uint32 framesRemainingInOutputBuffer; - if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) + { framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos; - } else { + } + else + { mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); } - - mal_uint32 framesToRead = totalFramesRemaining; - if (framesToRead > framesRemainingInOutputBuffer) { - framesToRead = framesRemainingInOutputBuffer; - } + if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer; - memcpy((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); + memcpy((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead) % audioBuffer->bufferSizeInFrames; framesRead += framesToRead; // If we've read to the end of the buffer, mark it as processed. - if (framesToRead == framesRemainingInOutputBuffer) { + if (framesToRead == framesRemainingInOutputBuffer) + { audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; isSubBufferProcessed[currentSubBufferIndex] = true; currentSubBufferIndex = (currentSubBufferIndex + 1) % 2; // We need to break from this loop if we're not looping. - if (!audioBuffer->looping) { + if (!audioBuffer->looping) + { StopAudioBuffer(audioBuffer); break; } @@ -624,24 +631,23 @@ static mal_uint32 AudioBuffer_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, vo // Zero-fill excess. mal_uint32 totalFramesRemaining = (frameCount - framesRead); - if (totalFramesRemaining > 0) { + if (totalFramesRemaining > 0) + { memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); // For static buffers we can fill the remaining frames with silence for safety, but we don't want // to report those frames as "read". The reason for this is that the caller uses the return value // to know whether or not a non-looping sound has finished playback. - if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) { - framesRead += totalFramesRemaining; - } + if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining; } return framesRead; } // Create a new audio buffer. Initially filled with silence. -AudioBuffer* CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) +AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) { - AudioBuffer* audioBuffer = (AudioBuffer*)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*mal_get_sample_size_in_bytes(format)), 1); + AudioBuffer *audioBuffer = (AudioBuffer *)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*mal_get_sample_size_in_bytes(format)), 1); if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to allocate memory for audio buffer"); @@ -658,7 +664,8 @@ AudioBuffer* CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint3 dspConfig.sampleRateIn = sampleRate; dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE; mal_result resultMAL = mal_dsp_init(&dspConfig, AudioBuffer_OnDSPRead, audioBuffer, &audioBuffer->dsp); - if (resultMAL != MAL_SUCCESS) { + if (resultMAL != MAL_SUCCESS) + { TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create data conversion pipeline"); free(audioBuffer); return NULL; @@ -683,7 +690,7 @@ AudioBuffer* CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint3 } // Delete an audio buffer. -void DeleteAudioBuffer(AudioBuffer* audioBuffer) +void DeleteAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { @@ -696,7 +703,7 @@ void DeleteAudioBuffer(AudioBuffer* audioBuffer) } // Check if an audio buffer is playing. -bool IsAudioBufferPlaying(AudioBuffer* audioBuffer) +bool IsAudioBufferPlaying(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { @@ -711,7 +718,7 @@ bool IsAudioBufferPlaying(AudioBuffer* audioBuffer) // // This will restart the buffer from the start. Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position // should be maintained. -void PlayAudioBuffer(AudioBuffer* audioBuffer) +void PlayAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { @@ -725,7 +732,7 @@ void PlayAudioBuffer(AudioBuffer* audioBuffer) } // Stop an audio buffer. -void StopAudioBuffer(AudioBuffer* audioBuffer) +void StopAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { @@ -734,10 +741,7 @@ void StopAudioBuffer(AudioBuffer* audioBuffer) } // Don't do anything if the audio buffer is already stopped. - if (!IsAudioBufferPlaying(audioBuffer)) - { - return; - } + if (!IsAudioBufferPlaying(audioBuffer)) return; audioBuffer->playing = false; audioBuffer->paused = false; @@ -747,7 +751,7 @@ void StopAudioBuffer(AudioBuffer* audioBuffer) } // Pause an audio buffer. -void PauseAudioBuffer(AudioBuffer* audioBuffer) +void PauseAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { @@ -759,7 +763,7 @@ void PauseAudioBuffer(AudioBuffer* audioBuffer) } // Resume an audio buffer. -void ResumeAudioBuffer(AudioBuffer* audioBuffer) +void ResumeAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) { @@ -771,7 +775,7 @@ void ResumeAudioBuffer(AudioBuffer* audioBuffer) } // Set volume for an audio buffer. -void SetAudioBufferVolume(AudioBuffer* audioBuffer, float volume) +void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume) { if (audioBuffer == NULL) { @@ -783,7 +787,7 @@ void SetAudioBufferVolume(AudioBuffer* audioBuffer, float volume) } // Set pitch for an audio buffer. -void SetAudioBufferPitch(AudioBuffer* audioBuffer, float pitch) +void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch) { if (audioBuffer == NULL) { @@ -874,23 +878,13 @@ Sound LoadSoundFromWave(Wave wave) mal_uint32 frameCountIn = wave.sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. mal_uint32 frameCount = mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn); - if (frameCount == 0) { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion"); - } - + if (frameCount == 0) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion"); AudioBuffer* audioBuffer = CreateAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC); - if (audioBuffer == NULL) - { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create audio buffer"); - } - + if (audioBuffer == NULL) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create audio buffer"); frameCount = mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.config.formatIn, audioBuffer->dsp.config.channelsIn, audioBuffer->dsp.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); - if (frameCount == 0) - { - TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed"); - } + if (frameCount == 0) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed"); sound.audioBuffer = audioBuffer; #else @@ -965,7 +959,7 @@ void UnloadWave(Wave wave) void UnloadSound(Sound sound) { #if USE_MINI_AL - DeleteAudioBuffer((AudioBuffer*)sound.audioBuffer); + DeleteAudioBuffer((AudioBuffer *)sound.audioBuffer); #else alSourceStop(sound.source); @@ -981,7 +975,7 @@ void UnloadSound(Sound sound) void UpdateSound(Sound sound, const void *data, int samplesCount) { #if USE_MINI_AL - AudioBuffer* audioBuffer = (AudioBuffer*)sound.audioBuffer; + AudioBuffer *audioBuffer = (AudioBuffer *)sound.audioBuffer; if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); @@ -1021,7 +1015,7 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) void PlaySound(Sound sound) { #if USE_MINI_AL - PlayAudioBuffer((AudioBuffer*)sound.audioBuffer); + PlayAudioBuffer((AudioBuffer *)sound.audioBuffer); #else alSourcePlay(sound.source); // Play the sound #endif @@ -1046,7 +1040,7 @@ void PlaySound(Sound sound) void PauseSound(Sound sound) { #if USE_MINI_AL - PauseAudioBuffer((AudioBuffer*)sound.audioBuffer); + PauseAudioBuffer((AudioBuffer *)sound.audioBuffer); #else alSourcePause(sound.source); #endif @@ -1056,7 +1050,7 @@ void PauseSound(Sound sound) void ResumeSound(Sound sound) { #if USE_MINI_AL - ResumeAudioBuffer((AudioBuffer*)sound.audioBuffer); + ResumeAudioBuffer((AudioBuffer *)sound.audioBuffer); #else ALenum state; @@ -1070,7 +1064,7 @@ void ResumeSound(Sound sound) void StopSound(Sound sound) { #if USE_MINI_AL - StopAudioBuffer((AudioBuffer*)sound.audioBuffer); + StopAudioBuffer((AudioBuffer *)sound.audioBuffer); #else alSourceStop(sound.source); #endif @@ -1080,7 +1074,7 @@ void StopSound(Sound sound) bool IsSoundPlaying(Sound sound) { #if USE_MINI_AL - return IsAudioBufferPlaying((AudioBuffer*)sound.audioBuffer); + return IsAudioBufferPlaying((AudioBuffer *)sound.audioBuffer); #else bool playing = false; ALint state; @@ -1096,7 +1090,7 @@ bool IsSoundPlaying(Sound sound) void SetSoundVolume(Sound sound, float volume) { #if USE_MINI_AL - SetAudioBufferVolume((AudioBuffer*)sound.audioBuffer, volume); + SetAudioBufferVolume((AudioBuffer *)sound.audioBuffer, volume); #else alSourcef(sound.source, AL_GAIN, volume); #endif @@ -1106,7 +1100,7 @@ void SetSoundVolume(Sound sound, float volume) void SetSoundPitch(Sound sound, float pitch) { #if USE_MINI_AL - SetAudioBufferPitch((AudioBuffer*)sound.audioBuffer, pitch); + SetAudioBufferPitch((AudioBuffer *)sound.audioBuffer, pitch); #else alSourcef(sound.source, AL_PITCH, pitch); #endif @@ -1121,15 +1115,17 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. mal_uint32 frameCount = mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn); - if (frameCount == 0) { + if (frameCount == 0) + { TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion."); return; } - void* data = malloc(frameCount * channels * (sampleSize/8)); + void *data = malloc(frameCount*channels*(sampleSize/8)); frameCount = mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn); - if (frameCount == 0) { + if (frameCount == 0) + { TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed."); return; } @@ -1404,7 +1400,7 @@ void UnloadMusicStream(Music music) void PlayMusicStream(Music music) { #if USE_MINI_AL - AudioBuffer* audioBuffer = (AudioBuffer*)music->stream.audioBuffer; + AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer; if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); @@ -1416,9 +1412,9 @@ void PlayMusicStream(Music music) // // just make sure to play again on window restore // if (IsMusicPlaying(music)) PlayMusicStream(music); mal_uint32 frameCursorPos = audioBuffer->frameCursorPos; - { - PlayAudioStream(music->stream); // <-- This resets the cursor position. - } + + PlayAudioStream(music->stream); // <-- This resets the cursor position. + audioBuffer->frameCursorPos = frameCursorPos; #else alSourcePlay(music->stream.source); @@ -1502,7 +1498,7 @@ void UpdateMusicStream(Music music) #if USE_MINI_AL bool streamEnding = false; - unsigned int subBufferSizeInFrames = ((AudioBuffer*)music->stream.audioBuffer)->bufferSizeInFrames / 2; + unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.audioBuffer)->bufferSizeInFrames/2; // NOTE: Using dynamic allocation because it could require more than 16KB void *pcm = calloc(subBufferSizeInFrames*music->stream.sampleSize/8*music->stream.channels, 1); @@ -1566,10 +1562,7 @@ void UpdateMusicStream(Music music) } else { - if (music->loopCount == -1) - { - PlayMusicStream(music); - } + if (music->loopCount == -1) PlayMusicStream(music); } } else @@ -1756,11 +1749,9 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // The size of a streaming buffer must be at least double the size of a period. unsigned int periodSize = device.bufferSizeInFrames / device.periods; unsigned int subBufferSize = AUDIO_BUFFER_SIZE; - if (subBufferSize < periodSize) { - subBufferSize = periodSize; - } + if (subBufferSize < periodSize) subBufferSize = periodSize; - AudioBuffer* audioBuffer = CreateAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); + AudioBuffer *audioBuffer = CreateAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer"); @@ -1825,7 +1816,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un void CloseAudioStream(AudioStream stream) { #if USE_MINI_AL - DeleteAudioBuffer((AudioBuffer*)stream.audioBuffer); + DeleteAudioBuffer((AudioBuffer *)stream.audioBuffer); #else // Stop playing channel alSourceStop(stream.source); @@ -1879,23 +1870,22 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) } mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; - unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames * stream.channels * (stream.sampleSize/8)) * subBufferToUpdate); + unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate); // Does this API expect a whole buffer to be updated in one go? Assuming so, but if not will need to change this logic. if (subBufferSizeInFrames >= (mal_uint32)samplesCount) { mal_uint32 framesToWrite = subBufferSizeInFrames; - if (framesToWrite > (mal_uint32)samplesCount) { - framesToWrite = (mal_uint32)samplesCount; - } + if (framesToWrite > (mal_uint32)samplesCount) framesToWrite = (mal_uint32)samplesCount; - mal_uint32 bytesToWrite = framesToWrite * stream.channels * (stream.sampleSize/8); + mal_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); memcpy(subBuffer, data, bytesToWrite); // Any leftover frames should be filled with zeros. mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; - if (leftoverFrameCount > 0) { - memset(subBuffer + bytesToWrite, 0, leftoverFrameCount * stream.channels * (stream.sampleSize/8)); + if (leftoverFrameCount > 0) + { + memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); } audioBuffer->isSubBufferProcessed[subBufferToUpdate] = false; @@ -1929,7 +1919,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) bool IsAudioBufferProcessed(AudioStream stream) { #if USE_MINI_AL - AudioBuffer* audioBuffer = (AudioBuffer*)stream.audioBuffer; + AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer; if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "IsAudioBufferProcessed() : No audio buffer"); @@ -1951,7 +1941,7 @@ bool IsAudioBufferProcessed(AudioStream stream) void PlayAudioStream(AudioStream stream) { #if USE_MINI_AL - PlayAudioBuffer((AudioBuffer*)stream.audioBuffer); + PlayAudioBuffer((AudioBuffer *)stream.audioBuffer); #else alSourcePlay(stream.source); #endif @@ -1961,7 +1951,7 @@ void PlayAudioStream(AudioStream stream) void PauseAudioStream(AudioStream stream) { #if USE_MINI_AL - PauseAudioBuffer((AudioBuffer*)stream.audioBuffer); + PauseAudioBuffer((AudioBuffer *)stream.audioBuffer); #else alSourcePause(stream.source); #endif @@ -1971,7 +1961,7 @@ void PauseAudioStream(AudioStream stream) void ResumeAudioStream(AudioStream stream) { #if USE_MINI_AL - ResumeAudioBuffer((AudioBuffer*)stream.audioBuffer); + ResumeAudioBuffer((AudioBuffer *)stream.audioBuffer); #else ALenum state; alGetSourcei(stream.source, AL_SOURCE_STATE, &state); @@ -1984,7 +1974,7 @@ void ResumeAudioStream(AudioStream stream) bool IsAudioStreamPlaying(AudioStream stream) { #if USE_MINI_AL - return IsAudioBufferPlaying((AudioBuffer*)stream.audioBuffer); + return IsAudioBufferPlaying((AudioBuffer *)stream.audioBuffer); #else bool playing = false; ALint state; @@ -2001,7 +1991,7 @@ bool IsAudioStreamPlaying(AudioStream stream) void StopAudioStream(AudioStream stream) { #if USE_MINI_AL - StopAudioBuffer((AudioBuffer*)stream.audioBuffer); + StopAudioBuffer((AudioBuffer *)stream.audioBuffer); #else alSourceStop(stream.source); #endif @@ -2010,7 +2000,7 @@ void StopAudioStream(AudioStream stream) void SetAudioStreamVolume(AudioStream stream, float volume) { #if USE_MINI_AL - SetAudioBufferVolume((AudioBuffer*)stream.audioBuffer, volume); + SetAudioBufferVolume((AudioBuffer *)stream.audioBuffer, volume); #else alSourcef(stream.source, AL_GAIN, volume); #endif @@ -2019,7 +2009,7 @@ void SetAudioStreamVolume(AudioStream stream, float volume) void SetAudioStreamPitch(AudioStream stream, float pitch) { #if USE_MINI_AL - SetAudioBufferPitch((AudioBuffer*)stream.audioBuffer, pitch); + SetAudioBufferPitch((AudioBuffer *)stream.audioBuffer, pitch); #else alSourcef(stream.source, AL_PITCH, pitch); #endif -- cgit v1.2.3 From 61afd07bd7b3a96c6f0f460b668f52cf1a8bd90f Mon Sep 17 00:00:00 2001 From: Ray San Date: Wed, 20 Dec 2017 12:34:18 +0100 Subject: Force OpenAL backend on some platforms OpenAL audio backend is being forced on HTML5 and OSX --- src/Makefile | 27 ++++++++++++++++++++++++--- src/audio.c | 29 +++++++++++++++++------------ src/audio.h | 19 ++++++++++++------- 3 files changed, 53 insertions(+), 22 deletions(-) (limited to 'src/audio.c') diff --git a/src/Makefile b/src/Makefile index 00f5fa2f..1d1aa75c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -47,12 +47,20 @@ API_VERSION = 1 PLATFORM ?= PLATFORM_DESKTOP RAYLIB_PATH = .. +# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll) +RAYLIB_LIBTYPE ?= STATIC + # Included raylib audio module on compilation # NOTE: Some programs like tools could not require audio support INCLUDE_AUDIO_MODULE ?= YES -# Library type used for raylib: STATIC (.a) or SHARED (.so/.dll) -RAYLIB_LIBTYPE ?= STATIC +# Force OpenAL Soft backend for audio +FORCE_OPENAL_BACKEND ?= FALSE + +# OpenAL Soft audio backend forced on HTML5 and OSX (see below) +ifeq ($(PLATFORM),PLATFORM_WEB) + FORCE_OPENAL_BACKEND = TRUE +endif # Use cross-compiler for PLATFORM_RPI ifeq ($(PLATFORM),PLATFORM_RPI) @@ -95,6 +103,13 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif endif +# Force OpenAL Soft audio backend for OSX platform +# NOTE 1: mini_al library does not support CoreAudio yet +# NOTE 2: Required OpenAL libraries should be available on OSX +ifeq ($(PLATFORM_OS),OSX) + FORCE_OPENAL_BACKEND = TRUE +endif + ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk @@ -269,6 +284,10 @@ ifeq ($(RAYLIB_LIBTYPE),SHARED) CFLAGS += -fPIC -DBUILD_LIBTYPE_SHARED endif +ifeq ($(FORCE_OPENAL_BACKEND),TRUE) + CFLAGS += -DFORCE_OPENAL_BACKEND +endif + # Define include paths for required headers # NOTE: Several external required libraries (stb and others) INCLUDE_PATHS = -I. -Iexternal -Iexternal/glfw/include @@ -329,8 +348,10 @@ endif ifeq ($(INCLUDE_AUDIO_MODULE),YES) OBJS += audio.o - OBJS += mini_al.o OBJS += stb_vorbis.o +ifeq ($(FORCE_OPENAL_BACKEND),FALSE) + OBJS += mini_al.o +endif endif # Default target entry diff --git a/src/audio.c b/src/audio.c index 4855333f..b8ca60fd 100644 --- a/src/audio.c +++ b/src/audio.c @@ -16,6 +16,9 @@ * Define to use the module as standalone library (independently of raylib). * Required types and functions are defined in the same module. * +* #define FORCE_OPENAL_BACKEND +* Force OpenAL Soft audio backend usage +* * #define SUPPORT_FILEFORMAT_WAV * #define SUPPORT_FILEFORMAT_OGG * #define SUPPORT_FILEFORMAT_XM @@ -24,19 +27,24 @@ * Selected desired fileformats to be supported for loading. Some of those formats are * supported by default, to remove support, just comment unrequired #define in this module * -* LIMITATIONS: +* LIMITATIONS (only OpenAL Soft): * Only up to two channels supported: MONO and STEREO (for additional channels, use AL_EXT_MCFORMATS) * Only the following sample sizes supported: 8bit PCM, 16bit PCM, 32-bit float PCM (using AL_EXT_FLOAT32) * * DEPENDENCIES: -* OpenAL Soft - Audio device management (http://kcat.strangesoft.net/openal.html) +* mini_al - Audio device/context management (https://github.com/dr-soft/mini_al) * stb_vorbis - OGG audio files loading (http://www.nothings.org/stb_vorbis/) * jar_xm - XM module file loading * jar_mod - MOD audio file loading * dr_flac - FLAC audio file loading * +* *OpenAL Soft - Audio device management, still used on HTML5 and OSX platforms +* * CONTRIBUTORS: -* Joshua Reisenauer (github: @kd7tck): +* David Reid (github: @mackron) (Nov. 2017): +* - Complete port to mini_al library +* +* Joshua Reisenauer (github: @kd7tck) (2015) * - XM audio module support (jar_xm) * - MOD audio module support (jar_mod) * - Mixing channels support @@ -45,7 +53,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2017 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -72,8 +80,8 @@ #define SUPPORT_FILEFORMAT_MOD //------------------------------------------------- -#ifndef USE_MINI_AL -#define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. +#if !defined(FORCE_OPENAL_BACKEND) + #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. #endif #if defined(AUDIO_STANDALONE) @@ -86,7 +94,7 @@ #include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. -#if !defined(USE_MINI_AL) || USE_MINI_AL == 0 +#if !defined(USE_MINI_AL) || (USE_MINI_AL == 0) #if defined(__APPLE__) #include "OpenAL/al.h" // OpenAL basic header #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) @@ -480,11 +488,8 @@ void InitAudioDevice(void) alListenerf(AL_GAIN, 1.0f); - if (alIsExtensionPresent("AL_EXT_float32")) { - TraceLog(LOG_INFO, "AL_EXT_float32 supported"); - } else { - TraceLog(LOG_INFO, "AL_EXT_float32 not supported"); - } + if (alIsExtensionPresent("AL_EXT_float32")) TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 supported"); + else TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 not supported"); } } #endif diff --git a/src/audio.h b/src/audio.h index 4c9faf25..01c93741 100644 --- a/src/audio.h +++ b/src/audio.h @@ -10,19 +10,24 @@ * - Manage mixing channels * - Manage raw audio context * -* LIMITATIONS: +* LIMITATIONS (only OpenAL Soft): * Only up to two channels supported: MONO and STEREO (for additional channels, use AL_EXT_MCFORMATS) * Only the following sample sizes supported: 8bit PCM, 16bit PCM, 32-bit float PCM (using AL_EXT_FLOAT32) * * DEPENDENCIES: -* OpenAL Soft - Audio device management (http://kcat.strangesoft.net/openal.html) +* mini_al - Audio device/context management (https://github.com/dr-soft/mini_al) * stb_vorbis - OGG audio files loading (http://www.nothings.org/stb_vorbis/) -* jar_xm - XM module file loading (#define SUPPORT_FILEFORMAT_XM) -* jar_mod - MOD audio file loading (#define SUPPORT_FILEFORMAT_MOD) -* dr_flac - FLAC audio file loading (#define SUPPORT_FILEFORMAT_FLAC) +* jar_xm - XM module file loading +* jar_mod - MOD audio file loading +* dr_flac - FLAC audio file loading +* +* *OpenAL Soft - Audio device management, still used on HTML5 and OSX platforms * * CONTRIBUTORS: -* Joshua Reisenauer (github: @kd7tck): +* David Reid (github: @mackron) (Nov. 2017): +* - Complete port to mini_al library +* +* Joshua Reisenauer (github: @kd7tck) (2015) * - XM audio module support (jar_xm) * - MOD audio module support (jar_mod) * - Mixing channels support @@ -31,7 +36,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2017 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. -- cgit v1.2.3 From c93bca8c272af83ae752afb46db2b54d85dca454 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 28 Dec 2017 17:58:09 +0100 Subject: Review Makefile config flags Support external GLFW usage Renamed some flags for consistency --- examples/Makefile | 6 ++++++ src/Makefile | 38 +++++++++++++++++++++++--------------- src/audio.c | 6 +++--- 3 files changed, 32 insertions(+), 18 deletions(-) (limited to 'src/audio.c') diff --git a/examples/Makefile b/examples/Makefile index fe3ff205..c9081b2e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -33,6 +33,9 @@ ifeq ($(PLATFORM),PLATFORM_RPI) RAYLIB_PATH ?= /home/pi/raylib endif +# Use external GLFW library instead of rglfw module +USE_EXTERNAL_GLFW ?= FALSE + # Library type used for raylib: STATIC (.a) or SHARED (.so/.dll) RAYLIB_LIBTYPE ?= STATIC @@ -235,6 +238,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) # On XWindow requires also below libraries LDLIBS += -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor endif + ifeq ($(USE_EXTERNAL_GLFW),TRUE) + LDLIBS += -lglfw + endif endif ifeq ($(PLATFORM),PLATFORM_RPI) # Libraries for Raspberry Pi compiling diff --git a/src/Makefile b/src/Makefile index 1d1aa75c..de9a639e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -52,21 +52,24 @@ RAYLIB_LIBTYPE ?= STATIC # Included raylib audio module on compilation # NOTE: Some programs like tools could not require audio support -INCLUDE_AUDIO_MODULE ?= YES +INCLUDE_AUDIO_MODULE ?= TRUE # Force OpenAL Soft backend for audio -FORCE_OPENAL_BACKEND ?= FALSE +USE_OPENAL_BACKEND ?= FALSE + +# Use external GLFW library instead of rglfw module +USE_EXTERNAL_GLFW ?= FALSE # OpenAL Soft audio backend forced on HTML5 and OSX (see below) ifeq ($(PLATFORM),PLATFORM_WEB) - FORCE_OPENAL_BACKEND = TRUE + USE_OPENAL_BACKEND = TRUE endif # Use cross-compiler for PLATFORM_RPI ifeq ($(PLATFORM),PLATFORM_RPI) - RPI_CROSS_COMPILE ?= NO + USE_RPI_CROSS_COMPILER ?= FALSE - ifeq ($(RPI_CROSS_COMPILE),YES) + ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot endif @@ -107,7 +110,7 @@ endif # NOTE 1: mini_al library does not support CoreAudio yet # NOTE 2: Required OpenAL libraries should be available on OSX ifeq ($(PLATFORM_OS),OSX) - FORCE_OPENAL_BACKEND = TRUE + USE_OPENAL_BACKEND = TRUE endif ifeq ($(PLATFORM),PLATFORM_WEB) @@ -201,7 +204,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(RPI_CROSS_COMPILE),YES) + ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) # Define RPI cross-compiler #CC = armv6j-hardfloat-linux-gnueabi-gcc CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc @@ -226,7 +229,7 @@ endif AR = ar ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(RPI_CROSS_COMPILE),YES) + ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) # Define RPI cross-archiver #CC = armv6j-hardfloat-linux-gnueabi-gcc AR = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-ar @@ -284,8 +287,8 @@ ifeq ($(RAYLIB_LIBTYPE),SHARED) CFLAGS += -fPIC -DBUILD_LIBTYPE_SHARED endif -ifeq ($(FORCE_OPENAL_BACKEND),TRUE) - CFLAGS += -DFORCE_OPENAL_BACKEND +ifeq ($(USE_OPENAL_BACKEND),TRUE) + CFLAGS += -DUSE_OPENAL_BACKEND endif # Define include paths for required headers @@ -297,6 +300,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I/usr/local/include LDFLAGS += -L. -Lsrc -L/usr/local/lib -L$(RAYLIB_RELEASE_PATH) endif + ifeq ($(USE_EXTERNAL_GLFW),TRUE) + LDFLAGS += -lglfw + endif endif # Define additional directories containing required header files @@ -343,15 +349,17 @@ OBJS = core.o \ utils.o ifeq ($(PLATFORM),PLATFORM_DESKTOP) - OBJS += rglfw.o + ifeq ($(USE_EXTERNAL_GLFW),FALSE) + OBJS += rglfw.o + endif endif -ifeq ($(INCLUDE_AUDIO_MODULE),YES) +ifeq ($(INCLUDE_AUDIO_MODULE),TRUE) OBJS += audio.o OBJS += stb_vorbis.o -ifeq ($(FORCE_OPENAL_BACKEND),FALSE) - OBJS += mini_al.o -endif + ifeq ($(USE_OPENAL_BACKEND),FALSE) + OBJS += mini_al.o + endif endif # Default target entry diff --git a/src/audio.c b/src/audio.c index b8ca60fd..17b8ea22 100644 --- a/src/audio.c +++ b/src/audio.c @@ -16,8 +16,8 @@ * Define to use the module as standalone library (independently of raylib). * Required types and functions are defined in the same module. * -* #define FORCE_OPENAL_BACKEND -* Force OpenAL Soft audio backend usage +* #define USE_OPENAL_BACKEND +* Use OpenAL Soft audio backend usage * * #define SUPPORT_FILEFORMAT_WAV * #define SUPPORT_FILEFORMAT_OGG @@ -80,7 +80,7 @@ #define SUPPORT_FILEFORMAT_MOD //------------------------------------------------- -#if !defined(FORCE_OPENAL_BACKEND) +#if !defined(USE_OPENAL_BACKEND) #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. #endif -- cgit v1.2.3 From 230e78a23e3482aaa3e16f56ade3c940712d1272 Mon Sep 17 00:00:00 2001 From: - <-> Date: Mon, 15 Jan 2018 13:54:25 +0100 Subject: Code tweak while using OpenAL backend --- src/audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/audio.c') diff --git a/src/audio.c b/src/audio.c index 17b8ea22..052d4d3c 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1114,6 +1114,7 @@ void SetSoundPitch(Sound sound, float pitch) // Convert wave data to desired format void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) { +#if USE_MINI_AL mal_format formatIn = ((wave->sampleSize == 8) ? mal_format_u8 : ((wave->sampleSize == 16) ? mal_format_s16 : mal_format_f32)); mal_format formatOut = (( sampleSize == 8) ? mal_format_u8 : (( sampleSize == 16) ? mal_format_s16 : mal_format_f32)); @@ -1142,7 +1143,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) free(wave->data); wave->data = data; -#if 0 +#else // Format sample rate // NOTE: Only supported 22050 <--> 44100 if (wave->sampleRate != sampleRate) -- cgit v1.2.3