2014-09-17 00:51:31 +04:00
/**********************************************************************************************
2013-11-19 02:38:44 +04:00
*
2013-11-23 16:30:54 +04:00
* raylib . audio
2013-11-19 02:38:44 +04:00
*
2016-07-29 22:35:57 +03:00
* Basic functions to manage Audio :
2016-07-15 19:16:34 +03:00
* Manage audio device ( init / close )
* Load and Unload audio files
* Play / Stop / Pause / Resume loaded audio
* Manage mixing channels
* Manage raw audio context
2014-09-03 18:51:28 +04:00
*
* Uses external lib :
2014-09-17 00:51:31 +04:00
* OpenAL Soft - Audio device management lib ( http : //kcat.strangesoft.net/openal.html)
* stb_vorbis - Ogg audio files loading ( http : //www.nothings.org/stb_vorbis/)
2016-07-15 19:16:34 +03:00
* jar_xm - XM module file loading
* jar_mod - MOD audio file loading
2014-09-03 18:51:28 +04:00
*
2016-07-15 19:16:34 +03:00
* 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
*
* Copyright ( c ) 2014 - 2016 Ramon Santamaria ( @ raysan5 )
2014-09-03 18:51:28 +04:00
*
* This software is provided " as-is " , without any express or implied warranty . In no event
2013-11-23 16:30:54 +04:00
* will the authors be held liable for any damages arising from the use of this software .
2013-11-19 02:38:44 +04:00
*
2014-09-03 18:51:28 +04:00
* Permission is granted to anyone to use this software for any purpose , including commercial
2013-11-23 16:30:54 +04:00
* applications , and to alter it and redistribute it freely , subject to the following restrictions :
2013-11-19 02:38:44 +04:00
*
2014-09-03 18:51:28 +04:00
* 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
2013-11-23 16:30:54 +04:00
* in the product documentation would be appreciated but is not required .
2013-11-19 02:38:44 +04:00
*
2013-11-23 16:30:54 +04:00
* 2. Altered source versions must be plainly marked as such , and must not be misrepresented
* as being the original software .
2013-11-19 02:38:44 +04:00
*
2013-11-23 16:30:54 +04:00
* 3. This notice may not be removed or altered from any source distribution .
2013-11-19 02:38:44 +04:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-07-29 22:41:19 +03:00
//#define AUDIO_STANDALONE // NOTE: To use the audio module as standalone lib, just uncomment this line
# if defined(AUDIO_STANDALONE)
# include "audio.h"
# else
# include "raylib.h"
# endif
2013-11-19 02:38:44 +04:00
2016-06-02 18:12:31 +03:00
# include "AL/al.h" // OpenAL basic header
# include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work)
2013-11-19 02:38:44 +04:00
2016-06-02 18:12:31 +03:00
# include <stdlib.h> // Required for: malloc(), free()
# include <string.h> // Required for: strcmp(), strncmp()
# include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
2013-11-19 02:38:44 +04:00
2016-08-01 13:49:17 +03:00
// Tokens defined by OpenAL extension: AL_EXT_float32
2016-06-11 12:21:24 +03:00
# ifndef AL_FORMAT_MONO_FLOAT32
# define AL_FORMAT_MONO_FLOAT32 0x10010
# endif
# ifndef AL_FORMAT_STEREO_FLOAT32
# define AL_FORMAT_STEREO_FLOAT32 0x10011
# endif
2015-07-31 13:31:39 +03:00
# if defined(AUDIO_STANDALONE)
2016-06-02 18:12:31 +03:00
# include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
2015-07-31 13:31:39 +03:00
# else
2016-06-02 18:12:31 +03:00
# include "utils.h" // Required for: DecompressData()
// NOTE: Includes Android fopen() function map
2015-07-31 13:31:39 +03:00
# endif
2014-01-23 15:36:18 +04:00
2015-05-21 15:13:51 +03:00
//#define STB_VORBIS_HEADER_ONLY
2016-06-06 15:34:43 +03:00
# include "external/stb_vorbis.h" // OGG loading functions
2016-04-26 04:40:19 +03:00
# define JAR_XM_IMPLEMENTATION
2016-06-06 15:34:43 +03:00
# include "external/jar_xm.h" // XM loading functions
2013-11-19 02:38:44 +04:00
2016-06-02 06:09:00 +03:00
# define JAR_MOD_IMPLEMENTATION
2016-06-06 15:34:43 +03:00
# include "external/jar_mod.h" // MOD loading functions
2016-06-02 06:09:00 +03:00
2016-10-10 19:22:55 +03:00
# define DR_FLAC_IMPLEMENTATION
# define DR_FLAC_NO_WIN32_IO
# include "external/dr_flac.h" // FLAC loading functions
2016-07-29 14:17:50 +03:00
# ifdef _MSC_VER
# undef bool
# endif
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
2016-08-01 22:37:45 +03:00
# define MAX_STREAM_BUFFERS 2 // Number of buffers for each audio stream
2016-08-01 13:49:17 +03:00
// NOTE: Music buffer size is defined by number of samples, independent of sample size
// After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds
// and double-buffering system, I concluded that a 4096 samples buffer should be enough
2016-08-01 22:37:45 +03:00
// In case of music-stalls, just increase this number
# define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. short: 32Kb)
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
2014-04-19 18:36:49 +04:00
2016-10-10 19:22:55 +03:00
typedef enum { MUSIC_AUDIO_OGG = 0 , MUSIC_AUDIO_FLAC , MUSIC_MODULE_XM , MUSIC_MODULE_MOD } MusicContextType ;
2016-08-01 13:49:17 +03:00
2016-05-13 07:14:02 +03:00
// Music type (file streaming from memory)
2016-08-02 20:09:07 +03:00
typedef struct MusicData {
2016-08-01 13:49:17 +03:00
MusicContextType ctxType ; // Type of music context (OGG, XM, MOD)
stb_vorbis * ctxOgg ; // OGG audio context
2016-10-10 19:22:55 +03:00
drflac * ctxFlac ; // FLAC audio context
2016-08-01 13:49:17 +03:00
jar_xm_context_t * ctxXm ; // XM chiptune context
jar_mod_context_t ctxMod ; // MOD chiptune context
2016-08-02 18:32:24 +03:00
AudioStream stream ; // Audio stream (double buffering)
2016-08-01 13:49:17 +03:00
bool loop ; // Repeat music after finish (loop)
unsigned int totalSamples ; // Total number of samples
unsigned int samplesLeft ; // Number of samples left to end
} MusicData , * Music ;
2015-07-31 13:31:39 +03:00
# if defined(AUDIO_STANDALONE)
typedef enum { INFO = 0 , ERROR , WARNING , DEBUG , OTHER } TraceLogType ;
# endif
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
2016-08-01 13:58:30 +03:00
// ...
2016-06-02 18:12:31 +03:00
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
2016-09-08 01:20:06 +03:00
static Wave LoadWAV ( const char * fileName ) ; // Load WAV file
static Wave LoadOGG ( const char * fileName ) ; // Load OGG file
2016-10-10 19:22:55 +03:00
static Wave LoadFLAC ( const char * fileName ) ; // Load FLAC file
2014-04-09 22:25:26 +04:00
2015-07-31 13:31:39 +03:00
# 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)
# endif
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
2014-04-19 18:36:49 +04:00
// Module Functions Definition - Audio Device initialization and Closing
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
2016-08-01 13:49:17 +03:00
// Initialize audio device
2014-09-03 19:06:10 +04:00
void InitAudioDevice ( void )
2013-11-19 02:38:44 +04:00
{
2013-11-23 16:30:54 +04:00
// Open and initialize a device with default settings
ALCdevice * device = alcOpenDevice ( NULL ) ;
2014-09-03 18:51:28 +04:00
2016-06-02 18:12:31 +03:00
if ( ! device ) TraceLog ( ERROR , " Audio device could not be opened " ) ;
2016-08-01 13:49:17 +03:00
else
2013-11-23 16:30:54 +04:00
{
2016-08-01 13:49:17 +03:00
ALCcontext * context = alcCreateContext ( device , NULL ) ;
2014-09-03 18:51:28 +04:00
2016-08-01 13:49:17 +03:00
if ( ( context = = NULL ) | | ( alcMakeContextCurrent ( context ) = = ALC_FALSE ) )
{
if ( context ! = NULL ) alcDestroyContext ( context ) ;
2014-09-03 18:51:28 +04:00
2016-08-01 13:49:17 +03:00
alcCloseDevice ( device ) ;
2013-11-23 16:30:54 +04:00
2016-08-01 13:49:17 +03:00
TraceLog ( ERROR , " Could not initialize audio context " ) ;
}
else
{
TraceLog ( INFO , " Audio device and context initialized successfully: %s " , alcGetString ( device , ALC_DEVICE_SPECIFIER ) ) ;
2014-09-03 18:51:28 +04:00
2016-08-01 13:49:17 +03:00
// Listener definition (just for 2D)
alListener3f ( AL_POSITION , 0 , 0 , 0 ) ;
alListener3f ( AL_VELOCITY , 0 , 0 , 0 ) ;
alListener3f ( AL_ORIENTATION , 0 , 0 , - 1 ) ;
}
}
2013-11-19 02:38:44 +04:00
}
2016-05-14 10:25:40 +03:00
// Close the audio device for all contexts
2014-09-03 19:06:10 +04:00
void CloseAudioDevice ( void )
2013-11-19 02:38:44 +04:00
{
2013-11-23 16:30:54 +04:00
ALCdevice * device ;
ALCcontext * context = alcGetCurrentContext ( ) ;
2014-09-03 18:51:28 +04:00
2016-08-01 13:49:17 +03:00
if ( context = = NULL ) TraceLog ( WARNING , " Could not get current audio context for closing " ) ;
2013-11-19 02:38:44 +04:00
2013-11-23 16:30:54 +04:00
device = alcGetContextsDevice ( context ) ;
2013-11-19 02:38:44 +04:00
2013-11-23 16:30:54 +04:00
alcMakeContextCurrent ( NULL ) ;
alcDestroyContext ( context ) ;
alcCloseDevice ( device ) ;
2016-08-16 12:09:55 +03:00
2016-08-06 20:30:56 +03:00
TraceLog ( INFO , " Audio device closed successfully " ) ;
2013-11-19 02:38:44 +04:00
}
2016-07-15 19:16:34 +03:00
// Check if device has been initialized successfully
2016-05-01 02:05:43 +03:00
bool IsAudioDeviceReady ( void )
2016-04-30 09:00:12 +03:00
{
ALCcontext * context = alcGetCurrentContext ( ) ;
2016-07-29 22:35:57 +03:00
2016-04-30 09:00:12 +03:00
if ( context = = NULL ) return false ;
2016-06-02 18:12:31 +03:00
else
{
2016-04-30 09:00:12 +03:00
ALCdevice * device = alcGetContextsDevice ( context ) ;
2016-07-29 22:35:57 +03:00
2016-04-30 09:00:12 +03:00
if ( device = = NULL ) return false ;
else return true ;
}
}
2014-04-19 18:36:49 +04:00
//----------------------------------------------------------------------------------
// Module Functions Definition - Sounds loading and playing (.WAV)
//----------------------------------------------------------------------------------
2016-09-08 01:20:06 +03:00
// Load wave data from file into RAM
Wave LoadWave ( const char * fileName )
2013-11-19 02:38:44 +04:00
{
2016-01-23 15:22:13 +03:00
Wave wave = { 0 } ;
2014-09-17 00:51:31 +04:00
2016-08-01 13:49:17 +03:00
if ( strcmp ( GetExtension ( fileName ) , " wav " ) = = 0 ) wave = LoadWAV ( fileName ) ;
else if ( strcmp ( GetExtension ( fileName ) , " ogg " ) = = 0 ) wave = LoadOGG ( fileName ) ;
2016-10-10 19:22:55 +03:00
else if ( strcmp ( GetExtension ( fileName ) , " flac " ) = = 0 ) wave = LoadFLAC ( fileName ) ;
2016-09-08 01:20:06 +03:00
else TraceLog ( WARNING , " [%s] File extension not recognized, it can't be loaded " , fileName ) ;
2014-04-19 18:36:49 +04:00
2016-09-08 01:20:06 +03:00
return wave ;
}
2016-08-16 12:09:55 +03:00
2016-09-08 01:20:06 +03:00
// Load wave data from float array data (32bit)
2016-09-08 02:03:05 +03:00
Wave LoadWaveEx ( float * data , int sampleCount , int sampleRate , int sampleSize , int channels )
2016-09-08 01:20:06 +03:00
{
Wave wave ;
wave . data = data ;
2016-09-08 02:03:05 +03:00
wave . sampleCount = sampleCount ;
wave . sampleRate = sampleRate ;
2016-09-15 12:53:16 +03:00
wave . sampleSize = 32 ;
2016-09-08 02:03:05 +03:00
wave . channels = channels ;
2016-09-08 01:20:06 +03:00
2016-09-15 12:53:16 +03:00
// NOTE: Copy wave data to work with,
// user is responsible of input data to free
Wave cwave = WaveCopy ( wave ) ;
2016-09-08 01:20:06 +03:00
2016-09-15 12:53:16 +03:00
WaveFormat ( & cwave , sampleRate , sampleSize , channels ) ;
return cwave ;
2016-09-08 01:20:06 +03:00
}
// Load sound to memory
// NOTE: The entire file is loaded to memory to be played (no-streaming)
Sound LoadSound ( const char * fileName )
{
Wave wave = LoadWave ( fileName ) ;
Sound sound = LoadSoundFromWave ( wave ) ;
UnloadWave ( wave ) ; // Sound is loaded, we can unload wave
2014-09-03 18:51:28 +04:00
2013-11-23 16:30:54 +04:00
return sound ;
2013-11-19 02:38:44 +04:00
}
2014-12-15 03:08:30 +03:00
// Load sound from wave data
2016-08-01 13:49:17 +03:00
// NOTE: Wave data must be unallocated manually
2014-12-15 03:08:30 +03:00
Sound LoadSoundFromWave ( Wave wave )
{
2016-01-23 15:22:13 +03:00
Sound sound = { 0 } ;
2014-12-15 03:08:30 +03:00
if ( wave . data ! = NULL )
{
ALenum format = 0 ;
2016-08-16 12:09:55 +03:00
2016-08-15 17:35:11 +03:00
// The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample)
2014-12-15 03:08:30 +03:00
if ( wave . channels = = 1 )
{
2016-08-15 17:35:11 +03:00
switch ( wave . sampleSize )
{
case 8 : format = AL_FORMAT_MONO8 ; break ;
case 16 : format = AL_FORMAT_MONO16 ; break ;
case 32 : format = AL_FORMAT_MONO_FLOAT32 ; break ;
default : TraceLog ( WARNING , " Wave sample size not supported: %i " , wave . sampleSize ) ; break ;
}
2014-12-15 03:08:30 +03:00
}
else if ( wave . channels = = 2 )
{
2016-08-15 17:35:11 +03:00
switch ( wave . sampleSize )
{
case 8 : format = AL_FORMAT_STEREO8 ; break ;
case 16 : format = AL_FORMAT_STEREO16 ; break ;
case 32 : format = AL_FORMAT_STEREO_FLOAT32 ; break ;
default : TraceLog ( WARNING , " Wave sample size not supported: %i " , wave . sampleSize ) ; break ;
}
2014-12-15 03:08:30 +03:00
}
2016-08-15 17:35:11 +03:00
else TraceLog ( WARNING , " Wave number of channels not supported: %i " , wave . channels ) ;
2016-08-16 12:09:55 +03:00
2014-12-15 03:08:30 +03:00
// Create an audio source
ALuint source ;
alGenSources ( 1 , & source ) ; // Generate pointer to audio source
alSourcef ( source , AL_PITCH , 1 ) ;
alSourcef ( source , AL_GAIN , 1 ) ;
alSource3f ( source , AL_POSITION , 0 , 0 , 0 ) ;
alSource3f ( source , AL_VELOCITY , 0 , 0 , 0 ) ;
alSourcei ( source , AL_LOOPING , AL_FALSE ) ;
// Convert loaded data to OpenAL buffer
//----------------------------------------
ALuint buffer ;
alGenBuffers ( 1 , & buffer ) ; // Generate pointer to buffer
2016-08-16 12:09:55 +03:00
2016-08-15 17:35:11 +03:00
unsigned int dataSize = wave . sampleCount * wave . sampleSize / 8 ; // Size in bytes
2014-12-15 03:08:30 +03:00
// Upload sound data to buffer
2016-08-15 17:35:11 +03:00
alBufferData ( buffer , format , wave . data , dataSize , wave . sampleRate ) ;
2014-12-15 03:08:30 +03:00
// Attach sound buffer to source
alSourcei ( source , AL_BUFFER , buffer ) ;
2016-08-15 17:35:11 +03:00
TraceLog ( INFO , " [SND ID %i][BUFR ID %i] Sound data loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i) " , source , buffer , wave . sampleRate , wave . sampleSize , wave . channels ) ;
2014-12-15 03:08:30 +03:00
sound . source = source ;
sound . buffer = buffer ;
2016-08-29 12:17:58 +03:00
sound . format = format ;
2014-12-15 03:08:30 +03:00
}
return sound ;
}
2014-01-23 15:36:18 +04:00
// Load sound to memory from rRES file (raylib Resource)
2016-02-12 14:22:56 +03:00
// TODO: Maybe rresName could be directly a char array with all the data?
2014-01-23 15:36:18 +04:00
Sound LoadSoundFromRES ( const char * rresName , int resId )
{
2016-01-23 15:22:13 +03:00
Sound sound = { 0 } ;
2014-01-23 15:36:18 +04:00
2015-07-31 13:31:39 +03:00
# if defined(AUDIO_STANDALONE)
TraceLog ( WARNING , " Sound loading from rRES resource file not supported on standalone mode " ) ;
# else
2016-07-29 22:35:57 +03:00
2015-07-31 13:31:39 +03:00
bool found = false ;
2016-07-29 22:35:57 +03:00
2014-01-23 15:36:18 +04:00
char id [ 4 ] ; // rRES file identifier
unsigned char version ; // rRES file version and subversion
char useless ; // rRES header reserved data
short numRes ;
2014-09-03 18:51:28 +04:00
2014-01-23 15:36:18 +04:00
ResInfoHeader infoHeader ;
2014-09-03 18:51:28 +04:00
2014-01-23 15:36:18 +04:00
FILE * rresFile = fopen ( rresName , " rb " ) ;
2016-08-01 13:49:17 +03:00
if ( rresFile = = NULL ) TraceLog ( WARNING , " [%s] rRES raylib resource file could not be opened " , rresName ) ;
2014-04-09 22:25:26 +04:00
else
2014-01-23 15:36:18 +04:00
{
2014-04-09 22:25:26 +04:00
// Read rres file (basic file check - id)
fread ( & id [ 0 ] , sizeof ( char ) , 1 , rresFile ) ;
fread ( & id [ 1 ] , sizeof ( char ) , 1 , rresFile ) ;
fread ( & id [ 2 ] , sizeof ( char ) , 1 , rresFile ) ;
fread ( & id [ 3 ] , sizeof ( char ) , 1 , rresFile ) ;
fread ( & version , sizeof ( char ) , 1 , rresFile ) ;
fread ( & useless , sizeof ( char ) , 1 , rresFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
if ( ( id [ 0 ] ! = ' r ' ) & & ( id [ 1 ] ! = ' R ' ) & & ( id [ 2 ] ! = ' E ' ) & & ( id [ 3 ] ! = ' S ' ) )
2014-01-23 15:36:18 +04:00
{
2014-04-09 22:25:26 +04:00
TraceLog ( WARNING , " [%s] This is not a valid raylib resource file " , rresName ) ;
}
else
{
// Read number of resources embedded
fread ( & numRes , sizeof ( short ) , 1 , rresFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
for ( int i = 0 ; i < numRes ; i + + )
{
fread ( & infoHeader , sizeof ( ResInfoHeader ) , 1 , rresFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
if ( infoHeader . id = = resId )
2014-01-23 15:36:18 +04:00
{
2014-04-09 22:25:26 +04:00
found = true ;
// Check data is of valid SOUND type
if ( infoHeader . type = = 1 ) // SOUND data type
{
// TODO: Check data compression type
// NOTE: We suppose compression type 2 (DEFLATE - default)
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Reading SOUND parameters
Wave wave ;
short sampleRate , bps ;
char channels , reserved ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
fread ( & sampleRate , sizeof ( short ) , 1 , rresFile ) ; // Sample rate (frequency)
fread ( & bps , sizeof ( short ) , 1 , rresFile ) ; // Bits per sample
fread ( & channels , 1 , 1 , rresFile ) ; // Channels (1 - mono, 2 - stereo)
fread ( & reserved , 1 , 1 , rresFile ) ; // <reserved>
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
wave . sampleRate = sampleRate ;
2016-08-15 17:35:11 +03:00
wave . sampleSize = bps ;
2014-04-09 22:25:26 +04:00
wave . channels = ( short ) channels ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
unsigned char * data = malloc ( infoHeader . size ) ;
fread ( data , infoHeader . size , 1 , rresFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
wave . data = DecompressData ( data , infoHeader . size , infoHeader . srcSize ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
free ( data ) ;
2014-09-03 18:51:28 +04:00
2016-08-01 13:49:17 +03:00
sound = LoadSoundFromWave ( wave ) ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
// Sound is loaded, we can unload wave data
2014-04-19 18:36:49 +04:00
UnloadWave ( wave ) ;
2014-04-09 22:25:26 +04:00
}
2016-08-01 13:49:17 +03:00
else TraceLog ( WARNING , " [%s] Required resource do not seem to be a valid SOUND resource " , rresName ) ;
2014-01-23 15:36:18 +04:00
}
2014-04-09 22:25:26 +04:00
else
{
// Depending on type, skip the right amount of parameters
switch ( infoHeader . type )
{
case 0 : fseek ( rresFile , 6 , SEEK_CUR ) ; break ; // IMAGE: Jump 6 bytes of parameters
case 1 : fseek ( rresFile , 6 , SEEK_CUR ) ; break ; // SOUND: Jump 6 bytes of parameters
case 2 : fseek ( rresFile , 5 , SEEK_CUR ) ; break ; // MODEL: Jump 5 bytes of parameters (TODO: Review)
case 3 : break ; // TEXT: No parameters
case 4 : break ; // RAW: No parameters
default : break ;
}
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Jump DATA to read next infoHeader
fseek ( rresFile , infoHeader . size , SEEK_CUR ) ;
2014-09-03 18:51:28 +04:00
}
2014-01-23 15:36:18 +04:00
}
}
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
fclose ( rresFile ) ;
2014-01-23 15:36:18 +04:00
}
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
if ( ! found ) TraceLog ( WARNING , " [%s] Required resource id [%i] could not be found in the raylib resource file " , rresName , resId ) ;
2015-07-31 13:31:39 +03:00
# endif
2014-01-23 15:36:18 +04:00
return sound ;
}
2016-09-08 01:20:06 +03:00
// Unload Wave data
void UnloadWave ( Wave wave )
{
free ( wave . data ) ;
TraceLog ( INFO , " Unloaded wave data from RAM " ) ;
}
2013-11-19 02:38:44 +04:00
// Unload sound
void UnloadSound ( Sound sound )
{
2013-11-23 16:30:54 +04:00
alDeleteSources ( 1 , & sound . source ) ;
alDeleteBuffers ( 1 , & sound . buffer ) ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
TraceLog ( INFO , " [SND ID %i][BUFR ID %i] Unloaded sound data from RAM " , sound . source , sound . buffer ) ;
2013-11-19 02:38:44 +04:00
}
2016-08-29 12:17:58 +03:00
// Update sound buffer with new data
// NOTE: data must match sound.format
void UpdateSound ( Sound sound , void * data , int numSamples )
{
ALint sampleRate , sampleSize , channels ;
alGetBufferi ( sound . buffer , AL_FREQUENCY , & sampleRate ) ;
alGetBufferi ( sound . buffer , AL_BITS , & sampleSize ) ; // It could also be retrieved from sound.format
alGetBufferi ( sound . buffer , AL_CHANNELS , & channels ) ; // It could also be retrieved from sound.format
TraceLog ( DEBUG , " UpdateSound() : AL_FREQUENCY: %i " , sampleRate ) ;
TraceLog ( DEBUG , " UpdateSound() : AL_BITS: %i " , sampleSize ) ;
TraceLog ( DEBUG , " UpdateSound() : AL_CHANNELS: %i " , channels ) ;
unsigned int dataSize = numSamples * sampleSize / 8 ; // Size of data in bytes
alSourceStop ( sound . source ) ; // Stop sound
alSourcei ( sound . source , AL_BUFFER , 0 ) ; // Unbind buffer from sound to update
//alDeleteBuffers(1, &sound.buffer); // Delete current buffer data
//alGenBuffers(1, &sound.buffer); // Generate new buffer
// Upload new data to sound buffer
alBufferData ( sound . buffer , sound . format , data , dataSize , sampleRate ) ;
// Attach sound buffer to source again
alSourcei ( sound . source , AL_BUFFER , sound . buffer ) ;
}
2013-11-19 02:38:44 +04:00
// Play a sound
void PlaySound ( Sound sound )
{
2013-11-23 16:30:54 +04:00
alSourcePlay ( sound . source ) ; // Play the sound
2014-09-03 18:51:28 +04:00
2014-07-23 21:50:06 +04:00
//TraceLog(INFO, "Playing sound");
2013-11-23 16:30:54 +04:00
// Find the current position of the sound being played
// NOTE: Only work when the entire file is in a single buffer
//int byteOffset;
//alGetSourcei(sound.source, AL_BYTE_OFFSET, &byteOffset);
2014-01-23 15:36:18 +04:00
//
//int sampleRate;
//alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); // AL_CHANNELS, AL_BITS (bps)
2014-09-03 18:51:28 +04:00
2016-09-12 20:36:41 +03:00
//float seconds = (float)byteOffset/sampleRate; // Number of seconds since the beginning of the sound
2014-01-23 15:36:18 +04:00
//or
//float result;
//alGetSourcef(sound.source, AL_SEC_OFFSET, &result); // AL_SAMPLE_OFFSET
2013-11-19 02:38:44 +04:00
}
// Pause a sound
void PauseSound ( Sound sound )
{
2013-11-23 16:30:54 +04:00
alSourcePause ( sound . source ) ;
2013-11-19 02:38:44 +04:00
}
2016-08-01 13:49:17 +03:00
// Resume a paused sound
void ResumeSound ( Sound sound )
{
ALenum state ;
alGetSourcei ( sound . source , AL_SOURCE_STATE , & state ) ;
if ( state = = AL_PAUSED ) alSourcePlay ( sound . source ) ;
}
2013-11-19 02:38:44 +04:00
// Stop reproducing a sound
void StopSound ( Sound sound )
{
2013-11-23 16:30:54 +04:00
alSourceStop ( sound . source ) ;
2013-11-19 02:38:44 +04:00
}
2014-01-23 15:36:18 +04:00
// Check if a sound is playing
2016-05-03 19:04:21 +03:00
bool IsSoundPlaying ( Sound sound )
2014-01-23 15:36:18 +04:00
{
bool playing = false ;
ALint state ;
2014-09-03 18:51:28 +04:00
2014-01-23 15:36:18 +04:00
alGetSourcei ( sound . source , AL_SOURCE_STATE , & state ) ;
if ( state = = AL_PLAYING ) playing = true ;
2014-09-03 18:51:28 +04:00
2014-01-23 15:36:18 +04:00
return playing ;
}
2014-04-19 18:36:49 +04:00
// Set volume for a sound
void SetSoundVolume ( Sound sound , float volume )
{
alSourcef ( sound . source , AL_GAIN , volume ) ;
}
// Set pitch for a sound
void SetSoundPitch ( Sound sound , float pitch )
{
alSourcef ( sound . source , AL_PITCH , pitch ) ;
}
2016-09-08 01:20:06 +03:00
// Convert wave data to desired format
2016-09-09 02:34:30 +03:00
// TODO: Consider channels (mono - stereo)
2016-09-08 01:20:06 +03:00
void WaveFormat ( Wave * wave , int sampleRate , int sampleSize , int channels )
{
if ( wave - > sampleSize ! = sampleSize )
{
float * samples = GetWaveData ( * wave ) ; //Color *pixels = GetImageData(*image);
free ( wave - > data ) ;
2016-09-09 02:34:30 +03:00
wave - > sampleSize = sampleSize ;
2016-09-08 01:20:06 +03:00
2016-09-09 02:34:30 +03:00
//sample *= 4.0f; // Arbitrary gain to get reasonable output volume...
//if (sample > 1.0f) sample = 1.0f;
//if (sample < -1.0f) sample = -1.0f;
2016-09-08 01:20:06 +03:00
if ( sampleSize = = 8 )
{
wave - > data = ( unsigned char * ) malloc ( wave - > sampleCount * sizeof ( unsigned char ) ) ;
for ( int i = 0 ; i < wave - > sampleCount ; i + + )
{
2016-09-09 02:34:30 +03:00
( ( unsigned char * ) wave - > data ) [ i ] = ( unsigned char ) ( ( float ) samples [ i ] * 127 + 128 ) ;
2016-09-08 01:20:06 +03:00
}
}
else if ( sampleSize = = 16 )
{
wave - > data = ( short * ) malloc ( wave - > sampleCount * sizeof ( short ) ) ;
for ( int i = 0 ; i < wave - > sampleCount ; i + + )
{
2016-09-09 02:34:30 +03:00
( ( short * ) wave - > data ) [ i ] = ( short ) ( ( float ) samples [ i ] * 32000 ) ; // SHRT_MAX = 32767
2016-09-08 01:20:06 +03:00
}
}
else if ( sampleSize = = 32 )
{
wave - > data = ( float * ) malloc ( wave - > sampleCount * sizeof ( float ) ) ;
for ( int i = 0 ; i < wave - > sampleCount ; i + + )
{
2016-09-09 02:34:30 +03:00
( ( float * ) wave - > data ) [ i ] = ( float ) samples [ i ] ;
2016-09-08 01:20:06 +03:00
}
}
else TraceLog ( WARNING , " Wave formatting: Sample size not supported " ) ;
2016-09-15 12:53:16 +03:00
free ( samples ) ;
2016-09-08 01:20:06 +03:00
}
2016-09-09 02:34:30 +03:00
// NOTE: Only supported 1 or 2 channels (mono or stereo)
if ( ( channels > 0 ) & & ( channels < 3 ) & & ( wave - > channels ! = channels ) )
{
// TODO: Add/remove channels interlaced data if required...
}
2016-09-08 01:20:06 +03:00
}
// Copy a wave to a new wave
Wave WaveCopy ( Wave wave )
{
Wave newWave ;
2016-09-08 02:03:05 +03:00
if ( wave . sampleSize = = 8 ) newWave . data = ( unsigned char * ) malloc ( wave . sampleCount * wave . channels * sizeof ( unsigned char ) ) ;
else if ( wave . sampleSize = = 16 ) newWave . data = ( short * ) malloc ( wave . sampleCount * wave . channels * sizeof ( short ) ) ;
else if ( wave . sampleSize = = 32 ) newWave . data = ( float * ) malloc ( wave . sampleCount * wave . channels * sizeof ( float ) ) ;
2016-09-08 01:20:06 +03:00
else TraceLog ( WARNING , " Wave sample size not supported for copy " ) ;
if ( newWave . data ! = NULL )
{
// NOTE: Size must be provided in bytes
2016-09-08 02:03:05 +03:00
memcpy ( newWave . data , wave . data , wave . sampleCount * wave . channels * wave . sampleSize / 8 ) ;
2016-09-08 01:20:06 +03:00
newWave . sampleCount = wave . sampleCount ;
newWave . sampleRate = wave . sampleRate ;
newWave . sampleSize = wave . sampleSize ;
newWave . channels = wave . channels ;
}
return newWave ;
}
// Crop a wave to defined samples range
// NOTE: Security check in case of out-of-range
void WaveCrop ( Wave * wave , int initSample , int finalSample )
{
2016-09-15 12:53:16 +03:00
if ( ( initSample > = 0 ) & & ( initSample < finalSample ) & &
( finalSample > 0 ) & & ( finalSample < wave - > sampleCount ) )
2016-09-08 02:03:05 +03:00
{
// TODO: Review cropping (it could be simplified...)
float * samples = GetWaveData ( * wave ) ;
float * cropSamples = ( float * ) malloc ( ( finalSample - initSample ) * sizeof ( float ) ) ;
for ( int i = initSample ; i < finalSample ; i + + ) cropSamples [ i ] = samples [ i ] ;
free ( wave - > data ) ;
wave - > data = cropSamples ;
int sampleSize = wave - > sampleSize ;
wave - > sampleSize = 32 ;
WaveFormat ( wave , wave - > sampleRate , sampleSize , wave - > channels ) ;
}
else TraceLog ( WARNING , " Wave crop range out of bounds " ) ;
2016-09-08 01:20:06 +03:00
}
// Get samples data from wave as a floats array
2016-09-09 02:34:30 +03:00
// NOTE: Returned sample values are normalized to range [-1..1]
2016-09-15 12:53:16 +03:00
// TODO: Consider multiple channels (mono - stereo)
2016-09-08 01:20:06 +03:00
float * GetWaveData ( Wave wave )
{
float * samples = ( float * ) malloc ( wave . sampleCount * sizeof ( float ) ) ;
for ( int i = 0 ; i < wave . sampleCount ; i + + )
{
2016-09-09 02:34:30 +03:00
if ( wave . sampleSize = = 8 ) samples [ i ] = ( float ) ( ( ( unsigned char * ) wave . data ) [ i ] - 127 ) / 256.0f ;
else if ( wave . sampleSize = = 16 ) samples [ i ] = ( float ) ( ( short * ) wave . data ) [ i ] / 32767.0f ;
else if ( wave . sampleSize = = 32 ) samples [ i ] = ( ( float * ) wave . data ) [ i ] ;
2016-09-08 01:20:06 +03:00
}
return samples ;
}
2014-04-19 18:36:49 +04:00
//----------------------------------------------------------------------------------
// Module Functions Definition - Music loading and stream playing (.OGG)
//----------------------------------------------------------------------------------
2016-08-01 13:49:17 +03:00
// Load music stream from file
2016-09-08 01:20:06 +03:00
Music LoadMusicStream ( const char * fileName )
2016-07-29 22:35:57 +03:00
{
2016-08-01 13:49:17 +03:00
Music music = ( MusicData * ) malloc ( sizeof ( MusicData ) ) ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
if ( strcmp ( GetExtension ( fileName ) , " ogg " ) = = 0 )
2016-07-29 22:35:57 +03:00
{
2016-08-01 13:49:17 +03:00
// Open ogg audio stream
music - > ctxOgg = stb_vorbis_open_filename ( fileName , NULL , NULL ) ;
2016-07-29 22:35:57 +03:00
2016-10-10 19:22:55 +03:00
if ( music - > ctxOgg = = NULL ) TraceLog ( WARNING , " [%s] OGG audio file could not be opened " , fileName ) ;
2014-04-19 18:36:49 +04:00
else
{
2016-08-01 13:49:17 +03:00
stb_vorbis_info info = stb_vorbis_get_info ( music - > ctxOgg ) ; // Get Ogg file info
2016-08-01 22:37:45 +03:00
//float totalLengthSeconds = stb_vorbis_stream_length_in_seconds(music->ctxOgg);
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
// TODO: Support 32-bit sampleSize OGGs
music - > stream = InitAudioStream ( info . sample_rate , 16 , info . channels ) ;
music - > totalSamples = ( unsigned int ) stb_vorbis_stream_length_in_samples ( music - > ctxOgg ) * info . channels ;
music - > samplesLeft = music - > totalSamples ;
music - > ctxType = MUSIC_AUDIO_OGG ;
music - > loop = true ; // We loop by default
2016-08-01 22:37:45 +03:00
TraceLog ( DEBUG , " [%s] OGG sample rate: %i " , fileName , info . sample_rate ) ;
TraceLog ( DEBUG , " [%s] OGG channels: %i " , fileName , info . channels ) ;
TraceLog ( DEBUG , " [%s] OGG memory required: %i " , fileName , info . temp_memory_required ) ;
2016-08-16 12:09:55 +03:00
2014-04-19 18:36:49 +04:00
}
}
2016-10-10 19:22:55 +03:00
else if ( strcmp ( GetExtension ( fileName ) , " flac " ) = = 0 )
{
music - > ctxFlac = drflac_open_file ( fileName ) ;
if ( music - > ctxFlac = = NULL ) TraceLog ( WARNING , " [%s] FLAC audio file could not be opened " , fileName ) ;
else
{
music - > stream = InitAudioStream ( music - > ctxFlac - > sampleRate , music - > ctxFlac - > bitsPerSample , music - > ctxFlac - > channels ) ;
2016-10-17 01:03:38 +03:00
music - > totalSamples = ( unsigned int ) music - > ctxFlac - > totalSampleCount ;
2016-10-10 19:22:55 +03:00
music - > samplesLeft = music - > totalSamples ;
music - > ctxType = MUSIC_AUDIO_FLAC ;
music - > loop = true ; // We loop by default
TraceLog ( DEBUG , " [%s] FLAC sample rate: %i " , fileName , music - > ctxFlac - > sampleRate ) ;
TraceLog ( DEBUG , " [%s] FLAC bits per sample: %i " , fileName , music - > ctxFlac - > bitsPerSample ) ;
TraceLog ( DEBUG , " [%s] FLAC channels: %i " , fileName , music - > ctxFlac - > channels ) ;
}
}
2016-08-01 13:49:17 +03:00
else if ( strcmp ( GetExtension ( fileName ) , " xm " ) = = 0 )
2016-04-25 04:18:18 +03:00
{
2016-08-01 13:49:17 +03:00
int result = jar_xm_create_context_from_file ( & music - > ctxXm , 48000 , fileName ) ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
if ( ! result ) // XM context created successfully
2016-04-25 04:18:18 +03:00
{
2016-08-01 13:49:17 +03:00
jar_xm_set_max_loop_count ( music - > ctxXm , 0 ) ; // Set infinite number of loops
// NOTE: Only stereo is supported for XM
music - > stream = InitAudioStream ( 48000 , 32 , 2 ) ;
2016-08-01 22:37:45 +03:00
music - > totalSamples = ( unsigned int ) jar_xm_get_remaining_samples ( music - > ctxXm ) ;
music - > samplesLeft = music - > totalSamples ;
2016-08-01 13:49:17 +03:00
music - > ctxType = MUSIC_MODULE_XM ;
music - > loop = true ;
2016-08-16 12:09:55 +03:00
2016-08-01 22:37:45 +03:00
TraceLog ( DEBUG , " [%s] XM number of samples: %i " , fileName , music - > totalSamples ) ;
TraceLog ( DEBUG , " [%s] XM track length: %11.6f sec " , fileName , ( float ) music - > totalSamples / 48000.0f ) ;
2016-05-12 06:15:37 +03:00
}
2016-08-01 13:49:17 +03:00
else TraceLog ( WARNING , " [%s] XM file could not be opened " , fileName ) ;
2016-05-12 06:15:37 +03:00
}
2016-08-01 13:49:17 +03:00
else if ( strcmp ( GetExtension ( fileName ) , " mod " ) = = 0 )
2016-06-02 06:09:00 +03:00
{
2016-08-01 13:49:17 +03:00
jar_mod_init ( & music - > ctxMod ) ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
if ( jar_mod_load_file ( & music - > ctxMod , fileName ) )
2016-06-02 06:09:00 +03:00
{
2016-08-01 22:37:45 +03:00
music - > stream = InitAudioStream ( 48000 , 16 , 2 ) ;
2016-08-01 13:49:17 +03:00
music - > totalSamples = ( unsigned int ) jar_mod_max_samples ( & music - > ctxMod ) ;
music - > samplesLeft = music - > totalSamples ;
2016-08-01 22:37:45 +03:00
music - > ctxType = MUSIC_MODULE_MOD ;
music - > loop = true ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
TraceLog ( INFO , " [%s] MOD number of samples: %i " , fileName , music - > samplesLeft ) ;
TraceLog ( INFO , " [%s] MOD track length: %11.6f sec " , fileName , ( float ) music - > totalSamples / 48000.0f ) ;
2016-06-02 06:09:00 +03:00
}
2016-08-01 13:49:17 +03:00
else TraceLog ( WARNING , " [%s] MOD file could not be opened " , fileName ) ;
2016-06-02 06:09:00 +03:00
}
2016-08-01 13:49:17 +03:00
else TraceLog ( WARNING , " [%s] Music extension not recognized, it can't be loaded " , fileName ) ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
return music ;
2014-04-19 18:36:49 +04:00
}
2016-08-01 13:49:17 +03:00
// Unload music stream
void UnloadMusicStream ( Music music )
2016-07-29 22:35:57 +03:00
{
2016-08-01 13:49:17 +03:00
CloseAudioStream ( music - > stream ) ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
if ( music - > ctxType = = MUSIC_AUDIO_OGG ) stb_vorbis_close ( music - > ctxOgg ) ;
2016-10-10 19:22:55 +03:00
else if ( music - > ctxType = = MUSIC_AUDIO_FLAC ) drflac_free ( music - > ctxFlac ) ;
2016-08-01 13:49:17 +03:00
else if ( music - > ctxType = = MUSIC_MODULE_XM ) jar_xm_free_context ( music - > ctxXm ) ;
else if ( music - > ctxType = = MUSIC_MODULE_MOD ) jar_mod_unload ( & music - > ctxMod ) ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
free ( music ) ;
}
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
// Start music playing (open stream)
void PlayMusicStream ( Music music )
{
alSourcePlay ( music - > stream . source ) ;
2016-07-29 22:35:57 +03:00
}
2016-08-01 13:49:17 +03:00
// Pause music playing
void PauseMusicStream ( Music music )
2014-04-19 18:36:49 +04:00
{
2016-08-01 13:49:17 +03:00
alSourcePause ( music - > stream . source ) ;
}
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
// Resume music playing
void ResumeMusicStream ( Music music )
{
ALenum state ;
alGetSourcei ( music - > stream . source , AL_SOURCE_STATE , & state ) ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
if ( state = = AL_PAUSED ) alSourcePlay ( music - > stream . source ) ;
}
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
// Stop music playing (close stream)
2016-09-15 12:53:16 +03:00
// TODO: Restart XM context
2016-08-01 13:49:17 +03:00
void StopMusicStream ( Music music )
{
alSourceStop ( music - > stream . source ) ;
2016-09-15 12:53:16 +03:00
switch ( music - > ctxType )
{
case MUSIC_AUDIO_OGG : stb_vorbis_seek_start ( music - > ctxOgg ) ; break ;
case MUSIC_MODULE_XM : break ;
case MUSIC_MODULE_MOD : jar_mod_seek_start ( & music - > ctxMod ) ; break ;
default : break ;
}
music - > samplesLeft = music - > totalSamples ;
2016-05-11 10:37:10 +03:00
}
2014-09-03 18:51:28 +04:00
2016-07-15 19:16:34 +03:00
// Update (re-fill) music buffers if data already processed
2016-08-01 13:49:17 +03:00
void UpdateMusicStream ( Music music )
2016-07-15 19:16:34 +03:00
{
2016-09-15 12:53:16 +03:00
ALenum state ;
2016-07-15 19:16:34 +03:00
ALint processed = 0 ;
2016-09-15 12:53:16 +03:00
alGetSourcei ( music - > stream . source , AL_SOURCE_STATE , & state ) ; // Get music stream state
alGetSourcei ( music - > stream . source , AL_BUFFERS_PROCESSED , & processed ) ; // Get processed buffers
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
if ( processed > 0 )
2016-07-15 19:16:34 +03:00
{
2016-08-02 18:32:24 +03:00
bool active = true ;
short pcm [ AUDIO_BUFFER_SIZE ] ;
float pcmf [ AUDIO_BUFFER_SIZE ] ;
2016-09-15 12:53:16 +03:00
int numBuffersToProcess = processed ;
2016-08-16 12:09:55 +03:00
int numSamples = 0 ; // Total size of data steamed in L+R samples for xm floats,
2016-08-02 18:32:24 +03:00
// individual L or R for ogg shorts
for ( int i = 0 ; i < numBuffersToProcess ; i + + )
{
switch ( music - > ctxType )
{
2016-08-16 12:09:55 +03:00
case MUSIC_AUDIO_OGG :
2016-08-02 18:32:24 +03:00
{
if ( music - > samplesLeft > = AUDIO_BUFFER_SIZE ) numSamples = AUDIO_BUFFER_SIZE ;
else numSamples = music - > samplesLeft ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
// NOTE: Returns the number of samples to process (should be the same as numSamples -> it is)
int numSamplesOgg = stb_vorbis_get_samples_short_interleaved ( music - > ctxOgg , music - > stream . channels , pcm , numSamples ) ;
// TODO: Review stereo channels Ogg, not enough samples served!
2016-08-07 14:38:14 +03:00
UpdateAudioStream ( music - > stream , pcm , numSamplesOgg * music - > stream . channels ) ;
music - > samplesLeft - = ( numSamplesOgg * music - > stream . channels ) ;
2016-08-16 12:09:55 +03:00
2016-10-10 19:22:55 +03:00
} break ;
case MUSIC_AUDIO_FLAC :
{
if ( music - > samplesLeft > = AUDIO_BUFFER_SIZE ) numSamples = AUDIO_BUFFER_SIZE ;
else numSamples = music - > samplesLeft ;
int pcmi [ AUDIO_BUFFER_SIZE ] ;
// NOTE: Returns the number of samples to process (should be the same as numSamples)
2016-10-17 01:03:38 +03:00
unsigned int numSamplesFlac = ( unsigned int ) drflac_read_s32 ( music - > ctxFlac , numSamples , pcmi ) ;
2016-10-10 19:22:55 +03:00
2016-10-10 20:42:02 +03:00
UpdateAudioStream ( music - > stream , pcmi , numSamplesFlac * music - > stream . channels ) ;
2016-10-10 19:22:55 +03:00
music - > samplesLeft - = ( numSamples * music - > stream . channels ) ;
2016-08-02 18:32:24 +03:00
} break ;
2016-08-16 12:09:55 +03:00
case MUSIC_MODULE_XM :
2016-08-02 18:32:24 +03:00
{
if ( music - > samplesLeft > = AUDIO_BUFFER_SIZE / 2 ) numSamples = AUDIO_BUFFER_SIZE / 2 ;
else numSamples = music - > samplesLeft ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
// NOTE: Output buffer is 2*numsamples elements (left and right value for each sample)
jar_xm_generate_samples ( music - > ctxXm , pcmf , numSamples ) ;
UpdateAudioStream ( music - > stream , pcmf , numSamples * 2 ) ; // Using 32bit PCM data
music - > samplesLeft - = numSamples ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
//TraceLog(INFO, "Samples left: %i", music->samplesLeft);
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
} break ;
2016-08-16 12:09:55 +03:00
case MUSIC_MODULE_MOD :
2016-08-02 18:32:24 +03:00
{
if ( music - > samplesLeft > = AUDIO_BUFFER_SIZE / 2 ) numSamples = AUDIO_BUFFER_SIZE / 2 ;
else numSamples = music - > samplesLeft ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
// NOTE: Output buffer size is nbsample*channels (default: 48000Hz, 16bit, Stereo)
2016-08-16 12:09:55 +03:00
jar_mod_fillbuffer ( & music - > ctxMod , pcm , numSamples , 0 ) ;
2016-08-02 18:32:24 +03:00
UpdateAudioStream ( music - > stream , pcm , numSamples * 2 ) ;
music - > samplesLeft - = numSamples ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
} break ;
default : break ;
}
2016-07-29 22:35:57 +03:00
2016-08-02 18:32:24 +03:00
if ( music - > samplesLeft < = 0 )
{
active = false ;
break ;
}
}
2016-09-15 12:53:16 +03:00
// This error is registered when UpdateAudioStream() fails
if ( alGetError ( ) = = AL_INVALID_VALUE ) TraceLog ( WARNING , " OpenAL: Error buffering data... " ) ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
// Reset audio stream for looping
2016-09-15 12:53:16 +03:00
if ( ! active )
2016-07-15 19:16:34 +03:00
{
2016-09-15 12:53:16 +03:00
StopMusicStream ( music ) ; // Stop music (and reset)
if ( music - > loop ) 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 ( state ! = AL_PLAYING ) PlayMusicStream ( music ) ;
2016-07-15 19:16:34 +03:00
}
2014-09-17 00:51:31 +04:00
}
2014-04-19 18:36:49 +04:00
}
2016-05-12 04:14:59 +03:00
// Check if any music is playing
2016-08-01 13:49:17 +03:00
bool IsMusicPlaying ( Music music )
2014-04-09 22:25:26 +04:00
{
2014-12-31 20:03:32 +03:00
bool playing = false ;
ALint state ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
alGetSourcei ( music - > stream . source , AL_SOURCE_STATE , & state ) ;
2016-07-29 22:35:57 +03:00
2016-08-01 13:49:17 +03:00
if ( state = = AL_PLAYING ) playing = true ;
2014-09-03 18:51:28 +04:00
2014-12-31 20:03:32 +03:00
return playing ;
2014-04-09 22:25:26 +04:00
}
2014-04-19 18:36:49 +04:00
// Set volume for music
2016-08-01 13:49:17 +03:00
void SetMusicVolume ( Music music , float volume )
2014-01-23 15:36:18 +04:00
{
2016-08-01 13:49:17 +03:00
alSourcef ( music - > stream . source , AL_GAIN , volume ) ;
2016-05-12 04:14:59 +03:00
}
2016-06-02 18:12:31 +03:00
// Set pitch for music
2016-08-01 13:49:17 +03:00
void SetMusicPitch ( Music music , float pitch )
2016-05-12 04:14:59 +03:00
{
2016-08-01 13:49:17 +03:00
alSourcef ( music - > stream . source , AL_PITCH , pitch ) ;
2014-01-23 15:36:18 +04:00
}
2016-06-02 06:09:00 +03:00
// Get music time length (in seconds)
2016-08-01 13:49:17 +03:00
float GetMusicTimeLength ( Music music )
2014-01-23 15:36:18 +04:00
{
2016-08-01 13:49:17 +03:00
float totalSeconds = ( float ) music - > totalSamples / music - > stream . sampleRate ;
2016-08-16 12:09:55 +03:00
2014-04-19 18:36:49 +04:00
return totalSeconds ;
}
// Get current music time played (in seconds)
2016-08-01 13:49:17 +03:00
float GetMusicTimePlayed ( Music music )
2014-04-19 18:36:49 +04:00
{
2016-05-21 19:08:09 +03:00
float secondsPlayed = 0.0f ;
2016-07-29 22:35:57 +03:00
2016-08-02 18:32:24 +03:00
unsigned int samplesPlayed = music - > totalSamples - music - > samplesLeft ;
secondsPlayed = ( float ) samplesPlayed / ( music - > stream . sampleRate * music - > stream . channels ) ;
2016-08-01 13:49:17 +03:00
return secondsPlayed ;
}
// Init audio stream (to stream audio pcm data)
2016-08-02 18:32:24 +03:00
AudioStream InitAudioStream ( unsigned int sampleRate , unsigned int sampleSize , unsigned int channels )
2016-08-01 13:49:17 +03:00
{
AudioStream stream = { 0 } ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
stream . sampleRate = sampleRate ;
stream . sampleSize = sampleSize ;
stream . channels = channels ;
// Setup OpenAL format
if ( channels = = 1 )
{
switch ( sampleSize )
2016-05-15 02:30:32 +03:00
{
2016-08-01 13:49:17 +03:00
case 8 : stream . format = AL_FORMAT_MONO8 ; break ;
case 16 : stream . format = AL_FORMAT_MONO16 ; break ;
case 32 : stream . format = AL_FORMAT_MONO_FLOAT32 ; break ;
default : TraceLog ( WARNING , " Init audio stream: Sample size not supported: %i " , sampleSize ) ; break ;
2016-06-02 06:09:00 +03:00
}
2016-08-01 13:49:17 +03:00
}
else if ( channels = = 2 )
{
switch ( sampleSize )
2016-06-02 06:09:00 +03:00
{
2016-08-01 13:49:17 +03:00
case 8 : stream . format = AL_FORMAT_STEREO8 ; break ;
case 16 : stream . format = AL_FORMAT_STEREO16 ; break ;
case 32 : stream . format = AL_FORMAT_STEREO_FLOAT32 ; break ;
default : TraceLog ( WARNING , " Init audio stream: Sample size not supported: %i " , sampleSize ) ; break ;
2016-05-15 02:30:32 +03:00
}
2016-08-01 13:49:17 +03:00
}
else TraceLog ( WARNING , " Init audio stream: Number of channels not supported: %i " , channels ) ;
// Create an audio source
alGenSources ( 1 , & stream . source ) ;
alSourcef ( stream . source , AL_PITCH , 1 ) ;
alSourcef ( stream . source , AL_GAIN , 1 ) ;
alSource3f ( stream . source , AL_POSITION , 0 , 0 , 0 ) ;
alSource3f ( stream . source , AL_VELOCITY , 0 , 0 , 0 ) ;
2016-08-02 18:32:24 +03:00
// Create Buffers (double buffering)
2016-08-01 13:49:17 +03:00
alGenBuffers ( MAX_STREAM_BUFFERS , stream . buffers ) ;
// Initialize buffer with zeros by default
for ( int i = 0 ; i < MAX_STREAM_BUFFERS ; i + + )
{
if ( stream . sampleSize = = 8 )
2016-05-15 02:30:32 +03:00
{
2016-08-01 13:49:17 +03:00
unsigned char pcm [ AUDIO_BUFFER_SIZE ] = { 0 } ;
alBufferData ( stream . buffers [ i ] , stream . format , pcm , AUDIO_BUFFER_SIZE * sizeof ( unsigned char ) , stream . sampleRate ) ;
}
else if ( stream . sampleSize = = 16 )
{
short pcm [ AUDIO_BUFFER_SIZE ] = { 0 } ;
alBufferData ( stream . buffers [ i ] , stream . format , pcm , AUDIO_BUFFER_SIZE * sizeof ( short ) , stream . sampleRate ) ;
}
else if ( stream . sampleSize = = 32 )
{
float pcm [ AUDIO_BUFFER_SIZE ] = { 0.0f } ;
alBufferData ( stream . buffers [ i ] , stream . format , pcm , AUDIO_BUFFER_SIZE * sizeof ( float ) , stream . sampleRate ) ;
2016-05-15 02:30:32 +03:00
}
2016-04-25 04:18:18 +03:00
}
2014-09-03 18:51:28 +04:00
2016-08-01 13:49:17 +03:00
alSourceQueueBuffers ( stream . source , MAX_STREAM_BUFFERS , stream . buffers ) ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
TraceLog ( INFO , " [AUD ID %i] Audio stream loaded successfully " , stream . source ) ;
return stream ;
2014-04-19 18:36:49 +04:00
}
2016-08-01 13:49:17 +03:00
// Close audio stream and free memory
2016-08-02 18:32:24 +03:00
void CloseAudioStream ( AudioStream stream )
2016-08-01 13:49:17 +03:00
{
// Stop playing channel
alSourceStop ( stream . source ) ;
// Flush out all queued buffers
int queued = 0 ;
alGetSourcei ( stream . source , AL_BUFFERS_QUEUED , & queued ) ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
ALuint buffer = 0 ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
while ( queued > 0 )
{
alSourceUnqueueBuffers ( stream . source , 1 , & buffer ) ;
queued - - ;
}
// Delete source and buffers
alDeleteSources ( 1 , & stream . source ) ;
alDeleteBuffers ( MAX_STREAM_BUFFERS , stream . buffers ) ;
2016-08-16 12:09:55 +03:00
2016-08-01 13:49:17 +03:00
TraceLog ( INFO , " [AUD ID %i] Unloaded audio stream data " , stream . source ) ;
}
2016-08-02 18:32:24 +03:00
// Update audio stream buffers with data
// NOTE: Only one buffer per call
void UpdateAudioStream ( AudioStream stream , void * data , int numSamples )
{
2016-08-01 13:49:17 +03:00
ALuint buffer = 0 ;
alSourceUnqueueBuffers ( stream . source , 1 , & buffer ) ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
// Check if any buffer was available for unqueue
if ( alGetError ( ) ! = AL_INVALID_VALUE )
{
if ( stream . sampleSize = = 8 ) alBufferData ( buffer , stream . format , ( unsigned char * ) data , numSamples * sizeof ( unsigned char ) , stream . sampleRate ) ;
else if ( stream . sampleSize = = 16 ) alBufferData ( buffer , stream . format , ( short * ) data , numSamples * sizeof ( short ) , stream . sampleRate ) ;
else if ( stream . sampleSize = = 32 ) alBufferData ( buffer , stream . format , ( float * ) data , numSamples * sizeof ( float ) , stream . sampleRate ) ;
2016-08-16 12:09:55 +03:00
2016-08-02 18:32:24 +03:00
alSourceQueueBuffers ( stream . source , 1 , & buffer ) ;
}
}
// Check if any audio stream buffers requires refill
bool IsAudioBufferProcessed ( AudioStream stream )
{
ALint processed = 0 ;
2016-08-01 13:49:17 +03:00
2016-08-02 18:32:24 +03:00
// Determine if music stream is ready to be written
alGetSourcei ( stream . source , AL_BUFFERS_PROCESSED , & processed ) ;
2016-08-01 13:49:17 +03:00
2016-08-02 18:32:24 +03:00
return ( processed > 0 ) ;
2016-08-01 13:49:17 +03:00
}
2014-04-19 18:36:49 +04:00
2016-08-02 18:32:24 +03:00
// Play audio stream
void PlayAudioStream ( AudioStream stream )
2014-04-19 18:36:49 +04:00
{
2016-08-02 18:32:24 +03:00
alSourcePlay ( stream . source ) ;
}
2016-07-29 22:35:57 +03:00
2016-08-02 18:32:24 +03:00
// Play audio stream
void PauseAudioStream ( AudioStream stream )
{
alSourcePause ( stream . source ) ;
}
2016-07-29 22:35:57 +03:00
2016-08-02 18:32:24 +03:00
// Resume audio stream playing
void ResumeAudioStream ( AudioStream stream )
{
ALenum state ;
alGetSourcei ( stream . source , AL_SOURCE_STATE , & state ) ;
2016-07-29 22:35:57 +03:00
2016-08-02 18:32:24 +03:00
if ( state = = AL_PAUSED ) alSourcePlay ( stream . source ) ;
2014-04-19 18:36:49 +04:00
}
2016-08-02 18:32:24 +03:00
// Stop audio stream
void StopAudioStream ( AudioStream stream )
{
alSourceStop ( stream . source ) ;
}
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
2013-11-19 02:38:44 +04:00
// Load WAV file into Wave structure
2014-04-19 18:36:49 +04:00
static Wave LoadWAV ( const char * fileName )
2013-11-19 02:38:44 +04:00
{
2013-12-01 15:34:31 +04:00
// Basic WAV headers structs
typedef struct {
char chunkID [ 4 ] ;
2014-11-09 10:06:58 +03:00
int chunkSize ;
2013-12-01 15:34:31 +04:00
char format [ 4 ] ;
} RiffHeader ;
typedef struct {
char subChunkID [ 4 ] ;
2014-11-09 10:06:58 +03:00
int subChunkSize ;
2013-12-01 15:34:31 +04:00
short audioFormat ;
short numChannels ;
2014-11-09 10:06:58 +03:00
int sampleRate ;
int byteRate ;
2013-12-01 15:34:31 +04:00
short blockAlign ;
short bitsPerSample ;
} WaveFormat ;
typedef struct {
char subChunkID [ 4 ] ;
2014-11-09 10:06:58 +03:00
int subChunkSize ;
2013-12-01 15:34:31 +04:00
} WaveData ;
2014-09-03 18:51:28 +04:00
2013-12-01 15:34:31 +04:00
RiffHeader riffHeader ;
WaveFormat waveFormat ;
WaveData waveData ;
2014-09-03 18:51:28 +04:00
2016-01-23 15:22:13 +03:00
Wave wave = { 0 } ;
2013-12-01 15:34:31 +04:00
FILE * wavFile ;
2014-09-03 18:51:28 +04:00
2013-11-19 02:38:44 +04:00
wavFile = fopen ( fileName , " rb " ) ;
2014-09-03 18:51:28 +04:00
2014-12-31 20:03:32 +03:00
if ( wavFile = = NULL )
2013-11-23 16:30:54 +04:00
{
2014-12-31 20:03:32 +03:00
TraceLog ( WARNING , " [%s] WAV file could not be opened " , fileName ) ;
2015-07-31 13:31:39 +03:00
wave . data = NULL ;
2013-11-23 16:30:54 +04:00
}
2014-04-09 22:25:26 +04:00
else
{
// Read in the first chunk into the struct
fread ( & riffHeader , sizeof ( RiffHeader ) , 1 , wavFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Check for RIFF and WAVE tags
2014-11-09 10:06:58 +03:00
if ( strncmp ( riffHeader . chunkID , " RIFF " , 4 ) | |
strncmp ( riffHeader . format , " WAVE " , 4 ) )
2014-04-09 22:25:26 +04:00
{
TraceLog ( WARNING , " [%s] Invalid RIFF or WAVE Header " , fileName ) ;
}
else
{
// Read in the 2nd chunk for the wave info
fread ( & waveFormat , sizeof ( WaveFormat ) , 1 , wavFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Check for fmt tag
if ( ( waveFormat . subChunkID [ 0 ] ! = ' f ' ) | | ( waveFormat . subChunkID [ 1 ] ! = ' m ' ) | |
( waveFormat . subChunkID [ 2 ] ! = ' t ' ) | | ( waveFormat . subChunkID [ 3 ] ! = ' ' ) )
{
TraceLog ( WARNING , " [%s] Invalid Wave format " , fileName ) ;
}
else
{
// Check for extra parameters;
if ( waveFormat . subChunkSize > 16 ) fseek ( wavFile , sizeof ( short ) , SEEK_CUR ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Read in the the last byte of data before the sound file
fread ( & waveData , sizeof ( WaveData ) , 1 , wavFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Check for data tag
if ( ( waveData . subChunkID [ 0 ] ! = ' d ' ) | | ( waveData . subChunkID [ 1 ] ! = ' a ' ) | |
( waveData . subChunkID [ 2 ] ! = ' t ' ) | | ( waveData . subChunkID [ 3 ] ! = ' a ' ) )
{
TraceLog ( WARNING , " [%s] Invalid data header " , fileName ) ;
}
else
{
// Allocate memory for data
2016-08-15 17:35:11 +03:00
wave . data = ( unsigned char * ) malloc ( sizeof ( unsigned char ) * waveData . subChunkSize ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Read in the sound data into the soundData variable
fread ( wave . data , waveData . subChunkSize , 1 , wavFile ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Now we set the variables that we need later
2016-08-15 17:35:11 +03:00
wave . sampleCount = waveData . subChunkSize ;
2014-04-09 22:25:26 +04:00
wave . sampleRate = waveFormat . sampleRate ;
2016-08-15 17:35:11 +03:00
wave . sampleSize = waveFormat . bitsPerSample ;
2014-04-09 22:25:26 +04:00
wave . channels = waveFormat . numChannels ;
2014-09-03 18:51:28 +04:00
2016-08-15 17:35:11 +03:00
TraceLog ( INFO , " [%s] WAV file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i) " , fileName , wave . sampleRate , wave . sampleSize , wave . channels ) ;
2014-04-09 22:25:26 +04:00
}
}
}
2013-11-23 16:30:54 +04:00
2014-04-09 22:25:26 +04:00
fclose ( wavFile ) ;
}
2014-09-03 18:51:28 +04:00
2013-11-23 16:30:54 +04:00
return wave ;
2013-12-01 15:34:31 +04:00
}
2013-11-19 02:38:44 +04:00
2014-04-19 18:36:49 +04:00
// Load OGG file into Wave structure
2014-09-17 00:51:31 +04:00
// NOTE: Using stb_vorbis library
2016-09-08 01:20:06 +03:00
static Wave LoadOGG ( const char * fileName )
2013-11-19 02:38:44 +04:00
{
2014-04-19 18:36:49 +04:00
Wave wave ;
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
stb_vorbis * oggFile = stb_vorbis_open_filename ( fileName , NULL , NULL ) ;
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
if ( oggFile = = NULL )
{
TraceLog ( WARNING , " [%s] OGG file could not be opened " , fileName ) ;
wave . data = NULL ;
}
else
{
stb_vorbis_info info = stb_vorbis_get_info ( oggFile ) ;
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
wave . sampleRate = info . sample_rate ;
2016-08-15 17:35:11 +03:00
wave . sampleSize = 16 ; // 16 bit per sample (short)
2015-07-31 13:31:39 +03:00
wave . channels = info . channels ;
2014-04-09 22:25:26 +04:00
2016-08-01 13:49:17 +03:00
int totalSamplesLength = ( stb_vorbis_stream_length_in_samples ( oggFile ) * info . channels ) ;
2015-07-31 13:31:39 +03:00
float totalSeconds = stb_vorbis_stream_length_in_seconds ( oggFile ) ;
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
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 ) ;
2014-09-03 18:51:28 +04:00
2016-10-17 01:03:38 +03:00
int totalSamples = ( int ) ( totalSeconds * info . sample_rate * info . channels ) ;
2016-08-15 17:35:11 +03:00
wave . sampleCount = totalSamples ;
2014-09-03 18:51:28 +04:00
2016-08-15 17:35:11 +03:00
wave . data = ( short * ) malloc ( totalSamplesLength * sizeof ( short ) ) ;
2014-09-03 18:51:28 +04:00
2016-08-15 17:35:11 +03:00
int samplesObtained = stb_vorbis_get_samples_short_interleaved ( oggFile , info . channels , ( short * ) wave . data , totalSamplesLength ) ;
2015-02-02 02:53:49 +03:00
2015-07-31 13:31:39 +03:00
TraceLog ( DEBUG , " [%s] Samples obtained: %i " , fileName , samplesObtained ) ;
2014-04-19 18:36:49 +04:00
2016-08-15 17:35:11 +03:00
TraceLog ( INFO , " [%s] OGG file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i) " , fileName , wave . sampleRate , wave . sampleSize , wave . channels ) ;
2015-07-31 13:31:39 +03:00
stb_vorbis_close ( oggFile ) ;
}
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
return wave ;
2014-04-09 22:25:26 +04:00
}
2013-11-19 02:38:44 +04:00
2016-10-10 19:22:55 +03:00
// Load FLAC file into Wave structure
// NOTE: Using dr_flac library
static Wave LoadFLAC ( const char * fileName )
{
Wave wave ;
// Decode an entire FLAC file in one go
uint64_t totalSampleCount ;
wave . data = drflac_open_and_decode_file_s32 ( fileName , & wave . channels , & wave . sampleRate , & totalSampleCount ) ;
wave . sampleCount = ( int ) totalSampleCount ;
wave . sampleSize = 32 ;
if ( wave . data = = NULL ) TraceLog ( WARNING , " [%s] FLAC data could not be loaded " , fileName ) ;
return wave ;
}
2015-07-31 13:31:39 +03:00
// Some required functions for audio standalone module version
# if defined(AUDIO_STANDALONE)
// Get the extension for a filename
const char * GetExtension ( const char * fileName )
{
const char * dot = strrchr ( fileName , ' . ' ) ;
2016-08-31 11:27:29 +03:00
if ( ! dot | | dot = = fileName ) return " " ;
2015-07-31 13:31:39 +03:00
return ( dot + 1 ) ;
}
// Outputs a trace log message (INFO, ERROR, WARNING)
// NOTE: If a file has been init, output log is written there
void TraceLog ( int msgType , const char * text , . . . )
{
va_list args ;
int traceDebugMsgs = 0 ;
# ifdef DO_NOT_TRACE_DEBUG_MSGS
traceDebugMsgs = 0 ;
# endif
2016-08-31 11:27:29 +03:00
switch ( msgType )
2015-07-31 13:31:39 +03:00
{
case INFO : fprintf ( stdout , " INFO: " ) ; break ;
case ERROR : fprintf ( stdout , " ERROR: " ) ; break ;
case WARNING : fprintf ( stdout , " WARNING: " ) ; break ;
case DEBUG : if ( traceDebugMsgs ) fprintf ( stdout , " DEBUG: " ) ; break ;
default : break ;
}
if ( ( msgType ! = DEBUG ) | | ( ( msgType = = DEBUG ) & & ( traceDebugMsgs ) ) )
{
va_start ( args , text ) ;
vfprintf ( stdout , text , args ) ;
va_end ( args ) ;
fprintf ( stdout , " \n " ) ;
}
if ( msgType = = ERROR ) exit ( 1 ) ; // If ERROR message, exit program
}
# endif