summaryrefslogtreecommitdiffhomepage
path: root/src/audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio.c')
-rw-r--r--src/audio.c228
1 files changed, 150 insertions, 78 deletions
diff --git a/src/audio.c b/src/audio.c
index 659ead0f..b81d5572 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -1,26 +1,22 @@
/**********************************************************************************************
*
-* raylib.audio
+* raylib.audio - Basic funtionality to work with audio
*
-* This module provides basic functionality to work with audio:
-* Manage audio device (init/close)
-* Load and Unload audio files (WAV, OGG, FLAC, XM, MOD)
-* Play/Stop/Pause/Resume loaded audio
-* Manage mixing channels
-* Manage raw audio context
-*
-* NOTES:
-*
-* 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)
+* FEATURES:
+* - Manage audio device (init/close)
+* - Load and unload audio files
+* - Format wave data (sample rate, size, channels)
+* - Play/Stop/Pause/Resume loaded audio
+* - Manage mixing channels
+* - Manage raw audio context
*
* CONFIGURATION:
*
* #define AUDIO_STANDALONE
-* If defined, the module can be used as standalone library (independently of raylib).
+* Define to use the module as standalone library (independently of raylib).
* Required types and functions are defined in the same module.
*
-* #define SUPPORT_FILEFORMAT_WAV / SUPPORT_LOAD_WAV / ENABLE_LOAD_WAV
+* #define SUPPORT_FILEFORMAT_WAV
* #define SUPPORT_FILEFORMAT_OGG
* #define SUPPORT_FILEFORMAT_XM
* #define SUPPORT_FILEFORMAT_MOD
@@ -28,7 +24,9 @@
* 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
*
-* #define SUPPORT_RAW_AUDIO_BUFFERS
+* LIMITATIONS:
+* 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)
@@ -38,17 +36,16 @@
* dr_flac - FLAC audio file loading
*
* CONTRIBUTORS:
-*
-* Many thanks to Joshua Reisenauer (github: @kd7tck) for the following additions:
-* XM audio module support (jar_xm)
-* MOD audio module support (jar_mod)
-* Mixing channels support
-* Raw audio context support
+* Joshua Reisenauer (github: @kd7tck):
+* - XM audio module support (jar_xm)
+* - MOD audio module support (jar_mod)
+* - Mixing channels support
+* - Raw audio context support
*
*
* LICENSE: zlib/libpng
*
-* Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
+* Copyright (c) 2014-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.
@@ -67,17 +64,22 @@
*
**********************************************************************************************/
-//#define AUDIO_STANDALONE // NOTE: To use the audio module as standalone lib, just uncomment this line
+// Default configuration flags (supported features)
+//-------------------------------------------------
+#define SUPPORT_FILEFORMAT_WAV
+#define SUPPORT_FILEFORMAT_OGG
+#define SUPPORT_FILEFORMAT_XM
+//-------------------------------------------------
#if defined(AUDIO_STANDALONE)
#include "audio.h"
#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
#else
#include "raylib.h"
- #include "utils.h" // Required for: fopen() Android mapping, TraceLog()
+ #include "utils.h" // Required for: fopen() Android mapping
#endif
-#ifdef __APPLE__
+#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
@@ -93,18 +95,26 @@
#include <string.h> // Required for: strcmp(), strncmp()
#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
-//#define STB_VORBIS_HEADER_ONLY
-#include "external/stb_vorbis.h" // OGG loading functions
+#if defined(SUPPORT_FILEFORMAT_OGG)
+ //#define STB_VORBIS_HEADER_ONLY
+ #include "external/stb_vorbis.h" // OGG loading functions
+#endif
-#define JAR_XM_IMPLEMENTATION
-#include "external/jar_xm.h" // XM loading functions
+#if defined(SUPPORT_FILEFORMAT_XM)
+ #define JAR_XM_IMPLEMENTATION
+ #include "external/jar_xm.h" // XM loading functions
+#endif
-#define JAR_MOD_IMPLEMENTATION
-#include "external/jar_mod.h" // MOD loading functions
+#if defined(SUPPORT_FILEFORMAT_MOD)
+ #define JAR_MOD_IMPLEMENTATION
+ #include "external/jar_mod.h" // MOD loading functions
+#endif
-#define DR_FLAC_IMPLEMENTATION
-#define DR_FLAC_NO_WIN32_IO
-#include "external/dr_flac.h" // FLAC loading functions
+#if defined(SUPPORT_FILEFORMAT_FLAC)
+ #define DR_FLAC_IMPLEMENTATION
+ #define DR_FLAC_NO_WIN32_IO
+ #include "external/dr_flac.h" // FLAC loading functions
+#endif
#ifdef _MSC_VER
#undef bool
@@ -139,10 +149,18 @@ typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_AUDIO_FLAC, MUSIC_MODULE_XM, MUSIC_MOD
// Music type (file streaming from memory)
typedef struct MusicData {
MusicContextType ctxType; // Type of music context (OGG, XM, MOD)
+#if defined(SUPPORT_FILEFORMAT_OGG)
stb_vorbis *ctxOgg; // OGG audio context
+#endif
+#if defined(SUPPORT_FILEFORMAT_FLAC)
drflac *ctxFlac; // FLAC audio context
+#endif
+#if defined(SUPPORT_FILEFORMAT_XM)
jar_xm_context_t *ctxXm; // XM chiptune context
+#endif
+#if defined(SUPPORT_FILEFORMAT_MOD)
jar_mod_context_t ctxMod; // MOD chiptune context
+#endif
AudioStream stream; // Audio stream (double buffering)
@@ -163,13 +181,19 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
+#if defined(SUPPORT_FILEFORMAT_WAV)
static Wave LoadWAV(const char *fileName); // Load WAV file
+#endif
+#if defined(SUPPORT_FILEFORMAT_OGG)
static Wave LoadOGG(const char *fileName); // Load OGG file
+#endif
+#if defined(SUPPORT_FILEFORMAT_FLAC)
static Wave LoadFLAC(const char *fileName); // Load FLAC file
+#endif
#if defined(AUDIO_STANDALONE)
-const char *GetExtension(const char *fileName); // Get the extension for a filename
-void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message (INFO, ERROR, WARNING)
+bool IsFileExtension(const char *fileName, const char *ext); // Check file extension
+void TraceLog(int msgType, const char *text, ...); // Outputs trace log message (INFO, ERROR, WARNING)
#endif
//----------------------------------------------------------------------------------
@@ -259,10 +283,15 @@ Wave LoadWave(const char *fileName)
{
Wave wave = { 0 };
- if (strcmp(GetExtension(fileName), "wav") == 0) wave = LoadWAV(fileName);
- else if (strcmp(GetExtension(fileName), "ogg") == 0) wave = LoadOGG(fileName);
- else if (strcmp(GetExtension(fileName), "flac") == 0) wave = LoadFLAC(fileName);
- else if (strcmp(GetExtension(fileName),"rres") == 0)
+ if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
+#if defined(SUPPORT_FILEFORMAT_OGG)
+ else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(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);
@@ -273,7 +302,8 @@ Wave LoadWave(const char *fileName)
UnloadResource(rres);
}
- else TraceLog(WARNING, "[%s] File extension not recognized, it can't be loaded", fileName);
+#endif
+ else TraceLog(WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName);
return wave;
}
@@ -540,7 +570,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
// NOTE: Only supported mono <--> stereo
if (wave->channels != channels)
{
- void *data = malloc(wave->sampleCount*channels*wave->sampleSize/8);
+ void *data = malloc(wave->sampleCount*wave->sampleSize/8*channels);
if ((wave->channels == 1) && (channels == 2)) // mono ---> stereo (duplicate mono information)
{
@@ -577,7 +607,7 @@ Wave WaveCopy(Wave wave)
{
Wave newWave = { 0 };
- newWave.data = malloc(wave.sampleCount*wave.channels*wave.sampleSize/8);
+ newWave.data = malloc(wave.sampleCount*wave.sampleSize/8*wave.channels);
if (newWave.data != NULL)
{
@@ -602,7 +632,7 @@ void WaveCrop(Wave *wave, int initSample, int finalSample)
{
int sampleCount = finalSample - initSample;
- void *data = malloc(sampleCount*wave->channels*wave->sampleSize/8);
+ void *data = malloc(sampleCount*wave->sampleSize/8*wave->channels);
memcpy(data, (unsigned char*)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8);
@@ -640,7 +670,7 @@ Music LoadMusicStream(const char *fileName)
{
Music music = (MusicData *)malloc(sizeof(MusicData));
- if (strcmp(GetExtension(fileName), "ogg") == 0)
+ if (IsFileExtension(fileName, ".ogg"))
{
// Open ogg audio stream
music->ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL);
@@ -663,7 +693,8 @@ Music LoadMusicStream(const char *fileName)
TraceLog(DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required);
}
}
- else if (strcmp(GetExtension(fileName), "flac") == 0)
+#if defined(SUPPORT_FILEFORMAT_FLAC)
+ else if (IsFileExtension(fileName, ".flac"))
{
music->ctxFlac = drflac_open_file(fileName);
@@ -682,7 +713,9 @@ Music LoadMusicStream(const char *fileName)
TraceLog(DEBUG, "[%s] FLAC channels: %i", fileName, music->ctxFlac->channels);
}
}
- else if (strcmp(GetExtension(fileName), "xm") == 0)
+#endif
+#if defined(SUPPORT_FILEFORMAT_XM)
+ else if (IsFileExtension(fileName, ".xm"))
{
int result = jar_xm_create_context_from_file(&music->ctxXm, 48000, fileName);
@@ -702,7 +735,9 @@ Music LoadMusicStream(const char *fileName)
}
else TraceLog(WARNING, "[%s] XM file could not be opened", fileName);
}
- else if (strcmp(GetExtension(fileName), "mod") == 0)
+#endif
+#if defined(SUPPORT_FILEFORMAT_MOD)
+ else if (IsFileExtension(fileName, ".mod"))
{
jar_mod_init(&music->ctxMod);
@@ -719,7 +754,8 @@ Music LoadMusicStream(const char *fileName)
}
else TraceLog(WARNING, "[%s] MOD file could not be opened", fileName);
}
- else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
+#endif
+ else TraceLog(WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName);
return music;
}
@@ -730,9 +766,15 @@ void UnloadMusicStream(Music music)
CloseAudioStream(music->stream);
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
+#if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
+#endif
+#if defined(SUPPORT_FILEFORMAT_XM)
else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context(music->ctxXm);
+#endif
+#if defined(SUPPORT_FILEFORMAT_MOD)
else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod);
+#endif
free(music);
}
@@ -755,30 +797,46 @@ void ResumeMusicStream(Music music)
ALenum state;
alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state);
- if (state == AL_PAUSED) alSourcePlay(music->stream.source);
+ if (state == AL_PAUSED)
+ {
+ TraceLog(INFO, "[AUD ID %i] Resume music stream playing", music->stream.source);
+ alSourcePlay(music->stream.source);
+ }
}
// Stop music playing (close stream)
+// TODO: To clear a buffer, make sure they have been already processed!
void StopMusicStream(Music music)
{
alSourceStop(music->stream.source);
+ /*
// Clear stream buffers
+ // WARNING: Queued buffers must have been processed before unqueueing and reloaded with data!!!
void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1);
-
+
for (int i = 0; i < MAX_STREAM_BUFFERS; i++)
{
+ //UpdateAudioStream(music->stream, pcm, AUDIO_BUFFER_SIZE); // Update one buffer at a time
alBufferData(music->stream.buffers[i], music->stream.format, pcm, AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, music->stream.sampleRate);
}
free(pcm);
+ */
// Restart music context
switch (music->ctxType)
{
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break;
+#if defined(SUPPORT_FILEFORMAT_FLAC)
+ case MUSIC_MODULE_FLAC: /* TODO: Restart FLAC context */ break;
+#endif
+#if defined(SUPPORT_FILEFORMAT_XM)
case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break;
+#endif
+#if defined(SUPPORT_FILEFORMAT_MOD)
case MUSIC_MODULE_MOD: jar_mod_seek_start(&music->ctxMod); break;
+#endif
default: break;
}
@@ -797,14 +855,14 @@ void UpdateMusicStream(Music music)
if (processed > 0)
{
- bool active = true;
+ bool streamEnding = false;
// NOTE: Using dynamic allocation because it could require more than 16KB
- void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.channels*music->stream.sampleSize/8, 1);
+ void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1);
int numBuffersToProcess = processed;
int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats,
- //individual L or R for ogg shorts
+ // individual L or R for ogg shorts
for (int i = 0; i < numBuffersToProcess; i++)
{
@@ -820,14 +878,20 @@ void UpdateMusicStream(Music music)
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;
}
@@ -836,16 +900,16 @@ void UpdateMusicStream(Music music)
if (music->samplesLeft <= 0)
{
- active = false;
+ streamEnding = true;
break;
}
}
-
- // This error is registered when UpdateAudioStream() fails
- if (alGetError() == AL_INVALID_VALUE) TraceLog(WARNING, "OpenAL: Error buffering data...");
+
+ // Free allocated pcm data
+ free(pcm);
// Reset audio stream for looping
- if (!active)
+ if (streamEnding)
{
StopMusicStream(music); // Stop music (and reset)
@@ -862,8 +926,6 @@ void UpdateMusicStream(Music music)
// just make sure to play again on window restore
if (state != AL_PLAYING) PlayMusicStream(music);
}
-
- free(pcm);
}
}
@@ -1010,7 +1072,8 @@ void CloseAudioStream(AudioStream stream)
}
// Update audio stream buffers with data
-// NOTE: Only updates one buffer per call
+// NOTE 1: Only updates one buffer of the stream source: unqueue -> update -> queue
+// NOTE 2: To unqueue a buffer it needs to be processed: IsAudioBufferProcessed()
void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
{
ALuint buffer = 0;
@@ -1019,9 +1082,10 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
// Check if any buffer was available for unqueue
if (alGetError() != AL_INVALID_VALUE)
{
- alBufferData(buffer, stream.format, data, samplesCount*stream.channels*stream.sampleSize/8, stream.sampleRate);
+ alBufferData(buffer, stream.format, data, samplesCount*stream.sampleSize/8*stream.channels, stream.sampleRate);
alSourceQueueBuffers(stream.source, 1, &buffer);
}
+ else TraceLog(WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source);
}
// Check if any audio stream buffers requires refill
@@ -1066,6 +1130,7 @@ void StopAudioStream(AudioStream stream)
// Module specific Functions Definition
//----------------------------------------------------------------------------------
+#if defined(SUPPORT_FILEFORMAT_WAV)
// Load WAV file into Wave structure
static Wave LoadWAV(const char *fileName)
{
@@ -1182,31 +1247,29 @@ static Wave LoadWAV(const char *fileName)
return wave;
}
+#endif
+#if defined(SUPPORT_FILEFORMAT_OGG)
// Load OGG file into Wave structure
// NOTE: Using stb_vorbis library
static Wave LoadOGG(const char *fileName)
{
- Wave wave;
+ Wave wave = { 0 };
stb_vorbis *oggFile = stb_vorbis_open_filename(fileName, NULL, NULL);
- if (oggFile == NULL)
- {
- TraceLog(WARNING, "[%s] OGG file could not be opened", fileName);
- wave.data = NULL;
- }
+ if (oggFile == NULL) TraceLog(WARNING, "[%s] OGG file could not be opened", fileName);
else
{
stb_vorbis_info info = stb_vorbis_get_info(oggFile);
-
+
wave.sampleRate = info.sample_rate;
wave.sampleSize = 16; // 16 bit per sample (short)
wave.channels = info.channels;
- wave.sampleCount = (int)stb_vorbis_stream_length_in_samples(oggFile);
+ wave.sampleCount = (int)stb_vorbis_stream_length_in_samples(oggFile); // Independent by channel
float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile);
- if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio lenght is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds);
+ if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio length is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds);
wave.data = (short *)malloc(wave.sampleCount*wave.channels*sizeof(short));
@@ -1222,7 +1285,9 @@ static Wave LoadOGG(const char *fileName)
return wave;
}
+#endif
+#if defined(SUPPORT_FILEFORMAT_FLAC)
// Load FLAC file into Wave structure
// NOTE: Using dr_flac library
static Wave LoadFLAC(const char *fileName)
@@ -1244,15 +1309,22 @@ static Wave LoadFLAC(const char *fileName)
return wave;
}
+#endif
// Some required functions for audio standalone module version
#if defined(AUDIO_STANDALONE)
-// Get the extension for a filename
-const char *GetExtension(const char *fileName)
+// Check file extension
+bool IsFileExtension(const char *fileName, const char *ext)
{
- const char *dot = strrchr(fileName, '.');
- if (!dot || dot == fileName) return "";
- return (dot + 1);
+ bool result = false;
+ const char *fileExt;
+
+ if ((fileExt = strrchr(fileName, '.')) != NULL)
+ {
+ if (strcmp(fileExt, ext) == 0) result = true;
+ }
+
+ return result;
}
// Outputs a trace log message (INFO, ERROR, WARNING)
@@ -1262,7 +1334,7 @@ void TraceLog(int msgType, const char *text, ...)
va_list args;
int traceDebugMsgs = 0;
-#ifdef DO_NOT_TRACE_DEBUG_MSGS
+#if defined(DO_NOT_TRACE_DEBUG_MSGS)
traceDebugMsgs = 0;
#endif