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
*
2013-11-23 16:30:54 +04:00
* Basic functions to manage Audio : InitAudioDevice , LoadAudioFiles , PlayAudioFiles
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/)
2014-09-03 18:51:28 +04:00
*
2015-07-29 22:41:19 +03:00
* Copyright ( c ) 2014 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)
# include "AL/alext.h" // OpenAL extensions for other format types
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
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-02 18:12:31 +03:00
# include "stb_vorbis.h" // OGG loading functions
2016-04-26 04:40:19 +03:00
# define JAR_XM_IMPLEMENTATION
2016-06-02 18:12:31 +03:00
# include "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-02 18:12:31 +03:00
# include "jar_mod.h" // MOD loading functions
2016-06-02 06:09:00 +03:00
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
2016-05-20 01:22:12 +03:00
# define MAX_STREAM_BUFFERS 2 // Number of buffers for each alSource
# define MAX_MIX_CHANNELS 4 // Number of open AL sources
# define MAX_MUSIC_STREAMS 2 // Number of simultanious music sources
2014-12-31 20:03:32 +03:00
2016-04-17 15:48:20 +03:00
# if defined(PLATFORM_RPI) || defined(PLATFORM_ANDROID)
// NOTE: On RPI and Android should be lower to avoid frame-stalls
2016-05-03 07:59:55 +03:00
# define MUSIC_BUFFER_SIZE_SHORT 4096*2 // PCM data buffer (short) - 16Kb (RPI)
# define MUSIC_BUFFER_SIZE_FLOAT 4096 // PCM data buffer (float) - 16Kb (RPI)
2015-02-02 02:53:49 +03:00
# else
2014-12-31 20:03:32 +03:00
// NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care...
2016-05-03 07:59:55 +03:00
# define MUSIC_BUFFER_SIZE_SHORT 4096*8 // PCM data buffer (short) - 64Kb
# define MUSIC_BUFFER_SIZE_FLOAT 4096*4 // PCM data buffer (float) - 64Kb
2014-12-31 20:03:32 +03:00
# endif
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
2014-04-19 18:36:49 +04:00
2016-05-16 05:37:15 +03:00
// Used to create custom audio streams that are not bound to a specific file. There can be
// no more than 4 concurrent mixchannels in use. This is due to each active mixc being tied to
// a dedicated mix channel.
typedef struct MixChannel_t {
2016-05-03 07:59:55 +03:00
unsigned short sampleRate ; // default is 48000
unsigned char channels ; // 1=mono,2=stereo
unsigned char mixChannel ; // 0-3 or mixA-mixD, each mix channel can receive up to one dedicated audio stream
bool floatingPoint ; // if false then the short datatype is used instead
2016-05-14 10:25:40 +03:00
bool playing ; // false if paused
2016-06-02 18:12:31 +03:00
ALenum alFormat ; // OpenAL format specifier
ALuint alSource ; // OpenAL source
ALuint alBuffer [ MAX_STREAM_BUFFERS ] ; // OpenAL sample buffer
2016-05-16 05:37:15 +03:00
} MixChannel_t ;
2016-04-30 09:43:21 +03:00
2016-05-13 07:14:02 +03:00
// Music type (file streaming from memory)
2016-05-16 05:37:15 +03:00
// NOTE: Anything longer than ~10 seconds should be streamed into a mix channel...
2016-05-13 07:14:02 +03:00
typedef struct Music {
stb_vorbis * stream ;
2016-06-02 18:12:31 +03:00
jar_xm_context_t * xmctx ; // XM chiptune context
jar_mod_context_t modctx ; // MOD chiptune context
2016-05-20 01:22:12 +03:00
MixChannel_t * mixc ; // mix channel
2016-05-13 07:14:02 +03:00
2016-06-02 06:09:00 +03:00
unsigned int totalSamplesLeft ;
2016-05-13 07:14:02 +03:00
float totalLengthSeconds ;
bool loop ;
2016-06-02 18:12:31 +03:00
bool chipTune ; // chiptune is loaded?
2016-05-13 07:14:02 +03:00
} Music ;
2016-06-02 18:12:31 +03:00
// Audio errors registered
typedef enum {
ERROR_RAW_CONTEXT_CREATION = 1 ,
ERROR_XM_CONTEXT_CREATION = 2 ,
ERROR_MOD_CONTEXT_CREATION = 4 ,
ERROR_MIX_CHANNEL_CREATION = 8 ,
ERROR_MUSIC_CHANNEL_CREATION = 16 ,
ERROR_LOADING_XM = 32 ,
ERROR_LOADING_MOD = 64 ,
ERROR_LOADING_WAV = 128 ,
ERROR_LOADING_OGG = 256 ,
ERROR_OUT_OF_MIX_CHANNELS = 512 ,
ERROR_EXTENSION_NOT_RECOGNIZED = 1024 ,
ERROR_UNABLE_TO_OPEN_RRES_FILE = 2048 ,
ERROR_INVALID_RRES_FILE = 4096 ,
ERROR_INVALID_RRES_RESOURCE = 8192 ,
ERROR_UNINITIALIZED_CHANNELS = 16384
} AudioError ;
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-06-02 12:02:23 +03:00
static Music musicChannels_g [ MAX_MUSIC_STREAMS ] ; // Current music loaded, up to two can play at the same time
2016-06-02 19:19:47 +03:00
static MixChannel_t * mixChannels_g [ MAX_MIX_CHANNELS ] ; // What mix channels are currently active
static bool musicEnabled_g = false ;
2016-05-11 10:37:10 +03:00
2016-06-02 19:19:47 +03:00
static int lastAudioError = 0 ; // Registers last audio error
2016-06-02 18:12:31 +03:00
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
2016-05-13 02:02:23 +03:00
static Wave LoadWAV ( const char * fileName ) ; // Load WAV file
static Wave LoadOGG ( char * fileName ) ; // Load OGG file
static void UnloadWave ( Wave wave ) ; // Unload wave data
2014-04-09 22:25:26 +04:00
2016-05-16 05:37:15 +03:00
static bool BufferMusicStream ( int index , int numBuffers ) ; // Fill music buffers with data
2016-05-20 01:22:12 +03:00
static void EmptyMusicStream ( int index ) ; // Empty music buffers
2015-07-31 13:31:39 +03:00
2016-06-02 18:12:31 +03:00
static MixChannel_t * InitMixChannel ( unsigned short sampleRate , unsigned char mixChannel , unsigned char channels , bool floatingPoint ) ; // For streaming into mix channels.
static void CloseMixChannel ( MixChannel_t * mixc ) ; // Frees mix channel
static int BufferMixChannel ( MixChannel_t * mixc , void * data , int numberElements ) ; // Pushes more audio data into mixc mix channel, if NULL is passed it pauses
2016-05-20 06:44:09 +03:00
static int FillAlBufferWithSilence ( MixChannel_t * mixc , ALuint buffer ) ; // Fill buffer with zeros, returns number processed
2016-05-16 05:37:15 +03:00
static void ResampleShortToFloat ( short * shorts , float * floats , unsigned short len ) ; // Pass two arrays of the same legnth in
static void ResampleByteToFloat ( char * chars , float * floats , unsigned short len ) ; // Pass two arrays of same length in
static int IsMusicStreamReadyForBuffering ( int index ) ; // Checks if music buffer is ready to be refilled
2016-05-02 04:53:40 +03: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-05-16 05:37:15 +03:00
// Initialize audio device and mixc
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 " ) ;
2013-11-23 16:30:54 +04:00
ALCcontext * context = alcCreateContext ( device , NULL ) ;
2014-09-03 18:51:28 +04:00
2016-06-02 18:12:31 +03:00
if ( ( context = = NULL ) | | ( alcMakeContextCurrent ( context ) = = ALC_FALSE ) )
2013-11-23 16:30:54 +04:00
{
2016-06-02 18:12:31 +03:00
if ( context ! = NULL ) alcDestroyContext ( context ) ;
2014-09-03 18:51:28 +04:00
2013-11-23 16:30:54 +04:00
alcCloseDevice ( device ) ;
2014-09-03 18:51:28 +04:00
2016-05-16 05:37:15 +03:00
TraceLog ( ERROR , " Could not setup mix channel " ) ;
2013-11-23 16:30:54 +04:00
}
2014-09-17 00:51:31 +04:00
TraceLog ( INFO , " Audio device and context initialized successfully: %s " , alcGetString ( device , ALC_DEVICE_SPECIFIER ) ) ;
2014-09-03 18:51:28 +04:00
2013-11-23 16:30:54 +04: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
{
2016-06-02 18:12:31 +03:00
for ( int index = 0 ; index < MAX_MUSIC_STREAMS ; index + + )
2016-05-13 07:14:02 +03:00
{
2016-06-02 18:12:31 +03:00
if ( musicChannels_g [ index ] . mixc ) StopMusicStream ( index ) ; // Stop music streaming and close current stream
2016-05-13 07:14:02 +03:00
}
2014-04-19 18:36:49 +04:00
2013-11-23 16:30:54 +04:00
ALCdevice * device ;
ALCcontext * context = alcGetCurrentContext ( ) ;
2014-09-03 18:51:28 +04:00
2016-05-16 05:37:15 +03:00
if ( context = = NULL ) TraceLog ( WARNING , " Could not get current mix channel 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 ) ;
2013-11-19 02:38:44 +04:00
}
2016-04-30 09:00:12 +03:00
// True if call to InitAudioDevice() was successful and CloseAudioDevice() has not been called yet
2016-05-01 02:05:43 +03:00
bool IsAudioDeviceReady ( void )
2016-04-30 09:00:12 +03:00
{
ALCcontext * context = alcGetCurrentContext ( ) ;
2016-06-02 18:12:31 +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-06-02 18:12:31 +03:00
2016-04-30 09:00:12 +03:00
if ( device = = NULL ) return false ;
else return true ;
}
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Custom audio output
//----------------------------------------------------------------------------------
2016-05-16 05:37:15 +03:00
// For streaming into mix channels.
// The mixChannel is what audio muxing channel you want to operate on, 0-3 are the ones available. Each mix channel can only be used one at a time.
// exmple usage is InitMixChannel(48000, 0, 2, true); // mixchannel 1, 48khz, stereo, floating point
2016-06-02 18:12:31 +03:00
static MixChannel_t * InitMixChannel ( unsigned short sampleRate , unsigned char mixChannel , unsigned char channels , bool floatingPoint )
2016-04-30 09:00:12 +03:00
{
2016-06-02 18:12:31 +03:00
if ( mixChannel > = MAX_MIX_CHANNELS ) return NULL ;
if ( ! IsAudioDeviceReady ( ) ) InitAudioDevice ( ) ;
2016-04-30 09:00:12 +03:00
2016-06-02 18:12:31 +03:00
if ( ! mixChannels_g [ mixChannel ] )
{
MixChannel_t * mixc = ( MixChannel_t * ) malloc ( sizeof ( MixChannel_t ) ) ;
2016-05-16 05:37:15 +03:00
mixc - > sampleRate = sampleRate ;
mixc - > channels = channels ;
mixc - > mixChannel = mixChannel ;
mixc - > floatingPoint = floatingPoint ;
2016-06-02 12:02:23 +03:00
mixChannels_g [ mixChannel ] = mixc ;
2016-05-01 01:41:46 +03:00
2016-06-02 18:12:31 +03:00
// Setup OpenAL format
if ( channels = = 1 )
2016-05-03 07:59:55 +03:00
{
2016-06-02 18:12:31 +03:00
if ( floatingPoint ) mixc - > alFormat = AL_FORMAT_MONO_FLOAT32 ;
else mixc - > alFormat = AL_FORMAT_MONO16 ;
2016-05-03 07:59:55 +03:00
}
2016-06-02 18:12:31 +03:00
else if ( channels = = 2 )
2016-05-03 07:59:55 +03:00
{
2016-06-02 18:12:31 +03:00
if ( floatingPoint ) mixc - > alFormat = AL_FORMAT_STEREO_FLOAT32 ;
else mixc - > alFormat = AL_FORMAT_STEREO16 ;
2016-05-03 07:59:55 +03:00
}
2016-05-01 01:41:46 +03:00
// Create an audio source
2016-05-16 05:37:15 +03:00
alGenSources ( 1 , & mixc - > alSource ) ;
alSourcef ( mixc - > alSource , AL_PITCH , 1 ) ;
alSourcef ( mixc - > alSource , AL_GAIN , 1 ) ;
alSource3f ( mixc - > alSource , AL_POSITION , 0 , 0 , 0 ) ;
alSource3f ( mixc - > alSource , AL_VELOCITY , 0 , 0 , 0 ) ;
2016-05-01 01:41:46 +03:00
// Create Buffer
2016-05-16 05:37:15 +03:00
alGenBuffers ( MAX_STREAM_BUFFERS , mixc - > alBuffer ) ;
2016-05-02 04:53:40 +03:00
2016-06-02 18:12:31 +03:00
// Fill buffers
for ( int i = 0 ; i < MAX_STREAM_BUFFERS ; i + + ) FillAlBufferWithSilence ( mixc , mixc - > alBuffer [ i ] ) ;
2016-05-03 07:59:55 +03:00
2016-05-16 05:37:15 +03:00
alSourceQueueBuffers ( mixc - > alSource , MAX_STREAM_BUFFERS , mixc - > alBuffer ) ;
mixc - > playing = true ;
alSourcePlay ( mixc - > alSource ) ;
2016-05-01 01:41:46 +03:00
2016-05-16 05:37:15 +03:00
return mixc ;
2016-04-30 09:00:12 +03:00
}
2016-06-02 18:12:31 +03:00
2016-04-30 09:00:12 +03:00
return NULL ;
}
2016-05-16 05:37:15 +03:00
// Frees buffer in mix channel
2016-06-02 18:12:31 +03:00
static void CloseMixChannel ( MixChannel_t * mixc )
2016-04-30 09:00:12 +03:00
{
2016-06-02 18:12:31 +03:00
if ( mixc )
{
2016-05-16 05:37:15 +03:00
alSourceStop ( mixc - > alSource ) ;
mixc - > playing = false ;
2016-05-02 04:53:40 +03:00
2016-06-02 18:12:31 +03:00
// Flush out all queued buffers
2016-05-02 04:53:40 +03:00
ALuint buffer = 0 ;
int queued = 0 ;
2016-05-16 05:37:15 +03:00
alGetSourcei ( mixc - > alSource , AL_BUFFERS_QUEUED , & queued ) ;
2016-06-02 18:12:31 +03:00
2016-05-02 04:53:40 +03:00
while ( queued > 0 )
{
2016-05-16 05:37:15 +03:00
alSourceUnqueueBuffers ( mixc - > alSource , 1 , & buffer ) ;
2016-05-02 04:53:40 +03:00
queued - - ;
}
2016-06-02 18:12:31 +03:00
// Delete source and buffers
2016-05-16 05:37:15 +03:00
alDeleteSources ( 1 , & mixc - > alSource ) ;
alDeleteBuffers ( MAX_STREAM_BUFFERS , mixc - > alBuffer ) ;
2016-06-02 12:02:23 +03:00
mixChannels_g [ mixc - > mixChannel ] = NULL ;
2016-05-16 05:37:15 +03:00
free ( mixc ) ;
mixc = NULL ;
2016-04-30 09:00:12 +03:00
}
}
2016-05-16 05:37:15 +03:00
// Pushes more audio data into mixc mix channel, only one buffer per call
// Call "BufferMixChannel(mixc, NULL, 0)" if you want to pause the audio.
2016-05-03 07:59:55 +03:00
// @Returns number of samples that where processed.
2016-06-02 18:12:31 +03:00
static int BufferMixChannel ( MixChannel_t * mixc , void * data , int numberElements )
2016-04-30 09:43:21 +03:00
{
2016-06-02 18:12:31 +03:00
if ( ! mixc | | ( mixChannels_g [ mixc - > mixChannel ] ! = mixc ) ) return 0 ; // When there is two channels there must be an even number of samples
2016-05-03 12:52:45 +03:00
2016-06-02 18:12:31 +03:00
if ( ! data | | ! numberElements )
{
// Pauses audio until data is given
if ( mixc - > playing )
{
2016-05-16 05:37:15 +03:00
alSourcePause ( mixc - > alSource ) ;
mixc - > playing = false ;
}
2016-06-02 18:12:31 +03:00
2016-05-10 11:54:20 +03:00
return 0 ;
}
2016-06-02 18:12:31 +03:00
else if ( ! mixc - > playing )
{
// Restart audio otherwise
2016-05-16 05:37:15 +03:00
alSourcePlay ( mixc - > alSource ) ;
mixc - > playing = true ;
2016-05-03 12:52:45 +03:00
}
2016-06-02 18:12:31 +03:00
2016-05-16 05:37:15 +03:00
ALuint buffer = 0 ;
alSourceUnqueueBuffers ( mixc - > alSource , 1 , & buffer ) ;
2016-06-02 18:12:31 +03:00
if ( ! buffer ) return 0 ;
if ( mixc - > floatingPoint )
2016-05-01 01:41:46 +03:00
{
2016-06-02 18:12:31 +03:00
// Process float buffers
float * ptr = ( float * ) data ;
2016-05-16 05:37:15 +03:00
alBufferData ( buffer , mixc - > alFormat , ptr , numberElements * sizeof ( float ) , mixc - > sampleRate ) ;
}
2016-06-02 18:12:31 +03:00
else
2016-05-16 05:37:15 +03:00
{
2016-06-02 18:12:31 +03:00
// Process short buffers
short * ptr = ( short * ) data ;
2016-05-16 05:37:15 +03:00
alBufferData ( buffer , mixc - > alFormat , ptr , numberElements * sizeof ( short ) , mixc - > sampleRate ) ;
2016-05-01 01:41:46 +03:00
}
2016-06-02 18:12:31 +03:00
2016-05-16 05:37:15 +03:00
alSourceQueueBuffers ( mixc - > alSource , 1 , & buffer ) ;
return numberElements ;
2016-04-30 09:43:21 +03:00
}
2016-05-03 07:59:55 +03:00
// fill buffer with zeros, returns number processed
2016-05-20 06:44:09 +03:00
static int FillAlBufferWithSilence ( MixChannel_t * mixc , ALuint buffer )
2016-05-02 04:53:40 +03:00
{
2016-06-02 18:12:31 +03:00
if ( mixc - > floatingPoint )
{
float pcm [ MUSIC_BUFFER_SIZE_FLOAT ] = { 0.0f } ;
2016-05-16 05:37:15 +03:00
alBufferData ( buffer , mixc - > alFormat , pcm , MUSIC_BUFFER_SIZE_FLOAT * sizeof ( float ) , mixc - > sampleRate ) ;
2016-06-02 18:12:31 +03:00
2016-05-03 07:59:55 +03:00
return MUSIC_BUFFER_SIZE_FLOAT ;
}
else
{
2016-06-02 18:12:31 +03:00
short pcm [ MUSIC_BUFFER_SIZE_SHORT ] = { 0 } ;
2016-05-16 05:37:15 +03:00
alBufferData ( buffer , mixc - > alFormat , pcm , MUSIC_BUFFER_SIZE_SHORT * sizeof ( short ) , mixc - > sampleRate ) ;
2016-06-02 18:12:31 +03:00
2016-05-03 07:59:55 +03:00
return MUSIC_BUFFER_SIZE_SHORT ;
}
2016-05-02 04:53:40 +03:00
}
2016-05-02 11:24:24 +03:00
// example usage:
// short sh[3] = {1,2,3};float fl[3];
// ResampleShortToFloat(sh,fl,3);
static void ResampleShortToFloat ( short * shorts , float * floats , unsigned short len )
{
2016-06-02 18:12:31 +03:00
for ( int i = 0 ; i < len ; i + + )
2016-05-02 11:24:24 +03:00
{
2016-06-02 19:19:47 +03:00
if ( shorts [ i ] < 0 ) floats [ i ] = ( float ) shorts [ i ] / 32766.0f ;
else floats [ i ] = ( float ) shorts [ i ] / 32767.0f ;
2016-05-02 11:24:24 +03:00
}
}
// example usage:
// char ch[3] = {1,2,3};float fl[3];
2016-05-03 00:37:00 +03:00
// ResampleByteToFloat(ch,fl,3);
2016-05-02 11:24:24 +03:00
static void ResampleByteToFloat ( char * chars , float * floats , unsigned short len )
{
2016-06-02 18:12:31 +03:00
for ( int i = 0 ; i < len ; i + + )
2016-05-02 11:24:24 +03:00
{
2016-06-02 19:19:47 +03:00
if ( chars [ i ] < 0 ) floats [ i ] = ( float ) chars [ i ] / 127.0f ;
else floats [ i ] = ( float ) chars [ i ] / 128.0f ;
2016-05-02 11:24:24 +03:00
}
}
2016-06-02 10:03:00 +03:00
// used to output raw audio streams, returns negative numbers on error, + number represents the mix channel index
2016-05-20 06:44:09 +03:00
// if floating point is false the data size is 16bit short, otherwise it is float 32bit
2016-05-16 05:37:15 +03:00
RawAudioContext InitRawAudioContext ( int sampleRate , int channels , bool floatingPoint )
{
int mixIndex ;
2016-06-02 18:12:31 +03:00
for ( mixIndex = 0 ; mixIndex < MAX_MIX_CHANNELS ; mixIndex + + ) // find empty mix channel slot
2016-05-16 05:37:15 +03:00
{
2016-06-02 18:12:31 +03:00
if ( mixChannels_g [ mixIndex ] = = NULL ) break ;
else if ( mixIndex = = ( MAX_MIX_CHANNELS - 1 ) ) return ERROR_OUT_OF_MIX_CHANNELS ; // error
2016-05-16 05:37:15 +03:00
}
2016-06-02 18:12:31 +03:00
if ( InitMixChannel ( sampleRate , mixIndex , channels , floatingPoint ) ) return mixIndex ;
else return ERROR_RAW_CONTEXT_CREATION ; // error
2016-05-16 05:37:15 +03:00
}
void CloseRawAudioContext ( RawAudioContext ctx )
{
2016-06-02 18:12:31 +03:00
if ( mixChannels_g [ ctx ] ) CloseMixChannel ( mixChannels_g [ ctx ] ) ;
2016-05-16 05:37:15 +03:00
}
2016-06-02 10:03:00 +03:00
// if 0 is returned, the buffers are still full and you need to keep trying with the same data until a + number is returned.
// any + number returned is the number of samples that was processed and passed into buffer.
// data either needs to be array of floats or shorts.
2016-05-31 01:34:29 +03:00
int BufferRawAudioContext ( RawAudioContext ctx , void * data , unsigned short numberElements )
2016-05-20 06:44:09 +03:00
{
int numBuffered = 0 ;
2016-06-02 18:12:31 +03:00
if ( ctx > = 0 )
2016-05-20 06:44:09 +03:00
{
2016-06-02 12:02:23 +03:00
MixChannel_t * mixc = mixChannels_g [ ctx ] ;
2016-05-20 06:44:09 +03:00
numBuffered = BufferMixChannel ( mixc , data , numberElements ) ;
}
2016-06-02 18:12:31 +03:00
2016-05-20 06:44:09 +03:00
return numBuffered ;
}
2014-04-19 18:36:49 +04:00
//----------------------------------------------------------------------------------
// Module Functions Definition - Sounds loading and playing (.WAV)
//----------------------------------------------------------------------------------
2013-11-19 02:38:44 +04:00
// Load sound to memory
Sound LoadSound ( char * fileName )
{
2016-01-23 15:22:13 +03:00
Sound sound = { 0 } ;
Wave wave = { 0 } ;
2014-09-17 00:51:31 +04:00
2013-11-23 16:30:54 +04:00
// NOTE: The entire file is loaded to memory to play it all at once (no-streaming)
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
// Audio file loading
// NOTE: Buffer space is allocated inside function, Wave must be freed
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
if ( strcmp ( GetExtension ( fileName ) , " wav " ) = = 0 ) wave = LoadWAV ( fileName ) ;
else if ( strcmp ( GetExtension ( fileName ) , " ogg " ) = = 0 ) wave = LoadOGG ( fileName ) ;
2016-06-02 18:12:31 +03:00
else
{
2016-06-02 12:02:23 +03:00
TraceLog ( WARNING , " [%s] Sound extension not recognized, it can't be loaded " , fileName ) ;
2016-06-02 18:12:31 +03:00
// TODO: Find a better way to register errors (similar to glGetError())
lastAudioError = ERROR_EXTENSION_NOT_RECOGNIZED ;
2016-06-02 12:02:23 +03:00
}
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
if ( wave . data ! = NULL )
{
ALenum format = 0 ;
// The OpenAL format is worked out by looking at the number of channels and the bits per sample
2014-09-03 18:51:28 +04:00
if ( wave . channels = = 1 )
2014-04-19 18:36:49 +04:00
{
if ( wave . bitsPerSample = = 8 ) format = AL_FORMAT_MONO8 ;
else if ( wave . bitsPerSample = = 16 ) format = AL_FORMAT_MONO16 ;
2014-09-03 18:51:28 +04:00
}
else if ( wave . channels = = 2 )
2014-04-19 18:36:49 +04:00
{
if ( wave . bitsPerSample = = 8 ) format = AL_FORMAT_STEREO8 ;
else if ( wave . bitsPerSample = = 16 ) format = AL_FORMAT_STEREO16 ;
}
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
// Create an audio source
ALuint source ;
alGenSources ( 1 , & source ) ; // Generate pointer to audio source
2014-09-03 18:51:28 +04:00
alSourcef ( source , AL_PITCH , 1 ) ;
2014-04-19 18:36:49 +04:00
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 ) ;
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
// Convert loaded data to OpenAL buffer
//----------------------------------------
ALuint buffer ;
alGenBuffers ( 1 , & buffer ) ; // Generate pointer to buffer
2013-11-23 16:30:54 +04:00
2014-04-19 18:36:49 +04:00
// Upload sound data to buffer
alBufferData ( buffer , format , wave . data , wave . dataSize , wave . sampleRate ) ;
2013-11-23 16:30:54 +04:00
2014-04-19 18:36:49 +04:00
// Attach sound buffer to source
alSourcei ( source , AL_BUFFER , buffer ) ;
2015-02-02 02:53:49 +03:00
2014-12-31 20:03:32 +03:00
TraceLog ( INFO , " [%s] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i) " , fileName , wave . sampleRate , wave . bitsPerSample , wave . channels ) ;
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
// Unallocate WAV data
UnloadWave ( wave ) ;
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
sound . source = source ;
sound . buffer = buffer ;
}
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
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 ;
// The OpenAL format is worked out by looking at the number of channels and the bits per sample
if ( wave . channels = = 1 )
{
if ( wave . bitsPerSample = = 8 ) format = AL_FORMAT_MONO8 ;
else if ( wave . bitsPerSample = = 16 ) format = AL_FORMAT_MONO16 ;
}
else if ( wave . channels = = 2 )
{
if ( wave . bitsPerSample = = 8 ) format = AL_FORMAT_STEREO8 ;
else if ( wave . bitsPerSample = = 16 ) format = AL_FORMAT_STEREO16 ;
}
// 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
// Upload sound data to buffer
alBufferData ( buffer , format , wave . data , wave . dataSize , wave . sampleRate ) ;
// Attach sound buffer to source
alSourcei ( source , AL_BUFFER , buffer ) ;
// Unallocate WAV data
UnloadWave ( wave ) ;
2014-12-31 20:03:32 +03:00
TraceLog ( INFO , " [Wave] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i) " , wave . sampleRate , wave . bitsPerSample , wave . channels ) ;
2014-12-15 03:08:30 +03:00
sound . source = source ;
sound . buffer = buffer ;
}
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
bool found = false ;
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 " ) ;
2015-02-02 02:53:49 +03:00
if ( rresFile = = NULL )
2014-12-31 20:03:32 +03:00
{
TraceLog ( WARNING , " [%s] rRES raylib resource file could not be opened " , rresName ) ;
2016-06-02 18:12:31 +03:00
lastAudioError = ERROR_UNABLE_TO_OPEN_RRES_FILE ;
2014-12-31 20:03:32 +03:00
}
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 ) ;
2016-06-02 18:12:31 +03:00
lastAudioError = ERROR_INVALID_RRES_FILE ;
2014-04-09 22:25:26 +04:00
}
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 ;
wave . dataSize = infoHeader . srcSize ;
wave . bitsPerSample = bps ;
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
2014-04-09 22:25:26 +04:00
// Convert wave to Sound (OpenAL)
ALenum format = 0 ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// The OpenAL format is worked out by looking at the number of channels and the bits per sample
2014-09-03 18:51:28 +04:00
if ( wave . channels = = 1 )
2014-04-09 22:25:26 +04:00
{
if ( wave . bitsPerSample = = 8 ) format = AL_FORMAT_MONO8 ;
else if ( wave . bitsPerSample = = 16 ) format = AL_FORMAT_MONO16 ;
2014-09-03 18:51:28 +04:00
}
else if ( wave . channels = = 2 )
2014-04-09 22:25:26 +04:00
{
if ( wave . bitsPerSample = = 8 ) format = AL_FORMAT_STEREO8 ;
else if ( wave . bitsPerSample = = 16 ) format = AL_FORMAT_STEREO16 ;
}
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Create an audio source
ALuint source ;
alGenSources ( 1 , & source ) ; // Generate pointer to audio source
2014-09-03 18:51:28 +04:00
alSourcef ( source , AL_PITCH , 1 ) ;
2014-04-09 22:25:26 +04:00
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 ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Convert loaded data to OpenAL buffer
//----------------------------------------
ALuint buffer ;
alGenBuffers ( 1 , & buffer ) ; // Generate pointer to buffer
// Upload sound data to buffer
alBufferData ( buffer , format , ( void * ) wave . data , wave . dataSize , wave . sampleRate ) ;
// Attach sound buffer to source
alSourcei ( source , AL_BUFFER , buffer ) ;
2015-02-02 02:53:49 +03:00
2014-12-31 20:03:32 +03:00
TraceLog ( INFO , " [%s] Sound loaded successfully from resource (SampleRate: %i, BitRate: %i, Channels: %i) " , rresName , wave . sampleRate , wave . bitsPerSample , wave . channels ) ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
// Unallocate WAV data
2014-04-19 18:36:49 +04:00
UnloadWave ( wave ) ;
2014-04-09 22:25:26 +04:00
sound . source = source ;
sound . buffer = buffer ;
}
else
{
TraceLog ( WARNING , " [%s] Required resource do not seem to be a valid SOUND resource " , rresName ) ;
2016-06-02 18:12:31 +03:00
lastAudioError = ERROR_INVALID_RRES_RESOURCE ;
2014-04-09 22:25:26 +04:00
}
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 ;
}
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 ) ;
2015-08-05 20:17:56 +03:00
TraceLog ( INFO , " Unloaded sound data " ) ;
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
2013-11-23 16:30:54 +04: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
}
// 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 ) ;
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Music loading and stream playing (.OGG)
//----------------------------------------------------------------------------------
// Start music playing (open stream)
2016-05-12 06:15:37 +03:00
// returns 0 on success
2016-06-02 20:09:56 +03:00
int PlayMusicStream ( int index , char * fileName )
2014-04-19 18:36:49 +04:00
{
2016-05-11 10:37:10 +03:00
int mixIndex ;
2016-05-12 06:15:37 +03:00
2016-06-02 20:09:56 +03:00
if ( musicChannels_g [ index ] . stream | | musicChannels_g [ index ] . xmctx ) return ERROR_UNINITIALIZED_CHANNELS ; // error
2016-05-12 06:15:37 +03:00
2016-06-02 18:12:31 +03:00
for ( mixIndex = 0 ; mixIndex < MAX_MIX_CHANNELS ; mixIndex + + ) // find empty mix channel slot
2016-05-11 10:37:10 +03:00
{
2016-06-02 18:12:31 +03:00
if ( mixChannels_g [ mixIndex ] = = NULL ) break ;
else if ( mixIndex = = ( MAX_MIX_CHANNELS - 1 ) ) return ERROR_OUT_OF_MIX_CHANNELS ; // error
2016-05-11 10:37:10 +03:00
}
2014-04-19 18:36:49 +04:00
if ( strcmp ( GetExtension ( fileName ) , " ogg " ) = = 0 )
{
// Open audio stream
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . stream = stb_vorbis_open_filename ( fileName , NULL , NULL ) ;
2014-09-03 18:51:28 +04:00
2016-06-02 20:09:56 +03:00
if ( musicChannels_g [ index ] . stream = = NULL )
2014-12-31 20:03:32 +03:00
{
TraceLog ( WARNING , " [%s] OGG audio file could not be opened " , fileName ) ;
2016-06-02 12:02:23 +03:00
return ERROR_LOADING_OGG ; // error
2014-12-31 20:03:32 +03:00
}
2014-04-19 18:36:49 +04:00
else
{
// Get file info
2016-06-02 20:09:56 +03:00
stb_vorbis_info info = stb_vorbis_get_info ( musicChannels_g [ index ] . stream ) ;
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
TraceLog ( INFO , " [%s] Ogg sample rate: %i " , fileName , info . sample_rate ) ;
TraceLog ( INFO , " [%s] Ogg channels: %i " , fileName , info . channels ) ;
2015-12-03 15:45:06 +03:00
TraceLog ( DEBUG , " [%s] Temp memory required: %i " , fileName , info . temp_memory_required ) ;
2014-09-03 18:51:28 +04:00
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . loop = true ; // We loop by default
2016-05-15 01:26:17 +03:00
musicEnabled_g = true ;
2016-05-12 04:14:59 +03:00
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . totalSamplesLeft = ( unsigned int ) stb_vorbis_stream_length_in_samples ( musicChannels_g [ index ] . stream ) * info . channels ;
musicChannels_g [ index ] . totalLengthSeconds = stb_vorbis_stream_length_in_seconds ( musicChannels_g [ index ] . stream ) ;
2016-05-12 04:14:59 +03:00
2016-06-02 18:12:31 +03:00
if ( info . channels = = 2 )
{
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . mixc = InitMixChannel ( info . sample_rate , mixIndex , 2 , false ) ;
musicChannels_g [ index ] . mixc - > playing = true ;
2016-05-11 10:37:10 +03:00
}
2016-06-02 18:12:31 +03:00
else
{
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . mixc = InitMixChannel ( info . sample_rate , mixIndex , 1 , false ) ;
musicChannels_g [ index ] . mixc - > playing = true ;
2016-05-11 10:37:10 +03:00
}
2016-06-02 18:12:31 +03:00
2016-06-02 20:09:56 +03:00
if ( ! musicChannels_g [ index ] . mixc ) return ERROR_LOADING_OGG ; // error
2014-04-19 18:36:49 +04:00
}
}
2016-04-25 04:18:18 +03:00
else if ( strcmp ( GetExtension ( fileName ) , " xm " ) = = 0 )
{
2016-04-26 06:05:03 +03:00
// only stereo is supported for xm
2016-06-02 20:09:56 +03:00
if ( ! jar_xm_create_context_from_file ( & musicChannels_g [ index ] . xmctx , 48000 , fileName ) )
2016-04-25 04:18:18 +03:00
{
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . chipTune = true ;
musicChannels_g [ index ] . loop = true ;
jar_xm_set_max_loop_count ( musicChannels_g [ index ] . xmctx , 0 ) ; // infinite number of loops
musicChannels_g [ index ] . totalSamplesLeft = ( unsigned int ) jar_xm_get_remaining_samples ( musicChannels_g [ index ] . xmctx ) ;
musicChannels_g [ index ] . totalLengthSeconds = ( ( float ) musicChannels_g [ index ] . totalSamplesLeft ) / 48000.f ;
2016-05-15 01:26:17 +03:00
musicEnabled_g = true ;
2016-04-26 06:05:03 +03:00
2016-06-02 20:09:56 +03:00
TraceLog ( INFO , " [%s] XM number of samples: %i " , fileName , musicChannels_g [ index ] . totalSamplesLeft ) ;
TraceLog ( INFO , " [%s] XM track length: %11.6f sec " , fileName , musicChannels_g [ index ] . totalLengthSeconds ) ;
2016-04-27 02:50:07 +03:00
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . mixc = InitMixChannel ( 48000 , mixIndex , 2 , true ) ;
2016-06-02 18:12:31 +03:00
2016-06-02 20:09:56 +03:00
if ( ! musicChannels_g [ index ] . mixc ) return ERROR_XM_CONTEXT_CREATION ; // error
2016-06-02 18:12:31 +03:00
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . mixc - > playing = true ;
2016-04-25 04:18:18 +03:00
}
2016-05-12 06:15:37 +03:00
else
{
TraceLog ( WARNING , " [%s] XM file could not be opened " , fileName ) ;
2016-06-02 12:02:23 +03:00
return ERROR_LOADING_XM ; // error
2016-05-12 06:15:37 +03:00
}
}
2016-06-02 06:09:00 +03:00
else if ( strcmp ( GetExtension ( fileName ) , " mod " ) = = 0 )
{
2016-06-02 20:09:56 +03:00
jar_mod_init ( & musicChannels_g [ index ] . modctx ) ;
2016-06-02 18:12:31 +03:00
2016-06-02 20:09:56 +03:00
if ( jar_mod_load_file ( & musicChannels_g [ index ] . modctx , fileName ) )
2016-06-02 06:09:00 +03:00
{
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . chipTune = true ;
musicChannels_g [ index ] . loop = true ;
musicChannels_g [ index ] . totalSamplesLeft = ( unsigned int ) jar_mod_max_samples ( & musicChannels_g [ index ] . modctx ) ;
musicChannels_g [ index ] . totalLengthSeconds = ( ( float ) musicChannels_g [ index ] . totalSamplesLeft ) / 48000.f ;
2016-06-02 06:09:00 +03:00
musicEnabled_g = true ;
2016-06-02 20:09:56 +03:00
TraceLog ( INFO , " [%s] MOD number of samples: %i " , fileName , musicChannels_g [ index ] . totalSamplesLeft ) ;
TraceLog ( INFO , " [%s] MOD track length: %11.6f sec " , fileName , musicChannels_g [ index ] . totalLengthSeconds ) ;
2016-06-02 06:09:00 +03:00
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . mixc = InitMixChannel ( 48000 , mixIndex , 2 , false ) ;
2016-06-02 18:12:31 +03:00
2016-06-02 20:09:56 +03:00
if ( ! musicChannels_g [ index ] . mixc ) return ERROR_MOD_CONTEXT_CREATION ; // error
2016-06-02 18:12:31 +03:00
2016-06-02 20:09:56 +03:00
musicChannels_g [ index ] . mixc - > playing = true ;
2016-06-02 06:09:00 +03:00
}
else
{
TraceLog ( WARNING , " [%s] MOD file could not be opened " , fileName ) ;
2016-06-02 12:02:23 +03:00
return ERROR_LOADING_MOD ; // error
2016-06-02 06:09:00 +03:00
}
}
2016-05-12 06:15:37 +03:00
else
{
TraceLog ( WARNING , " [%s] Music extension not recognized, it can't be loaded " , fileName ) ;
2016-06-02 12:02:23 +03:00
return ERROR_EXTENSION_NOT_RECOGNIZED ; // error
2016-04-25 04:18:18 +03:00
}
2016-06-02 18:12:31 +03:00
2016-05-12 06:15:37 +03:00
return 0 ; // normal return
2014-04-19 18:36:49 +04:00
}
2016-06-02 12:02:23 +03:00
// Stop music playing for individual music index of musicChannels_g array (close stream)
2016-05-11 10:37:10 +03:00
void StopMusicStream ( int index )
2014-04-19 18:36:49 +04:00
{
2016-06-02 12:02:23 +03:00
if ( index < MAX_MUSIC_STREAMS & & musicChannels_g [ index ] . mixc )
2014-04-19 18:36:49 +04:00
{
2016-06-02 12:02:23 +03:00
CloseMixChannel ( musicChannels_g [ index ] . mixc ) ;
2016-04-25 04:18:18 +03:00
2016-06-02 12:02:23 +03:00
if ( musicChannels_g [ index ] . chipTune & & musicChannels_g [ index ] . xmctx )
2016-04-25 04:18:18 +03:00
{
2016-06-02 12:02:23 +03:00
jar_xm_free_context ( musicChannels_g [ index ] . xmctx ) ;
musicChannels_g [ index ] . xmctx = 0 ;
2016-06-02 06:09:00 +03:00
}
2016-06-02 18:12:31 +03:00
else if ( musicChannels_g [ index ] . chipTune & & musicChannels_g [ index ] . modctx . mod_loaded ) jar_mod_unload ( & musicChannels_g [ index ] . modctx ) ;
else stb_vorbis_close ( musicChannels_g [ index ] . stream ) ;
2016-05-11 10:37:10 +03:00
2016-06-02 18:12:31 +03:00
if ( ! GetMusicStreamCount ( ) ) musicEnabled_g = false ;
if ( musicChannels_g [ index ] . stream | | musicChannels_g [ index ] . xmctx )
2016-05-14 10:25:40 +03:00
{
2016-06-02 12:02:23 +03:00
musicChannels_g [ index ] . stream = NULL ;
musicChannels_g [ index ] . xmctx = NULL ;
2016-05-14 10:25:40 +03:00
}
2014-04-19 18:36:49 +04:00
}
2016-05-11 10:37:10 +03:00
}
2014-09-03 18:51:28 +04:00
2016-05-11 10:37:10 +03:00
//get number of music channels active at this time, this does not mean they are playing
2016-06-02 18:12:31 +03:00
int GetMusicStreamCount ( void )
2016-05-11 10:37:10 +03:00
{
2016-05-14 10:25:40 +03:00
int musicCount = 0 ;
2016-06-02 18:12:31 +03:00
// Find empty music slot
for ( int musicIndex = 0 ; musicIndex < MAX_MUSIC_STREAMS ; musicIndex + + )
{
2016-06-02 12:02:23 +03:00
if ( musicChannels_g [ musicIndex ] . stream ! = NULL | | musicChannels_g [ musicIndex ] . chipTune ) musicCount + + ;
2016-06-02 18:12:31 +03:00
}
2016-05-14 10:25:40 +03:00
2016-05-11 10:37:10 +03:00
return musicCount ;
2014-04-19 18:36:49 +04:00
}
// Pause music playing
2016-05-11 10:37:10 +03:00
void PauseMusicStream ( int index )
2014-04-19 18:36:49 +04:00
{
2014-09-17 00:51:31 +04:00
// Pause music stream if music available!
2016-06-02 12:02:23 +03:00
if ( index < MAX_MUSIC_STREAMS & & musicChannels_g [ index ] . mixc & & musicEnabled_g )
2014-09-17 00:51:31 +04:00
{
TraceLog ( INFO , " Pausing music stream " ) ;
2016-06-02 12:02:23 +03:00
alSourcePause ( musicChannels_g [ index ] . mixc - > alSource ) ;
musicChannels_g [ index ] . mixc - > playing = false ;
2014-09-17 00:51:31 +04:00
}
}
// Resume music playing
2016-05-11 10:37:10 +03:00
void ResumeMusicStream ( int index )
2014-09-17 00:51:31 +04:00
{
// Resume music playing... if music available!
2015-01-21 02:12:54 +03:00
ALenum state ;
2016-06-02 18:12:31 +03:00
if ( index < MAX_MUSIC_STREAMS & & musicChannels_g [ index ] . mixc )
{
2016-06-02 12:02:23 +03:00
alGetSourcei ( musicChannels_g [ index ] . mixc - > alSource , AL_SOURCE_STATE , & state ) ;
2016-06-02 18:12:31 +03:00
2016-05-11 10:37:10 +03:00
if ( state = = AL_PAUSED )
{
TraceLog ( INFO , " Resuming music stream " ) ;
2016-06-02 12:02:23 +03:00
alSourcePlay ( musicChannels_g [ index ] . mixc - > alSource ) ;
musicChannels_g [ index ] . mixc - > playing = true ;
2016-05-11 10:37:10 +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
bool IsMusicPlaying ( int index )
2014-04-09 22:25:26 +04:00
{
2014-12-31 20:03:32 +03:00
bool playing = false ;
ALint state ;
2016-05-11 10:37:10 +03:00
2016-06-02 18:12:31 +03:00
if ( index < MAX_MUSIC_STREAMS & & musicChannels_g [ index ] . mixc )
{
2016-06-02 12:02:23 +03:00
alGetSourcei ( musicChannels_g [ index ] . mixc - > alSource , AL_SOURCE_STATE , & state ) ;
2016-06-02 18:12:31 +03:00
2016-05-12 04:14:59 +03:00
if ( state = = AL_PLAYING ) playing = true ;
2016-05-11 10:37:10 +03:00
}
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-05-11 10:37:10 +03:00
void SetMusicVolume ( int index , float volume )
2014-01-23 15:36:18 +04:00
{
2016-06-02 18:12:31 +03:00
if ( index < MAX_MUSIC_STREAMS & & musicChannels_g [ index ] . mixc )
{
2016-06-02 12:02:23 +03:00
alSourcef ( musicChannels_g [ index ] . mixc - > alSource , AL_GAIN , volume ) ;
2016-05-12 04:14:59 +03:00
}
}
2016-06-02 18:12:31 +03:00
// Set pitch for music
2016-05-12 04:14:59 +03:00
void SetMusicPitch ( int index , float pitch )
{
2016-06-02 18:12:31 +03:00
if ( index < MAX_MUSIC_STREAMS & & musicChannels_g [ index ] . mixc )
{
2016-06-02 12:02:23 +03:00
alSourcef ( musicChannels_g [ index ] . mixc - > alSource , AL_PITCH , pitch ) ;
2016-05-11 10:37:10 +03:00
}
2014-01-23 15:36:18 +04:00
}
2016-06-02 06:09:00 +03:00
// Get music time length (in seconds)
2016-05-12 08:37:53 +03:00
float GetMusicTimeLength ( int index )
2014-01-23 15:36:18 +04:00
{
2016-04-25 04:18:18 +03:00
float totalSeconds ;
2016-06-02 18:12:31 +03:00
if ( musicChannels_g [ index ] . chipTune ) totalSeconds = ( float ) musicChannels_g [ index ] . totalLengthSeconds ;
else totalSeconds = stb_vorbis_stream_length_in_seconds ( musicChannels_g [ index ] . stream ) ;
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
return totalSeconds ;
}
// Get current music time played (in seconds)
2016-05-12 08:37:53 +03:00
float GetMusicTimePlayed ( int index )
2014-04-19 18:36:49 +04:00
{
2016-05-21 19:08:09 +03:00
float secondsPlayed = 0.0f ;
2016-06-02 18:12:31 +03:00
if ( index < MAX_MUSIC_STREAMS & & musicChannels_g [ index ] . mixc )
2016-04-25 04:18:18 +03:00
{
2016-06-02 12:02:23 +03:00
if ( musicChannels_g [ index ] . chipTune & & musicChannels_g [ index ] . xmctx )
2016-05-15 02:30:32 +03:00
{
uint64_t samples ;
2016-06-02 12:02:23 +03:00
jar_xm_get_position ( musicChannels_g [ index ] . xmctx , NULL , NULL , NULL , & samples ) ;
secondsPlayed = ( float ) samples / ( 48000.f * musicChannels_g [ index ] . mixc - > channels ) ; // Not sure if this is the correct value
2016-06-02 06:09:00 +03:00
}
2016-06-02 12:02:23 +03:00
else if ( musicChannels_g [ index ] . chipTune & & musicChannels_g [ index ] . modctx . mod_loaded )
2016-06-02 06:09:00 +03:00
{
2016-06-02 12:02:23 +03:00
long numsamp = jar_mod_current_samples ( & musicChannels_g [ index ] . modctx ) ;
2016-06-02 06:09:00 +03:00
secondsPlayed = ( float ) numsamp / ( 48000.f ) ;
2016-05-15 02:30:32 +03:00
}
else
{
2016-06-02 12:02:23 +03:00
int totalSamples = stb_vorbis_stream_length_in_samples ( musicChannels_g [ index ] . stream ) * musicChannels_g [ index ] . mixc - > channels ;
int samplesPlayed = totalSamples - musicChannels_g [ index ] . totalSamplesLeft ;
secondsPlayed = ( float ) samplesPlayed / ( musicChannels_g [ index ] . mixc - > sampleRate * musicChannels_g [ index ] . mixc - > channels ) ;
2016-05-15 02:30:32 +03:00
}
2016-04-25 04:18:18 +03:00
}
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
return secondsPlayed ;
}
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// Fill music buffers with new data from music stream
2016-05-16 05:37:15 +03:00
static bool BufferMusicStream ( int index , int numBuffers )
2014-04-19 18:36:49 +04:00
{
2016-05-03 07:59:55 +03:00
short pcm [ MUSIC_BUFFER_SIZE_SHORT ] ;
2016-06-02 10:03:00 +03:00
float pcmf [ MUSIC_BUFFER_SIZE_FLOAT ] ;
2016-04-26 08:18:49 +03:00
2016-06-02 18:12:31 +03:00
int size = 0 ; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts
bool active = true ; // We can get more data from stream (not finished)
2016-05-15 02:30:32 +03:00
2016-06-02 12:02:23 +03:00
if ( musicChannels_g [ index ] . chipTune ) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes.
2016-05-15 02:30:32 +03:00
{
2016-06-02 18:12:31 +03:00
for ( int i = 0 ; i < numBuffers ; i + + )
2016-05-16 05:37:15 +03:00
{
2016-06-02 18:12:31 +03:00
if ( musicChannels_g [ index ] . modctx . mod_loaded )
{
if ( musicChannels_g [ index ] . totalSamplesLeft > = MUSIC_BUFFER_SIZE_SHORT ) size = MUSIC_BUFFER_SIZE_SHORT / 2 ;
else size = musicChannels_g [ index ] . totalSamplesLeft / 2 ;
2016-06-02 12:02:23 +03:00
jar_mod_fillbuffer ( & musicChannels_g [ index ] . modctx , pcm , size , 0 ) ;
2016-06-02 18:12:31 +03:00
BufferMixChannel ( musicChannels_g [ index ] . mixc , pcm , size * 2 ) ;
2016-06-02 10:03:00 +03:00
}
2016-06-02 18:12:31 +03:00
else if ( musicChannels_g [ index ] . xmctx )
{
if ( musicChannels_g [ index ] . totalSamplesLeft > = MUSIC_BUFFER_SIZE_FLOAT ) size = MUSIC_BUFFER_SIZE_FLOAT / 2 ;
else size = musicChannels_g [ index ] . totalSamplesLeft / 2 ;
2016-06-02 12:02:23 +03:00
jar_xm_generate_samples ( musicChannels_g [ index ] . xmctx , pcmf , size ) ; // reads 2*readlen shorts and moves them to buffer+size memory location
2016-06-02 18:12:31 +03:00
BufferMixChannel ( musicChannels_g [ index ] . mixc , pcmf , size * 2 ) ;
2016-06-02 10:03:00 +03:00
}
2016-06-02 12:02:23 +03:00
musicChannels_g [ index ] . totalSamplesLeft - = size ;
2016-06-02 18:12:31 +03:00
if ( musicChannels_g [ index ] . totalSamplesLeft < = 0 )
2016-05-16 05:37:15 +03:00
{
active = false ;
break ;
}
}
2014-04-19 18:36:49 +04:00
}
2016-05-15 02:30:32 +03:00
else
{
2016-06-02 18:12:31 +03:00
if ( musicChannels_g [ index ] . totalSamplesLeft > = MUSIC_BUFFER_SIZE_SHORT ) size = MUSIC_BUFFER_SIZE_SHORT ;
else size = musicChannels_g [ index ] . totalSamplesLeft ;
2016-05-15 02:30:32 +03:00
2016-06-02 18:12:31 +03:00
for ( int i = 0 ; i < numBuffers ; i + + )
2016-05-16 05:37:15 +03:00
{
2016-06-02 12:02:23 +03:00
int streamedBytes = stb_vorbis_get_samples_short_interleaved ( musicChannels_g [ index ] . stream , musicChannels_g [ index ] . mixc - > channels , pcm , size ) ;
BufferMixChannel ( musicChannels_g [ index ] . mixc , pcm , streamedBytes * musicChannels_g [ index ] . mixc - > channels ) ;
musicChannels_g [ index ] . totalSamplesLeft - = streamedBytes * musicChannels_g [ index ] . mixc - > channels ;
2016-06-02 18:12:31 +03:00
if ( musicChannels_g [ index ] . totalSamplesLeft < = 0 )
2016-05-16 05:37:15 +03:00
{
active = false ;
break ;
}
}
2016-05-15 02:30:32 +03:00
}
2014-09-03 18:51:28 +04:00
return active ;
2014-04-19 18:36:49 +04:00
}
// Empty music buffers
2016-05-12 08:37:53 +03:00
static void EmptyMusicStream ( int index )
2014-04-19 18:36:49 +04:00
{
2014-09-03 18:51:28 +04:00
ALuint buffer = 0 ;
2014-04-19 18:36:49 +04:00
int queued = 0 ;
2014-09-03 18:51:28 +04:00
2016-06-02 12:02:23 +03:00
alGetSourcei ( musicChannels_g [ index ] . mixc - > alSource , AL_BUFFERS_QUEUED , & queued ) ;
2014-09-03 18:51:28 +04:00
2015-12-03 15:45:06 +03:00
while ( queued > 0 )
2014-04-19 18:36:49 +04:00
{
2016-06-02 12:02:23 +03:00
alSourceUnqueueBuffers ( musicChannels_g [ index ] . mixc - > alSource , 1 , & buffer ) ;
2014-09-03 18:51:28 +04:00
2014-04-19 18:36:49 +04:00
queued - - ;
}
}
2016-06-02 18:12:31 +03:00
// Determine if a music stream is ready to be written
2016-05-16 05:37:15 +03:00
static int IsMusicStreamReadyForBuffering ( int index )
2016-05-15 01:26:17 +03:00
{
ALint processed = 0 ;
2016-06-02 12:02:23 +03:00
alGetSourcei ( musicChannels_g [ index ] . mixc - > alSource , AL_BUFFERS_PROCESSED , & processed ) ;
2016-05-16 05:37:15 +03:00
return processed ;
2016-05-15 01:26:17 +03:00
}
2014-04-19 18:36:49 +04:00
// Update (re-fill) music buffers if data already processed
2016-05-12 08:37:53 +03:00
void UpdateMusicStream ( int index )
2014-04-19 18:36:49 +04:00
{
2016-05-13 02:02:23 +03:00
ALenum state ;
2014-04-19 18:36:49 +04:00
bool active = true ;
2016-05-16 05:37:15 +03:00
int numBuffers = IsMusicStreamReadyForBuffering ( index ) ;
2016-06-02 18:12:31 +03:00
if ( musicChannels_g [ index ] . mixc - > playing & & ( index < MAX_MUSIC_STREAMS ) & & musicEnabled_g & & musicChannels_g [ index ] . mixc & & numBuffers )
2014-04-19 18:36:49 +04:00
{
2016-05-16 05:37:15 +03:00
active = BufferMusicStream ( index , numBuffers ) ;
2016-05-13 02:02:23 +03:00
2016-06-02 12:02:23 +03:00
if ( ! active & & musicChannels_g [ index ] . loop )
2014-04-19 18:36:49 +04:00
{
2016-06-02 12:02:23 +03:00
if ( musicChannels_g [ index ] . chipTune )
2014-04-19 18:36:49 +04:00
{
2016-06-02 12:02:23 +03:00
if ( musicChannels_g [ index ] . modctx . mod_loaded ) jar_mod_seek_start ( & musicChannels_g [ index ] . modctx ) ;
musicChannels_g [ index ] . totalSamplesLeft = musicChannels_g [ index ] . totalLengthSeconds * 48000 ;
2014-04-19 18:36:49 +04:00
}
2016-05-13 02:02:23 +03:00
else
{
2016-06-02 12:02:23 +03:00
stb_vorbis_seek_start ( musicChannels_g [ index ] . stream ) ;
musicChannels_g [ index ] . totalSamplesLeft = stb_vorbis_stream_length_in_samples ( musicChannels_g [ index ] . stream ) * musicChannels_g [ index ] . mixc - > channels ;
2016-05-13 02:02:23 +03:00
}
2016-06-02 18:12:31 +03:00
2016-05-15 12:09:57 +03:00
active = true ;
2016-05-13 02:02:23 +03:00
}
2014-09-03 18:51:28 +04:00
2016-05-13 02:02:23 +03:00
if ( alGetError ( ) ! = AL_NO_ERROR ) TraceLog ( WARNING , " Error buffering data... " ) ;
2016-05-13 07:14:02 +03:00
2016-06-02 12:02:23 +03:00
alGetSourcei ( musicChannels_g [ index ] . mixc - > alSource , AL_SOURCE_STATE , & state ) ;
2014-09-03 18:51:28 +04:00
2016-06-02 12:02:23 +03:00
if ( state ! = AL_PLAYING & & active ) alSourcePlay ( musicChannels_g [ index ] . mixc - > alSource ) ;
2014-09-03 18:51:28 +04:00
2016-05-13 07:14:02 +03:00
if ( ! active ) StopMusicStream ( index ) ;
}
2014-01-23 15:36:18 +04:00
}
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
2014-09-03 18:51:28 +04:00
wave . data = ( unsigned char * ) malloc ( sizeof ( unsigned char ) * waveData . subChunkSize ) ;
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
wave . dataSize = waveData . subChunkSize ;
wave . sampleRate = waveFormat . sampleRate ;
wave . channels = waveFormat . numChannels ;
wave . bitsPerSample = waveFormat . bitsPerSample ;
2014-09-03 18:51:28 +04:00
2014-12-31 20:03:32 +03:00
TraceLog ( INFO , " [%s] WAV file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i) " , fileName , wave . sampleRate , wave . bitsPerSample , 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
2014-04-19 18:36:49 +04:00
static Wave LoadOGG ( 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 ;
wave . bitsPerSample = 16 ;
wave . channels = info . channels ;
2014-04-09 22:25:26 +04:00
2015-07-31 13:31:39 +03:00
TraceLog ( DEBUG , " [%s] Ogg sample rate: %i " , fileName , info . sample_rate ) ;
TraceLog ( DEBUG , " [%s] Ogg channels: %i " , fileName , info . channels ) ;
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
int totalSamplesLength = ( stb_vorbis_stream_length_in_samples ( oggFile ) * info . channels ) ;
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
wave . dataSize = totalSamplesLength * sizeof ( short ) ; // Size must be in bytes
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
TraceLog ( DEBUG , " [%s] Samples length: %i " , fileName , totalSamplesLength ) ;
2014-09-03 18:51:28 +04:00
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
TraceLog ( DEBUG , " [%s] Total seconds: %f " , fileName , totalSeconds ) ;
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
2015-07-31 13:31:39 +03:00
int totalSamples = totalSeconds * info . sample_rate * info . channels ;
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
TraceLog ( DEBUG , " [%s] Total samples calculated: %i " , fileName , totalSamples ) ;
2014-04-09 22:25:26 +04:00
2015-07-31 13:31:39 +03:00
wave . data = malloc ( sizeof ( short ) * totalSamplesLength ) ;
2014-09-03 18:51:28 +04:00
2015-07-31 13:31:39 +03:00
int samplesObtained = stb_vorbis_get_samples_short_interleaved ( oggFile , info . channels , 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
2015-07-31 13:31:39 +03:00
TraceLog ( INFO , " [%s] OGG file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i) " , fileName , wave . sampleRate , wave . bitsPerSample , wave . channels ) ;
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
2014-04-19 18:36:49 +04:00
// Unload Wave data
static void UnloadWave ( Wave wave )
2014-04-09 22:25:26 +04:00
{
2014-04-19 18:36:49 +04:00
free ( wave . data ) ;
2015-08-05 20:17:56 +03:00
TraceLog ( INFO , " Unloaded wave data " ) ;
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 , ' . ' ) ;
if ( ! dot | | dot = = fileName ) return " " ;
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
switch ( msgType )
{
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