mirror of
https://github.com/raysan5/raylib.git
synced 2025-09-13 14:58:15 +00:00
Initial work on adding support for mini_al.
This commit is contained in:
390
src/audio.c
390
src/audio.c
@@ -72,6 +72,8 @@
|
|||||||
#define SUPPORT_FILEFORMAT_MOD
|
#define SUPPORT_FILEFORMAT_MOD
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
|
#define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL.
|
||||||
|
|
||||||
#if defined(AUDIO_STANDALONE)
|
#if defined(AUDIO_STANDALONE)
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
|
#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
|
||||||
@@ -80,6 +82,9 @@
|
|||||||
#include "utils.h" // Required for: fopen() Android mapping
|
#include "utils.h" // Required for: fopen() Android mapping
|
||||||
#endif
|
#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
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
#include "OpenAL/al.h" // OpenAL basic header
|
#include "OpenAL/al.h" // OpenAL basic header
|
||||||
#include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work)
|
#include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work)
|
||||||
@@ -91,6 +96,7 @@
|
|||||||
|
|
||||||
// OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples
|
// 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)
|
// OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1)
|
||||||
|
//#endif
|
||||||
|
|
||||||
#include <stdlib.h> // Required for: malloc(), free()
|
#include <stdlib.h> // Required for: malloc(), free()
|
||||||
#include <string.h> // Required for: strcmp(), strncmp()
|
#include <string.h> // 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
|
// 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
|
// Initialize audio device
|
||||||
void InitAudioDevice(void)
|
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
|
// Open and initialize a device with default settings
|
||||||
ALCdevice *device = alcOpenDevice(NULL);
|
ALCdevice *device = alcOpenDevice(NULL);
|
||||||
|
|
||||||
@@ -230,13 +421,30 @@ void InitAudioDevice(void)
|
|||||||
alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f);
|
alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f);
|
||||||
|
|
||||||
alListenerf(AL_GAIN, 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
|
// Close the audio device for all contexts
|
||||||
void CloseAudioDevice(void)
|
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;
|
ALCdevice *device;
|
||||||
ALCcontext *context = alcGetCurrentContext();
|
ALCcontext *context = alcGetCurrentContext();
|
||||||
|
|
||||||
@@ -247,6 +455,7 @@ void CloseAudioDevice(void)
|
|||||||
alcMakeContextCurrent(NULL);
|
alcMakeContextCurrent(NULL);
|
||||||
alcDestroyContext(context);
|
alcDestroyContext(context);
|
||||||
alcCloseDevice(device);
|
alcCloseDevice(device);
|
||||||
|
#endif
|
||||||
|
|
||||||
TraceLog(LOG_INFO, "Audio device closed successfully");
|
TraceLog(LOG_INFO, "Audio device closed successfully");
|
||||||
}
|
}
|
||||||
@@ -254,6 +463,9 @@ void CloseAudioDevice(void)
|
|||||||
// Check if device has been initialized successfully
|
// Check if device has been initialized successfully
|
||||||
bool IsAudioDeviceReady(void)
|
bool IsAudioDeviceReady(void)
|
||||||
{
|
{
|
||||||
|
#if USE_MINI_AL
|
||||||
|
return isAudioInitialized;
|
||||||
|
#else
|
||||||
ALCcontext *context = alcGetCurrentContext();
|
ALCcontext *context = alcGetCurrentContext();
|
||||||
|
|
||||||
if (context == NULL) return false;
|
if (context == NULL) return false;
|
||||||
@@ -264,6 +476,7 @@ bool IsAudioDeviceReady(void)
|
|||||||
if (device == NULL) return false;
|
if (device == NULL) return false;
|
||||||
else return true;
|
else return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set master volume (listener)
|
// Set master volume (listener)
|
||||||
@@ -272,7 +485,11 @@ void SetMasterVolume(float volume)
|
|||||||
if (volume < 0.0f) volume = 0.0f;
|
if (volume < 0.0f) volume = 0.0f;
|
||||||
else if (volume > 1.0f) volume = 1.0f;
|
else if (volume > 1.0f) volume = 1.0f;
|
||||||
|
|
||||||
|
#if USE_MINI_AL
|
||||||
|
masterVolume = 1;
|
||||||
|
#else
|
||||||
alListenerf(AL_GAIN, volume);
|
alListenerf(AL_GAIN, volume);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
@@ -349,6 +566,47 @@ Sound LoadSoundFromWave(Wave wave)
|
|||||||
|
|
||||||
if (wave.data != NULL)
|
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;
|
ALenum format = 0;
|
||||||
|
|
||||||
// The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample)
|
// 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.source = source;
|
||||||
sound.buffer = buffer;
|
sound.buffer = buffer;
|
||||||
sound.format = format;
|
sound.format = format;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return sound;
|
return sound;
|
||||||
@@ -418,10 +677,16 @@ void UnloadWave(Wave wave)
|
|||||||
// Unload sound
|
// Unload sound
|
||||||
void UnloadSound(Sound sound)
|
void UnloadSound(Sound sound)
|
||||||
{
|
{
|
||||||
|
#if USE_MINI_AL
|
||||||
|
SoundInternal* internalSound = (SoundInternal*)sound.handle;
|
||||||
|
RemoveSound(internalSound);
|
||||||
|
free(internalSound);
|
||||||
|
#else
|
||||||
alSourceStop(sound.source);
|
alSourceStop(sound.source);
|
||||||
|
|
||||||
alDeleteSources(1, &sound.source);
|
alDeleteSources(1, &sound.source);
|
||||||
alDeleteBuffers(1, &sound.buffer);
|
alDeleteBuffers(1, &sound.buffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer);
|
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
|
// NOTE: data must match sound.format
|
||||||
void UpdateSound(Sound sound, const void *data, int samplesCount)
|
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;
|
ALint sampleRate, sampleSize, channels;
|
||||||
alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate);
|
alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate);
|
||||||
alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format
|
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
|
// Attach sound buffer to source again
|
||||||
alSourcei(sound.source, AL_BUFFER, sound.buffer);
|
alSourcei(sound.source, AL_BUFFER, sound.buffer);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play a sound
|
// Play a sound
|
||||||
void PlaySound(Sound 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
|
alSourcePlay(sound.source); // Play the sound
|
||||||
|
#endif
|
||||||
|
|
||||||
//TraceLog(LOG_INFO, "Playing sound");
|
//TraceLog(LOG_INFO, "Playing sound");
|
||||||
|
|
||||||
@@ -477,28 +772,72 @@ void PlaySound(Sound sound)
|
|||||||
// Pause a sound
|
// Pause a sound
|
||||||
void PauseSound(Sound 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);
|
alSourcePause(sound.source);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume a paused sound
|
// Resume a paused sound
|
||||||
void ResumeSound(Sound 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;
|
ALenum state;
|
||||||
|
|
||||||
alGetSourcei(sound.source, AL_SOURCE_STATE, &state);
|
alGetSourcei(sound.source, AL_SOURCE_STATE, &state);
|
||||||
|
|
||||||
if (state == AL_PAUSED) alSourcePlay(sound.source);
|
if (state == AL_PAUSED) alSourcePlay(sound.source);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop reproducing a sound
|
// Stop reproducing a sound
|
||||||
void StopSound(Sound 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);
|
alSourceStop(sound.source);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a sound is playing
|
// Check if a sound is playing
|
||||||
bool IsSoundPlaying(Sound sound)
|
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;
|
bool playing = false;
|
||||||
ALint state;
|
ALint state;
|
||||||
|
|
||||||
@@ -506,23 +845,73 @@ bool IsSoundPlaying(Sound sound)
|
|||||||
if (state == AL_PLAYING) playing = true;
|
if (state == AL_PLAYING) playing = true;
|
||||||
|
|
||||||
return playing;
|
return playing;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set volume for a sound
|
// Set volume for a sound
|
||||||
void SetSoundVolume(Sound sound, float volume)
|
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);
|
alSourcef(sound.source, AL_GAIN, volume);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set pitch for a sound
|
// Set pitch for a sound
|
||||||
void SetSoundPitch(Sound sound, float pitch)
|
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);
|
alSourcef(sound.source, AL_PITCH, pitch);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert wave data to desired format
|
// Convert wave data to desired format
|
||||||
void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
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
|
// Format sample rate
|
||||||
// NOTE: Only supported 22050 <--> 44100
|
// NOTE: Only supported 22050 <--> 44100
|
||||||
if (wave->sampleRate != sampleRate)
|
if (wave->sampleRate != sampleRate)
|
||||||
@@ -601,6 +990,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
|
|||||||
free(wave->data);
|
free(wave->data);
|
||||||
wave->data = data;
|
wave->data = data;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy a wave to a new wave
|
// Copy a wave to a new wave
|
||||||
|
2
src/external/mini_al.c
vendored
Normal file
2
src/external/mini_al.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define MAL_IMPLEMENTATION
|
||||||
|
#include "mini_al.h" // <-- The implementation of mini_al.h #includes windows.h, so need to #undef some stuff.
|
10437
src/external/mini_al.h
vendored
Normal file
10437
src/external/mini_al.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -486,6 +486,8 @@ typedef struct Wave {
|
|||||||
|
|
||||||
// Sound source type
|
// Sound source type
|
||||||
typedef struct Sound {
|
typedef struct Sound {
|
||||||
|
void* handle; // A pointer to internal data used by the sound system.
|
||||||
|
|
||||||
unsigned int source; // OpenAL audio source id
|
unsigned int source; // OpenAL audio source id
|
||||||
unsigned int buffer; // OpenAL audio buffer id
|
unsigned int buffer; // OpenAL audio buffer id
|
||||||
int format; // OpenAL audio format specifier
|
int format; // OpenAL audio format specifier
|
||||||
|
Reference in New Issue
Block a user