Parcourir la source

Merge pull request #123 from kd7tck/develop

mod player
Ray il y a 9 ans
Parent
commit
7447b3e1da

+ 2 - 2
CMakeLists.txt

@@ -8,7 +8,7 @@ set(CMAKE_C_FLAGS "-O1 -Wall -std=gnu99 -fgnu89-inline")
 IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_DESKTOP")
 
      add_definitions(-DPLATFORM_DESKTOP, -DGRAPHICS_API_OPENGL_33)
-     include_directories("." "src/" "external/openal_soft/include" "external/glew/include" "external/glfw3/include")
+     include_directories("." "src/" "external/openal_soft/include" "external/glfw3/include")
 
 ENDIF()
 
@@ -22,7 +22,7 @@ ENDIF()
 IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_WEB")
 
      add_definitions(-DPLATFORM_WEB, -GRAPHICS_API_OPENGL_ES2)
-     include_directories("." "src/" "external/openal_soft/include" "external/glew/include" "external/glfw3/include")
+     include_directories("." "src/" "external/openal_soft/include" "external/glfw3/include")
 
 ENDIF()
 

+ 85 - 2
external/openal_soft/include/AL/alext.h

@@ -13,8 +13,8 @@
  *
  * You should have received a copy of the GNU Library General Public
  *  License along with this library; if not, write to the
- *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- *  Boston, MA  02111-1307, USA.
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  * Or go to http://www.gnu.org/copyleft/lgpl.html
  */
 
@@ -348,6 +348,89 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64
 #endif
 #endif
 
+#ifndef ALC_EXT_DEFAULT_FILTER_ORDER
+#define ALC_EXT_DEFAULT_FILTER_ORDER 1
+#define ALC_DEFAULT_FILTER_ORDER                 0x1100
+#endif
+
+#ifndef AL_SOFT_deferred_updates
+#define AL_SOFT_deferred_updates 1
+#define AL_DEFERRED_UPDATES_SOFT                 0xC002
+typedef ALvoid (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void);
+typedef ALvoid (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void);
+AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void);
+#endif
+#endif
+
+#ifndef AL_SOFT_block_alignment
+#define AL_SOFT_block_alignment 1
+#define AL_UNPACK_BLOCK_ALIGNMENT_SOFT           0x200C
+#define AL_PACK_BLOCK_ALIGNMENT_SOFT             0x200D
+#endif
+
+#ifndef AL_SOFT_MSADPCM
+#define AL_SOFT_MSADPCM 1
+#define AL_FORMAT_MONO_MSADPCM_SOFT              0x1302
+#define AL_FORMAT_STEREO_MSADPCM_SOFT            0x1303
+#endif
+
+#ifndef AL_SOFT_source_length
+#define AL_SOFT_source_length 1
+/*#define AL_BYTE_LENGTH_SOFT                      0x2009*/
+/*#define AL_SAMPLE_LENGTH_SOFT                    0x200A*/
+/*#define AL_SEC_LENGTH_SOFT                       0x200B*/
+#endif
+
+#ifndef ALC_SOFT_pause_device
+#define ALC_SOFT_pause_device 1
+typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device);
+typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
+#endif
+#endif
+
+#ifndef AL_EXT_BFORMAT
+#define AL_EXT_BFORMAT 1
+#define AL_FORMAT_BFORMAT2D_8                    0x20021
+#define AL_FORMAT_BFORMAT2D_16                   0x20022
+#define AL_FORMAT_BFORMAT2D_FLOAT32              0x20023
+#define AL_FORMAT_BFORMAT3D_8                    0x20031
+#define AL_FORMAT_BFORMAT3D_16                   0x20032
+#define AL_FORMAT_BFORMAT3D_FLOAT32              0x20033
+#endif
+
+#ifndef AL_EXT_MULAW_BFORMAT
+#define AL_EXT_MULAW_BFORMAT 1
+#define AL_FORMAT_BFORMAT2D_MULAW                0x10031
+#define AL_FORMAT_BFORMAT3D_MULAW                0x10032
+#endif
+
+#ifndef ALC_SOFT_HRTF
+#define ALC_SOFT_HRTF 1
+#define ALC_HRTF_SOFT                            0x1992
+#define ALC_DONT_CARE_SOFT                       0x0002
+#define ALC_HRTF_STATUS_SOFT                     0x1993
+#define ALC_HRTF_DISABLED_SOFT                   0x0000
+#define ALC_HRTF_ENABLED_SOFT                    0x0001
+#define ALC_HRTF_DENIED_SOFT                     0x0002
+#define ALC_HRTF_REQUIRED_SOFT                   0x0003
+#define ALC_HRTF_HEADPHONES_DETECTED_SOFT        0x0004
+#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT         0x0005
+#define ALC_NUM_HRTF_SPECIFIERS_SOFT             0x1994
+#define ALC_HRTF_SPECIFIER_SOFT                  0x1995
+#define ALC_HRTF_ID_SOFT                         0x1996
+typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
+typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
+ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 1
external/openal_soft/include/AL/efx-presets.h

@@ -345,7 +345,7 @@ typedef struct {
 /* Driving Presets */
 
 #define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \
-    { 1.0000f, 0.0000f, 3.1623f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+    { 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
 
 #define EFX_REVERB_PRESET_DRIVING_PITGARAGE \
     { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }

BIN
external/openal_soft/lib/win32/libopenal32.a → external/openal_soft/lib/win32/libOpenAL32.dll.a


BIN
external/openal_soft/openal32.dll


+ 166 - 107
src/audio.c

@@ -56,6 +56,9 @@
 #define JAR_XM_IMPLEMENTATION
 #include "jar_xm.h"         // XM loading functions
 
+#define JAR_MOD_IMPLEMENTATION
+#include "jar_mod.h"        // For playing .mod files
+
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
@@ -95,10 +98,11 @@ typedef struct MixChannel_t {
 // NOTE: Anything longer than ~10 seconds should be streamed into a mix channel...
 typedef struct Music {
     stb_vorbis *stream;
-    jar_xm_context_t *chipctx; // Stores jar_xm mixc
+    jar_xm_context_t *xmctx;   // Stores jar_xm mixc, XM chiptune context
+    jar_mod_context_t modctx;  // Stores mod chiptune context
     MixChannel_t *mixc;        // mix channel
     
-    int totalSamplesLeft;
+    unsigned int totalSamplesLeft;
     float totalLengthSeconds;
     bool loop;
     bool chipTune;             // True if chiptune is loaded
@@ -111,9 +115,9 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-static MixChannel_t* mixChannelsActive_g[MAX_MIX_CHANNELS];        // What mix channels are currently active
+static MixChannel_t* mixChannels_g[MAX_MIX_CHANNELS];        // What mix channels are currently active
 static bool musicEnabled_g = false;
-static Music currentMusic[MAX_MUSIC_STREAMS];                      // Current music loaded, up to two can play at the same time
+static Music musicChannels_g[MAX_MUSIC_STREAMS];             // Current music loaded, up to two can play at the same time
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -175,7 +179,7 @@ void CloseAudioDevice(void)
 {
     for(int index=0; index<MAX_MUSIC_STREAMS; index++)
     {
-        if(currentMusic[index].mixc) StopMusicStream(index);      // Stop music streaming and close current stream
+        if(musicChannels_g[index].mixc) StopMusicStream(index);      // Stop music streaming and close current stream
     }
     
 
@@ -215,13 +219,13 @@ static MixChannel_t* InitMixChannel(unsigned short sampleRate, unsigned char mix
     if(mixChannel >= MAX_MIX_CHANNELS) return NULL;
     if(!IsAudioDeviceReady()) InitAudioDevice();
     
-    if(!mixChannelsActive_g[mixChannel]){
+    if(!mixChannels_g[mixChannel]){
         MixChannel_t *mixc = (MixChannel_t*)malloc(sizeof(MixChannel_t));
         mixc->sampleRate = sampleRate;
         mixc->channels = channels;
         mixc->mixChannel = mixChannel;
         mixc->floatingPoint = floatingPoint;
-        mixChannelsActive_g[mixChannel] = mixc;
+        mixChannels_g[mixChannel] = mixc;
         
         // setup openAL format
         if(channels == 1)
@@ -283,7 +287,7 @@ static void CloseMixChannel(MixChannel_t* mixc)
         //delete source and buffers
         alDeleteSources(1, &mixc->alSource);
         alDeleteBuffers(MAX_STREAM_BUFFERS, mixc->alBuffer);
-        mixChannelsActive_g[mixc->mixChannel] = NULL;
+        mixChannels_g[mixc->mixChannel] = NULL;
         free(mixc);
         mixc = NULL;
     }
@@ -294,7 +298,7 @@ static void CloseMixChannel(MixChannel_t* mixc)
 // @Returns number of samples that where processed.
 static int BufferMixChannel(MixChannel_t* mixc, void *data, int numberElements)
 {
-    if(!mixc || mixChannelsActive_g[mixc->mixChannel] != mixc) return 0; // when there is two channels there must be an even number of samples
+    if(!mixc || mixChannels_g[mixc->mixChannel] != mixc) return 0; // when there is two channels there must be an even number of samples
     
     if (!data || !numberElements)
     { // pauses audio until data is given
@@ -376,35 +380,38 @@ static void ResampleByteToFloat(char *chars, float *floats, unsigned short len)
     }
 }
 
-// used to output raw audio streams, returns negative numbers on error
+// used to output raw audio streams, returns negative numbers on error, + number represents the mix channel index
 // if floating point is false the data size is 16bit short, otherwise it is float 32bit
 RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint)
 {
     int mixIndex;
     for(mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot
     {
-        if(mixChannelsActive_g[mixIndex] == NULL) break;
-        else if(mixIndex == MAX_MIX_CHANNELS - 1) return -1; // error
+        if(mixChannels_g[mixIndex] == NULL) break;
+        else if(mixIndex == MAX_MIX_CHANNELS - 1) return ERROR_OUT_OF_MIX_CHANNELS; // error
     }
     
     if(InitMixChannel(sampleRate, mixIndex, channels, floatingPoint))
         return mixIndex;
     else
-        return -2; // error
+        return ERROR_RAW_CONTEXT_CREATION; // error
 }
 
 void CloseRawAudioContext(RawAudioContext ctx)
 {
-    if(mixChannelsActive_g[ctx])
-        CloseMixChannel(mixChannelsActive_g[ctx]);
+    if(mixChannels_g[ctx])
+        CloseMixChannel(mixChannels_g[ctx]);
 }
 
-int BufferRawAudioContext(RawAudioContext ctx, void *data, int numberElements)
+// 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.
+int BufferRawAudioContext(RawAudioContext ctx, void *data, unsigned short numberElements)
 {
     int numBuffered = 0;
     if(ctx >= 0)
     {
-        MixChannel_t* mixc = mixChannelsActive_g[ctx];
+        MixChannel_t* mixc = mixChannels_g[ctx];
         numBuffered = BufferMixChannel(mixc, data, numberElements);
     }
     return numBuffered;
@@ -431,7 +438,10 @@ Sound LoadSound(char *fileName)
 
     if (strcmp(GetExtension(fileName),"wav") == 0) wave = LoadWAV(fileName);
     else if (strcmp(GetExtension(fileName),"ogg") == 0) wave = LoadOGG(fileName);
-    else TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName);
+    else{
+        TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName);
+        sound.error = ERROR_EXTENSION_NOT_RECOGNIZED; //error
+    }
 
     if (wave.data != NULL)
     {
@@ -558,6 +568,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
     if (rresFile == NULL)
     {
         TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
+        sound.error = ERROR_UNABLE_TO_OPEN_RRES_FILE; //error
     }
     else
     {
@@ -572,6 +583,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
         if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
         {
             TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName);
+            sound.error = ERROR_INVALID_RRES_FILE;
         }
         else
         {
@@ -662,6 +674,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
                     else
                     {
                         TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName);
+                        sound.error = ERROR_INVALID_RRES_RESOURCE;
                     }
                 }
                 else
@@ -767,105 +780,134 @@ int PlayMusicStream(int musicIndex, char *fileName)
 {
     int mixIndex;
     
-    if(currentMusic[musicIndex].stream || currentMusic[musicIndex].chipctx) return 1; // error
+    if(musicChannels_g[musicIndex].stream || musicChannels_g[musicIndex].xmctx) return ERROR_UNINITIALIZED_CHANNELS; // error
     
     for(mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot
     {
-        if(mixChannelsActive_g[mixIndex] == NULL) break;
-        else if(mixIndex == MAX_MIX_CHANNELS - 1) return 2; // error
+        if(mixChannels_g[mixIndex] == NULL) break;
+        else if(mixIndex == MAX_MIX_CHANNELS - 1) return ERROR_OUT_OF_MIX_CHANNELS; // error
     }
     
     if (strcmp(GetExtension(fileName),"ogg") == 0)
     {
         // Open audio stream
-        currentMusic[musicIndex].stream = stb_vorbis_open_filename(fileName, NULL, NULL);
+        musicChannels_g[musicIndex].stream = stb_vorbis_open_filename(fileName, NULL, NULL);
 
-        if (currentMusic[musicIndex].stream == NULL)
+        if (musicChannels_g[musicIndex].stream == NULL)
         {
             TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName);
-            return 3; // error
+            return ERROR_LOADING_OGG; // error
         }
         else
         {
             // Get file info
-            stb_vorbis_info info = stb_vorbis_get_info(currentMusic[musicIndex].stream);
+            stb_vorbis_info info = stb_vorbis_get_info(musicChannels_g[musicIndex].stream);
 
             TraceLog(INFO, "[%s] Ogg sample rate: %i", fileName, info.sample_rate);
             TraceLog(INFO, "[%s] Ogg channels: %i", fileName, info.channels);
             TraceLog(DEBUG, "[%s] Temp memory required: %i", fileName, info.temp_memory_required);
 
-            currentMusic[musicIndex].loop = true;                  // We loop by default
+            musicChannels_g[musicIndex].loop = true;                  // We loop by default
             musicEnabled_g = true;
             
 
-            currentMusic[musicIndex].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[musicIndex].stream) * info.channels;
-            currentMusic[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[musicIndex].stream);
+            musicChannels_g[musicIndex].totalSamplesLeft = (unsigned int)stb_vorbis_stream_length_in_samples(musicChannels_g[musicIndex].stream) * info.channels;
+            musicChannels_g[musicIndex].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(musicChannels_g[musicIndex].stream);
             
             if (info.channels == 2){
-                currentMusic[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 2, false);
-                currentMusic[musicIndex].mixc->playing = true;
+                musicChannels_g[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 2, false);
+                musicChannels_g[musicIndex].mixc->playing = true;
             }
             else{
-                currentMusic[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 1, false);
-                currentMusic[musicIndex].mixc->playing = true;
+                musicChannels_g[musicIndex].mixc = InitMixChannel(info.sample_rate, mixIndex, 1, false);
+                musicChannels_g[musicIndex].mixc->playing = true;
             }
-            if(!currentMusic[musicIndex].mixc) return 4; // error
+            if(!musicChannels_g[musicIndex].mixc) return ERROR_LOADING_OGG; // error
         }
     }
     else if (strcmp(GetExtension(fileName),"xm") == 0)
     {
         // only stereo is supported for xm
-        if(!jar_xm_create_context_from_file(&currentMusic[musicIndex].chipctx, 48000, fileName))
+        if(!jar_xm_create_context_from_file(&musicChannels_g[musicIndex].xmctx, 48000, fileName))
         {
-            currentMusic[musicIndex].chipTune = true;
-            currentMusic[musicIndex].loop = true;
-            jar_xm_set_max_loop_count(currentMusic[musicIndex].chipctx, 0); // infinite number of loops
-            currentMusic[musicIndex].totalSamplesLeft =  jar_xm_get_remaining_samples(currentMusic[musicIndex].chipctx);
-            currentMusic[musicIndex].totalLengthSeconds = ((float)currentMusic[musicIndex].totalSamplesLeft) / 48000.f;
+            musicChannels_g[musicIndex].chipTune = true;
+            musicChannels_g[musicIndex].loop = true;
+            jar_xm_set_max_loop_count(musicChannels_g[musicIndex].xmctx, 0); // infinite number of loops
+            musicChannels_g[musicIndex].totalSamplesLeft =  (unsigned int)jar_xm_get_remaining_samples(musicChannels_g[musicIndex].xmctx);
+            musicChannels_g[musicIndex].totalLengthSeconds = ((float)musicChannels_g[musicIndex].totalSamplesLeft) / 48000.f;
             musicEnabled_g = true;
             
-            TraceLog(INFO, "[%s] XM number of samples: %i", fileName, currentMusic[musicIndex].totalSamplesLeft);
-            TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, currentMusic[musicIndex].totalLengthSeconds);
+            TraceLog(INFO, "[%s] XM number of samples: %i", fileName, musicChannels_g[musicIndex].totalSamplesLeft);
+            TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, musicChannels_g[musicIndex].totalLengthSeconds);
             
-            currentMusic[musicIndex].mixc = InitMixChannel(48000, mixIndex, 2, false);
-            if(!currentMusic[musicIndex].mixc) return 5; // error
-            currentMusic[musicIndex].mixc->playing = true;
+            musicChannels_g[musicIndex].mixc = InitMixChannel(48000, mixIndex, 2, true);
+            if(!musicChannels_g[musicIndex].mixc) return ERROR_XM_CONTEXT_CREATION; // error
+            musicChannels_g[musicIndex].mixc->playing = true;
         }
         else
         {
             TraceLog(WARNING, "[%s] XM file could not be opened", fileName);
-            return 6; // error
+            return ERROR_LOADING_XM; // error
+        }
+    }
+    else if (strcmp(GetExtension(fileName),"mod") == 0)
+    {
+        jar_mod_init(&musicChannels_g[musicIndex].modctx);
+        if(jar_mod_load_file(&musicChannels_g[musicIndex].modctx, fileName))
+        {
+            musicChannels_g[musicIndex].chipTune = true;
+            musicChannels_g[musicIndex].loop = true;
+            musicChannels_g[musicIndex].totalSamplesLeft = (unsigned int)jar_mod_max_samples(&musicChannels_g[musicIndex].modctx);
+            musicChannels_g[musicIndex].totalLengthSeconds = ((float)musicChannels_g[musicIndex].totalSamplesLeft) / 48000.f;
+            musicEnabled_g = true;
+            
+            TraceLog(INFO, "[%s] MOD number of samples: %i", fileName, musicChannels_g[musicIndex].totalSamplesLeft);
+            TraceLog(INFO, "[%s] MOD track length: %11.6f sec", fileName, musicChannels_g[musicIndex].totalLengthSeconds);
+            
+            musicChannels_g[musicIndex].mixc = InitMixChannel(48000, mixIndex, 2, false);
+            if(!musicChannels_g[musicIndex].mixc) return ERROR_MOD_CONTEXT_CREATION; // error
+            musicChannels_g[musicIndex].mixc->playing = true;
+        }
+        else
+        {
+            TraceLog(WARNING, "[%s] MOD file could not be opened", fileName);
+            return ERROR_LOADING_MOD; // error
         }
     }
     else
     {
         TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
-        return 7; // error
+        return ERROR_EXTENSION_NOT_RECOGNIZED; // error
     }
     return 0; // normal return
 }
 
-// Stop music playing for individual music index of currentMusic array (close stream)
+// Stop music playing for individual music index of musicChannels_g array (close stream)
 void StopMusicStream(int index)
 {
-    if (index < MAX_MUSIC_STREAMS && currentMusic[index].mixc)
+    if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
     {
-        CloseMixChannel(currentMusic[index].mixc);
+        CloseMixChannel(musicChannels_g[index].mixc);
         
-        if (currentMusic[index].chipTune)
+        if (musicChannels_g[index].chipTune && musicChannels_g[index].xmctx)
         {
-            jar_xm_free_context(currentMusic[index].chipctx);
+            jar_xm_free_context(musicChannels_g[index].xmctx);
+            musicChannels_g[index].xmctx = 0;
+        }
+        else if(musicChannels_g[index].chipTune && musicChannels_g[index].modctx.mod_loaded)
+        {
+            jar_mod_unload(&musicChannels_g[index].modctx);
         }
         else
         {
-            stb_vorbis_close(currentMusic[index].stream);
+            stb_vorbis_close(musicChannels_g[index].stream);
         }
         
         if(!getMusicStreamCount()) musicEnabled_g = false;
-        if(currentMusic[index].stream || currentMusic[index].chipctx)
+        if(musicChannels_g[index].stream || musicChannels_g[index].xmctx)
         {
-            currentMusic[index].stream = NULL;
-            currentMusic[index].chipctx = NULL;
+            musicChannels_g[index].stream = NULL;
+            musicChannels_g[index].xmctx = NULL;
         }
     }
 }
@@ -875,7 +917,7 @@ int getMusicStreamCount(void)
 {
     int musicCount = 0;
     for(int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) // find empty music slot
-        if(currentMusic[musicIndex].stream != NULL || currentMusic[musicIndex].chipTune) musicCount++;
+        if(musicChannels_g[musicIndex].stream != NULL || musicChannels_g[musicIndex].chipTune) musicCount++;
     
     return musicCount;
 }
@@ -884,11 +926,11 @@ int getMusicStreamCount(void)
 void PauseMusicStream(int index)
 {
     // Pause music stream if music available!
-    if (index < MAX_MUSIC_STREAMS && currentMusic[index].mixc && musicEnabled_g)
+    if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc && musicEnabled_g)
     {
         TraceLog(INFO, "Pausing music stream");
-        alSourcePause(currentMusic[index].mixc->alSource);
-        currentMusic[index].mixc->playing = false;
+        alSourcePause(musicChannels_g[index].mixc->alSource);
+        musicChannels_g[index].mixc->playing = false;
     }
 }
 
@@ -897,13 +939,13 @@ void ResumeMusicStream(int index)
 {
     // Resume music playing... if music available!
     ALenum state;
-    if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){
-        alGetSourcei(currentMusic[index].mixc->alSource, AL_SOURCE_STATE, &state);
+    if(index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc){
+        alGetSourcei(musicChannels_g[index].mixc->alSource, AL_SOURCE_STATE, &state);
         if (state == AL_PAUSED)
         {
             TraceLog(INFO, "Resuming music stream");
-            alSourcePlay(currentMusic[index].mixc->alSource);
-            currentMusic[index].mixc->playing = true;
+            alSourcePlay(musicChannels_g[index].mixc->alSource);
+            musicChannels_g[index].mixc->playing = true;
         }
     }
 }
@@ -914,8 +956,8 @@ bool IsMusicPlaying(int index)
     bool playing = false;
     ALint state;
     
-    if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){
-        alGetSourcei(currentMusic[index].mixc->alSource, AL_SOURCE_STATE, &state);
+    if(index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc){
+        alGetSourcei(musicChannels_g[index].mixc->alSource, AL_SOURCE_STATE, &state);
         if (state == AL_PLAYING) playing = true;
     }
 
@@ -925,29 +967,29 @@ bool IsMusicPlaying(int index)
 // Set volume for music
 void SetMusicVolume(int index, float volume)
 {
-    if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){
-        alSourcef(currentMusic[index].mixc->alSource, AL_GAIN, volume);
+    if(index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc){
+        alSourcef(musicChannels_g[index].mixc->alSource, AL_GAIN, volume);
     }
 }
 
 void SetMusicPitch(int index, float pitch)
 {
-    if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc){
-        alSourcef(currentMusic[index].mixc->alSource, AL_PITCH, pitch);
+    if(index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc){
+        alSourcef(musicChannels_g[index].mixc->alSource, AL_PITCH, pitch);
     }
 }
 
-// Get current music time length (in seconds)
+// Get music time length (in seconds)
 float GetMusicTimeLength(int index)
 {
     float totalSeconds;
-    if (currentMusic[index].chipTune)
+    if (musicChannels_g[index].chipTune)
     {
-        totalSeconds = currentMusic[index].totalLengthSeconds;
+        totalSeconds = (float)musicChannels_g[index].totalLengthSeconds;
     }
     else
     {
-        totalSeconds = stb_vorbis_stream_length_in_seconds(currentMusic[index].stream);
+        totalSeconds = stb_vorbis_stream_length_in_seconds(musicChannels_g[index].stream);
     }
 
     return totalSeconds;
@@ -957,19 +999,24 @@ float GetMusicTimeLength(int index)
 float GetMusicTimePlayed(int index)
 {
     float secondsPlayed = 0.0f;
-    if(index < MAX_MUSIC_STREAMS && currentMusic[index].mixc)
+    if(index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
     {
-        if (currentMusic[index].chipTune)
+        if (musicChannels_g[index].chipTune && musicChannels_g[index].xmctx)
         {
             uint64_t samples;
-            jar_xm_get_position(currentMusic[index].chipctx, NULL, NULL, NULL, &samples);
-            secondsPlayed = (float)samples / (48000 * currentMusic[index].mixc->channels); // Not sure if this is the correct value
+            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
+        }
+        else if(musicChannels_g[index].chipTune && musicChannels_g[index].modctx.mod_loaded)
+        {
+            long numsamp = jar_mod_current_samples(&musicChannels_g[index].modctx);
+            secondsPlayed = (float)numsamp / (48000.f);
         }
         else
         {
-            int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].mixc->channels;
-            int samplesPlayed = totalSamples - currentMusic[index].totalSamplesLeft;
-            secondsPlayed = (float)samplesPlayed / (currentMusic[index].mixc->sampleRate * currentMusic[index].mixc->channels);
+            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);
         }
     }
 
@@ -989,19 +1036,30 @@ static bool BufferMusicStream(int index, int numBuffers)
     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)
     
-    if (currentMusic[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes.
+    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.
     {
-        if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT)
-            size = MUSIC_BUFFER_SIZE_SHORT / 2;
-        else
-            size = currentMusic[index].totalSamplesLeft / 2;
-        
         for(int x=0; x<numBuffers; x++)
         {
-            jar_xm_generate_samples_16bit(currentMusic[index].chipctx, pcm, size); // reads 2*readlen shorts and moves them to buffer+size memory location
-            BufferMixChannel(currentMusic[index].mixc, pcm, size * 2);
-            currentMusic[index].totalSamplesLeft -= size * 2;
-            if(currentMusic[index].totalSamplesLeft <= 0)
+            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;
+                jar_mod_fillbuffer(&musicChannels_g[index].modctx, pcm, size, 0 );
+                BufferMixChannel(musicChannels_g[index].mixc, pcm, size * 2);
+            }
+            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;
+                jar_xm_generate_samples(musicChannels_g[index].xmctx, pcmf, size); // reads 2*readlen shorts and moves them to buffer+size memory location
+                BufferMixChannel(musicChannels_g[index].mixc, pcmf, size * 2);
+            }
+            
+
+            musicChannels_g[index].totalSamplesLeft -= size;
+            if(musicChannels_g[index].totalSamplesLeft <= 0)
             {
                 active = false;
                 break;
@@ -1010,17 +1068,17 @@ static bool BufferMusicStream(int index, int numBuffers)
     }
     else
     {
-        if(currentMusic[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT)
+        if(musicChannels_g[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT)
             size = MUSIC_BUFFER_SIZE_SHORT;
         else
-            size = currentMusic[index].totalSamplesLeft;
+            size = musicChannels_g[index].totalSamplesLeft;
         
         for(int x=0; x<numBuffers; x++)
         {
-            int streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic[index].stream, currentMusic[index].mixc->channels, pcm, size);
-            BufferMixChannel(currentMusic[index].mixc, pcm, streamedBytes * currentMusic[index].mixc->channels);
-            currentMusic[index].totalSamplesLeft -= streamedBytes * currentMusic[index].mixc->channels;
-            if(currentMusic[index].totalSamplesLeft <= 0)
+            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;
+            if(musicChannels_g[index].totalSamplesLeft <= 0)
             {
                 active = false;
                 break;
@@ -1037,11 +1095,11 @@ static void EmptyMusicStream(int index)
     ALuint buffer = 0;
     int queued = 0;
 
-    alGetSourcei(currentMusic[index].mixc->alSource, AL_BUFFERS_QUEUED, &queued);
+    alGetSourcei(musicChannels_g[index].mixc->alSource, AL_BUFFERS_QUEUED, &queued);
 
     while (queued > 0)
     {
-        alSourceUnqueueBuffers(currentMusic[index].mixc->alSource, 1, &buffer);
+        alSourceUnqueueBuffers(musicChannels_g[index].mixc->alSource, 1, &buffer);
 
         queued--;
     }
@@ -1051,7 +1109,7 @@ static void EmptyMusicStream(int index)
 static int IsMusicStreamReadyForBuffering(int index)
 {
     ALint processed = 0;
-    alGetSourcei(currentMusic[index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed);
+    alGetSourcei(musicChannels_g[index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed);
     return processed;
 }
 
@@ -1062,20 +1120,21 @@ void UpdateMusicStream(int index)
     bool active = true;
     int numBuffers = IsMusicStreamReadyForBuffering(index);
     
-    if (currentMusic[index].mixc->playing && index < MAX_MUSIC_STREAMS && musicEnabled_g && currentMusic[index].mixc && numBuffers)
+    if (musicChannels_g[index].mixc->playing && index < MAX_MUSIC_STREAMS && musicEnabled_g && musicChannels_g[index].mixc && numBuffers)
     {
         active = BufferMusicStream(index, numBuffers);
         
-        if (!active && currentMusic[index].loop)
+        if (!active && musicChannels_g[index].loop)
         {
-            if (currentMusic[index].chipTune)
+            if (musicChannels_g[index].chipTune)
             {
-                currentMusic[index].totalSamplesLeft = currentMusic[index].totalLengthSeconds * 48000;
+                if(musicChannels_g[index].modctx.mod_loaded) jar_mod_seek_start(&musicChannels_g[index].modctx);
+                musicChannels_g[index].totalSamplesLeft = musicChannels_g[index].totalLengthSeconds * 48000;
             }
             else
             {
-                stb_vorbis_seek_start(currentMusic[index].stream);
-                currentMusic[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic[index].stream) * currentMusic[index].mixc->channels;
+                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;
             }
             active = true;
         }
@@ -1083,9 +1142,9 @@ void UpdateMusicStream(int index)
 
         if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data...");
         
-        alGetSourcei(currentMusic[index].mixc->alSource, AL_SOURCE_STATE, &state);
+        alGetSourcei(musicChannels_g[index].mixc->alSource, AL_SOURCE_STATE, &state);
 
-        if (state != AL_PLAYING && active) alSourcePlay(currentMusic[index].mixc->alSource);
+        if (state != AL_PLAYING && active) alSourcePlay(musicChannels_g[index].mixc->alSource);
 
         if (!active) StopMusicStream(index);
         

+ 23 - 2
src/audio.h

@@ -41,15 +41,35 @@
 //----------------------------------------------------------------------------------
 #ifndef __cplusplus
 // Boolean type
-    #ifndef true
+    #if !defined(_STDBOOL_H)
         typedef enum { false, true } bool;
+        #define _STDBOOL_H
     #endif
 #endif
 
+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;
+
 // Sound source type
 typedef struct Sound {
     unsigned int source;
     unsigned int buffer;
+    AudioError error; // if there was any error during the creation or use of this Sound
 } Sound;
 
 // Wave type, defines audio wave data
@@ -63,6 +83,7 @@ typedef struct Wave {
 
 typedef int RawAudioContext;
 
+
 #ifdef __cplusplus
 extern "C" {            // Prevents name mangling of functions
 #endif
@@ -107,7 +128,7 @@ void SetMusicPitch(int index, float pitch);
 RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint);
 
 void CloseRawAudioContext(RawAudioContext ctx);
-int BufferRawAudioContext(RawAudioContext ctx, void *data, int numberElements); // returns number of elements buffered
+int BufferRawAudioContext(RawAudioContext ctx, void *data, unsigned short numberElements); // returns number of elements buffered
 
 #ifdef __cplusplus
 }

+ 1587 - 0
src/jar_mod.h

@@ -0,0 +1,1587 @@
+// jar_mod.h - v0.01 - public domain C0 - Joshua Reisenauer
+//
+// HISTORY:
+//
+//   v0.01  2016-03-12  Setup
+//
+//
+// USAGE:
+//
+// In ONE source file, put:
+//
+//    #define JAR_MOD_IMPLEMENTATION
+//    #include "jar_mod.h"
+//
+// Other source files should just include jar_mod.h
+//
+// SAMPLE CODE:
+// jar_mod_context_t modctx;
+// short samplebuff[4096];
+// bool bufferFull = false;
+// int intro_load(void)
+// {
+//     jar_mod_init(&modctx);
+//     jar_mod_load_file(&modctx, "file.mod");
+//     return 1;
+// }
+// int intro_unload(void)
+// {
+//     jar_mod_unload(&modctx);
+//     return 1;
+// }
+// int intro_tick(long counter)
+// {
+//     if(!bufferFull)
+//     {
+//         jar_mod_fillbuffer(&modctx, samplebuff, 4096, 0);
+//         bufferFull=true;
+//     }
+//     if(IsKeyDown(KEY_ENTER))
+//         return 1;
+//     return 0;
+// }
+//
+//
+// LISCENSE:
+//
+// Written by: Jean-François DEL NERO (http://hxc2001.com/) <Email : jeanfrancoisdelnero <> free.fr>
+// Adapted to jar_mod by: Joshua Adam Reisenauer <[email protected]>
+// This program is free software. It comes without any warranty, to the
+// extent permitted by applicable law. You can redistribute it and/or
+// modify it under the terms of the Do What The Fuck You Want To Public
+// License, Version 2, as published by Sam Hocevar. See
+// http://sam.zoy.org/wtfpl/COPYING for more details.
+///////////////////////////////////////////////////////////////////////////////////
+// HxCMOD Core API:
+// -------------------------------------------
+// int  jar_mod_init(jar_mod_context_t * modctx)
+//
+// - Initialize the jar_mod_context_t buffer. Must be called before doing anything else.
+//   Return 1 if success. 0 in case of error.
+// -------------------------------------------
+// mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename)
+//
+// - "Load" a MOD from file, context must already be initialized.
+//   Return size of file in bytes.
+// -------------------------------------------
+// void jar_mod_fillbuffer( jar_mod_context_t * modctx, unsigned short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf )
+//
+// - Generate and return the next samples chunk to outbuffer.
+//   nbsample specify the number of stereo 16bits samples you want.
+//   The output format is by default signed 48000Hz 16-bit Stereo PCM samples, otherwise it is changed with jar_mod_setcfg().
+//   The output buffer size in bytes must be equal to ( nbsample * 2 * channels ).
+//   The optional trkbuf parameter can be used to get detailed status of the player. Put NULL/0 is unused.
+// -------------------------------------------
+// void jar_mod_unload( jar_mod_context_t * modctx )
+// - "Unload" / clear the player status.
+// -------------------------------------------
+///////////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef INCLUDE_JAR_MOD_H
+#define INCLUDE_JAR_MOD_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+// Basic type
+typedef unsigned char muchar;
+typedef unsigned short muint;
+typedef short mint;
+typedef unsigned long mulong;
+
+#define NUMMAXCHANNELS 32
+#define MAXNOTES 12*12
+#define DEFAULT_SAMPLE_RATE 48000
+//
+// MOD file structures
+//
+
+#pragma pack(1)
+
+typedef struct {
+    muchar  name[22];
+    muint   length;
+    muchar  finetune;
+    muchar  volume;
+    muint   reppnt;
+    muint   replen;
+} sample;
+
+typedef struct {
+    muchar  sampperiod;
+    muchar  period;
+    muchar  sampeffect;
+    muchar  effect;
+} note;
+
+typedef struct {
+    muchar  title[20];
+    sample  samples[31];
+    muchar  length; // length of tablepos
+    muchar  protracker;
+    muchar  patterntable[128];
+    muchar  signature[4];
+    muchar  speed;
+} module;
+
+#pragma pack()
+
+//
+// HxCMod Internal structures
+//
+typedef struct {
+    char*   sampdata;
+    muint   sampnum;
+    muint   length;
+    muint   reppnt;
+    muint   replen;
+    mulong  samppos;
+    muint   period;
+    muchar  volume;
+    mulong  ticks;
+    muchar  effect;
+    muchar  parameffect;
+    muint   effect_code;
+    mint    decalperiod;
+    mint    portaspeed;
+    mint    portaperiod;
+    mint    vibraperiod;
+    mint    Arpperiods[3];
+    muchar  ArpIndex;
+    mint    oldk;
+    muchar  volumeslide;
+    muchar  vibraparam;
+    muchar  vibrapointeur;
+    muchar  finetune;
+    muchar  cut_param;
+    muint   patternloopcnt;
+    muint   patternloopstartpoint;
+} channel;
+
+typedef struct {
+    module  song;
+    char*   sampledata[31];
+    note*   patterndata[128];
+
+    mulong  playrate;
+    muint   tablepos;
+    muint   patternpos;
+    muint   patterndelay;
+    muint   jump_loop_effect;
+    muchar  bpm;
+    mulong  patternticks;
+    mulong  patterntickse;
+    mulong  patternticksaim;
+    mulong  sampleticksconst;
+    mulong  samplenb;
+    channel channels[NUMMAXCHANNELS];
+    muint   number_of_channels;
+    muint   fullperiod[MAXNOTES * 8];
+    muint   mod_loaded;
+    mint    last_r_sample;
+    mint    last_l_sample;
+    mint    stereo;
+    mint    stereo_separation;
+    mint    bits;
+    mint    filter;
+    
+    muchar *modfile; // the raw mod file
+    mulong  modfilesize;
+    muint   loopcount;
+} jar_mod_context_t;
+
+//
+// Player states structures
+//
+typedef struct track_state_
+{
+    unsigned char instrument_number;
+    unsigned short cur_period;
+    unsigned char  cur_volume;
+    unsigned short cur_effect;
+    unsigned short cur_parameffect;
+}track_state;
+
+typedef struct tracker_state_
+{
+    int number_of_tracks;
+    int bpm;
+    int speed;
+    int cur_pattern;
+    int cur_pattern_pos;
+    int cur_pattern_table_pos;
+    unsigned int buf_index;
+    track_state tracks[32];
+}tracker_state;
+
+typedef struct tracker_state_instrument_
+{
+    char name[22];
+    int  active;
+}tracker_state_instrument;
+
+typedef struct jar_mod_tracker_buffer_state_
+{
+    int  nb_max_of_state;
+    int  nb_of_state;
+    int  cur_rd_index;
+    int  sample_step;
+    char name[64];
+    tracker_state_instrument instruments[31];
+    tracker_state * track_state_buf;
+}jar_mod_tracker_buffer_state;
+
+
+
+bool   jar_mod_init(jar_mod_context_t * modctx);
+bool   jar_mod_setcfg(jar_mod_context_t * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter);
+void   jar_mod_fillbuffer(jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf);
+void   jar_mod_unload(jar_mod_context_t * modctx);
+mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename);
+mulong jar_mod_current_samples(jar_mod_context_t * modctx);
+mulong jar_mod_max_samples(jar_mod_context_t * modctx);
+void   jar_mod_seek_start(jar_mod_context_t * ctx);
+
+#ifdef __cplusplus
+}
+#endif
+//--------------------------------------------------------------------
+
+
+
+//-------------------------------------------------------------------------------
+#ifdef JAR_MOD_IMPLEMENTATION
+
+// Effects list
+#define EFFECT_ARPEGGIO              0x0 // Supported
+#define EFFECT_PORTAMENTO_UP         0x1 // Supported
+#define EFFECT_PORTAMENTO_DOWN       0x2 // Supported
+#define EFFECT_TONE_PORTAMENTO       0x3 // Supported
+#define EFFECT_VIBRATO               0x4 // Supported
+#define EFFECT_VOLSLIDE_TONEPORTA    0x5 // Supported
+#define EFFECT_VOLSLIDE_VIBRATO      0x6 // Supported
+#define EFFECT_VOLSLIDE_TREMOLO      0x7 // - TO BE DONE -
+#define EFFECT_SET_PANNING           0x8 // - TO BE DONE -
+#define EFFECT_SET_OFFSET            0x9 // Supported
+#define EFFECT_VOLUME_SLIDE          0xA // Supported
+#define EFFECT_JUMP_POSITION         0xB // Supported
+#define EFFECT_SET_VOLUME            0xC // Supported
+#define EFFECT_PATTERN_BREAK         0xD // Supported
+
+#define EFFECT_EXTENDED              0xE
+#define EFFECT_E_FINE_PORTA_UP       0x1 // Supported
+#define EFFECT_E_FINE_PORTA_DOWN     0x2 // Supported
+#define EFFECT_E_GLISSANDO_CTRL      0x3 // - TO BE DONE -
+#define EFFECT_E_VIBRATO_WAVEFORM    0x4 // - TO BE DONE -
+#define EFFECT_E_SET_FINETUNE        0x5 // - TO BE DONE -
+#define EFFECT_E_PATTERN_LOOP        0x6 // Supported
+#define EFFECT_E_TREMOLO_WAVEFORM    0x7 // - TO BE DONE -
+#define EFFECT_E_SET_PANNING_2       0x8 // - TO BE DONE -
+#define EFFECT_E_RETRIGGER_NOTE      0x9 // - TO BE DONE -
+#define EFFECT_E_FINE_VOLSLIDE_UP    0xA // Supported
+#define EFFECT_E_FINE_VOLSLIDE_DOWN  0xB // Supported
+#define EFFECT_E_NOTE_CUT            0xC // Supported
+#define EFFECT_E_NOTE_DELAY          0xD // - TO BE DONE -
+#define EFFECT_E_PATTERN_DELAY       0xE // Supported
+#define EFFECT_E_INVERT_LOOP         0xF // - TO BE DONE -
+#define EFFECT_SET_SPEED             0xF0 // Supported
+#define EFFECT_SET_TEMPO             0xF2 // Supported
+
+#define PERIOD_TABLE_LENGTH  MAXNOTES
+#define FULL_PERIOD_TABLE_LENGTH  ( PERIOD_TABLE_LENGTH * 8 )
+
+static const short periodtable[]=
+{
+    27392, 25856, 24384, 23040, 21696, 20480, 19328, 18240, 17216, 16256, 15360, 14496,
+    13696, 12928, 12192, 11520, 10848, 10240,  9664,  9120,  8606,  8128,  7680,  7248,
+     6848,  6464,  6096,  5760,  5424,  5120,  4832,  4560,  4304,  4064,  3840,  3624,
+     3424,  3232,  3048,  2880,  2712,  2560,  2416,  2280,  2152,  2032,  1920,  1812,
+     1712,  1616,  1524,  1440,  1356,  1280,  1208,  1140,  1076,  1016,   960,   906,
+      856,   808,   762,   720,   678,   640,   604,   570,   538,   508,   480,   453,
+      428,   404,   381,   360,   339,   320,   302,   285,   269,   254,   240,   226,
+      214,   202,   190,   180,   170,   160,   151,   143,   135,   127,   120,   113,
+      107,   101,    95,    90,    85,    80,    75,    71,    67,    63,    60,    56,
+       53,    50,    47,    45,    42,    40,    37,    35,    33,    31,    30,    28,
+       27,    25,    24,    22,    21,    20,    19,    18,    17,    16,    15,    14,
+       13,    13,    12,    11,    11,    10,     9,     9,     8,     8,     7,     7
+};
+
+static const short sintable[]={
+      0,  24,  49,  74,  97, 120, 141,161,
+    180, 197, 212, 224, 235, 244, 250,253,
+    255, 253, 250, 244, 235, 224, 212,197,
+    180, 161, 141, 120,  97,  74,  49, 24
+};
+
+typedef struct modtype_
+{
+    unsigned char signature[5];
+    int numberofchannels;
+}modtype;
+
+modtype modlist[]=
+{
+    { "M!K!",4},
+    { "M.K.",4},
+    { "FLT4",4},
+    { "FLT8",8},
+    { "4CHN",4},
+    { "6CHN",6},
+    { "8CHN",8},
+    { "10CH",10},
+    { "12CH",12},
+    { "14CH",14},
+    { "16CH",16},
+    { "18CH",18},
+    { "20CH",20},
+    { "22CH",22},
+    { "24CH",24},
+    { "26CH",26},
+    { "28CH",28},
+    { "30CH",30},
+    { "32CH",32},
+    { "",0}
+};
+
+///////////////////////////////////////////////////////////////////////////////////
+
+static void memcopy( void * dest, void *source, unsigned long size )
+{
+    unsigned long i;
+    unsigned char * d,*s;
+
+    d=(unsigned char*)dest;
+    s=(unsigned char*)source;
+    for(i=0;i<size;i++)
+    {
+        d[i]=s[i];
+    }
+}
+
+static void memclear( void * dest, unsigned char value, unsigned long size )
+{
+    unsigned long i;
+    unsigned char * d;
+
+    d=(unsigned char*)dest;
+    for(i=0;i<size;i++)
+    {
+        d[i]=value;
+    }
+}
+
+static int memcompare( unsigned char * buf1, unsigned char * buf2, unsigned int size )
+{
+    unsigned int i;
+
+    i = 0;
+
+    while(i<size)
+    {
+        if(buf1[i] != buf2[i])
+        {
+            return 0;
+        }
+        i++;
+    }
+
+    return 1;
+}
+
+static int getnote( jar_mod_context_t * mod, unsigned short period, int finetune )
+{
+    int i;
+
+    for(i = 0; i < FULL_PERIOD_TABLE_LENGTH; i++)
+    {
+        if(period >= mod->fullperiod[i])
+        {
+            return i;
+        }
+    }
+
+    return MAXNOTES;
+}
+
+static void worknote( note * nptr, channel * cptr, char t, jar_mod_context_t * mod )
+{
+    muint sample, period, effect, operiod;
+    muint curnote, arpnote;
+
+    sample = (nptr->sampperiod & 0xF0) | (nptr->sampeffect >> 4);
+    period = ((nptr->sampperiod & 0xF) << 8) | nptr->period;
+    effect = ((nptr->sampeffect & 0xF) << 8) | nptr->effect;
+
+    operiod = cptr->period;
+
+    if ( period || sample )
+    {
+        if( sample && sample < 32 )
+        {
+            cptr->sampnum = sample - 1;
+        }
+
+        if( period || sample )
+        {
+            cptr->sampdata = (char *) mod->sampledata[cptr->sampnum];
+            cptr->length = mod->song.samples[cptr->sampnum].length;
+            cptr->reppnt = mod->song.samples[cptr->sampnum].reppnt;
+            cptr->replen = mod->song.samples[cptr->sampnum].replen;
+
+            cptr->finetune = (mod->song.samples[cptr->sampnum].finetune)&0xF;
+
+            if(effect>>8!=4 && effect>>8!=6)
+            {
+                cptr->vibraperiod=0;
+                cptr->vibrapointeur=0;
+            }
+        }
+
+        if( (sample != 0) && ( (effect>>8) != EFFECT_VOLSLIDE_TONEPORTA ) )
+        {
+            cptr->volume = mod->song.samples[cptr->sampnum].volume;
+            cptr->volumeslide = 0;
+        }
+
+        if( ( (effect>>8) != EFFECT_TONE_PORTAMENTO && (effect>>8)!=EFFECT_VOLSLIDE_TONEPORTA) )
+        {
+            if (period!=0)
+                cptr->samppos = 0;
+        }
+
+        cptr->decalperiod = 0;
+        if( period )
+        {
+            if(cptr->finetune)
+            {
+                if( cptr->finetune <= 7 )
+                {
+                    period = mod->fullperiod[getnote(mod,period,0) + cptr->finetune];
+                }
+                else
+                {
+                    period = mod->fullperiod[getnote(mod,period,0) - (16 - (cptr->finetune)) ];
+                }
+            }
+
+            cptr->period = period;
+        }
+
+    }
+
+    cptr->effect = 0;
+    cptr->parameffect = 0;
+    cptr->effect_code = effect;
+
+    switch (effect >> 8)
+    {
+        case EFFECT_ARPEGGIO:
+            /*
+            [0]: Arpeggio
+            Where [0][x][y] means "play note, note+x semitones, note+y
+            semitones, then return to original note". The fluctuations are
+            carried out evenly spaced in one pattern division. They are usually
+            used to simulate chords, but this doesn't work too well. They are
+            also used to produce heavy vibrato. A major chord is when x=4, y=7.
+            A minor chord is when x=3, y=7.
+            */
+
+            if(effect&0xff)
+            {
+                cptr->effect = EFFECT_ARPEGGIO;
+                cptr->parameffect = effect&0xff;
+
+                cptr->ArpIndex = 0;
+
+                curnote = getnote(mod,cptr->period,cptr->finetune);
+
+                cptr->Arpperiods[0] = cptr->period;
+
+                arpnote = curnote + (((cptr->parameffect>>4)&0xF)*8);
+                if( arpnote >= FULL_PERIOD_TABLE_LENGTH )
+                    arpnote = FULL_PERIOD_TABLE_LENGTH - 1;
+
+                cptr->Arpperiods[1] = mod->fullperiod[arpnote];
+
+                arpnote = curnote + (((cptr->parameffect)&0xF)*8);
+                if( arpnote >= FULL_PERIOD_TABLE_LENGTH )
+                    arpnote = FULL_PERIOD_TABLE_LENGTH - 1;
+
+                cptr->Arpperiods[2] = mod->fullperiod[arpnote];
+            }
+        break;
+
+        case EFFECT_PORTAMENTO_UP:
+            /*
+            [1]: Slide up
+            Where [1][x][y] means "smoothly decrease the period of current
+            sample by x*16+y after each tick in the division". The
+            ticks/division are set with the 'set speed' effect (see below). If
+            the period of the note being played is z, then the final period
+            will be z - (x*16 + y)*(ticks - 1). As the slide rate depends on
+            the speed, changing the speed will change the slide. You cannot
+            slide beyond the note B3 (period 113).
+            */
+
+            cptr->effect = EFFECT_PORTAMENTO_UP;
+            cptr->parameffect = effect&0xff;
+        break;
+
+        case EFFECT_PORTAMENTO_DOWN:
+            /*
+            [2]: Slide down
+            Where [2][x][y] means "smoothly increase the period of current
+            sample by x*16+y after each tick in the division". Similar to [1],
+            but lowers the pitch. You cannot slide beyond the note C1 (period
+            856).
+            */
+
+            cptr->effect = EFFECT_PORTAMENTO_DOWN;
+            cptr->parameffect = effect&0xff;
+        break;
+
+        case EFFECT_TONE_PORTAMENTO:
+            /*
+            [3]: Slide to note
+            Where [3][x][y] means "smoothly change the period of current sample
+            by x*16+y after each tick in the division, never sliding beyond
+            current period". The period-length in this channel's division is a
+            parameter to this effect, and hence is not played. Sliding to a
+            note is similar to effects [1] and [2], but the slide will not go
+            beyond the given period, and the direction is implied by that
+            period. If x and y are both 0, then the old slide will continue.
+            */
+
+            cptr->effect = EFFECT_TONE_PORTAMENTO;
+            if( (effect&0xff) != 0 )
+            {
+                cptr->portaspeed = (short)(effect&0xff);
+            }
+
+            if(period!=0)
+            {
+                cptr->portaperiod = period;
+                cptr->period = operiod;
+            }
+        break;
+
+        case EFFECT_VIBRATO:
+            /*
+            [4]: Vibrato
+            Where [4][x][y] means "oscillate the sample pitch using a
+            particular waveform with amplitude y/16 semitones, such that (x *
+            ticks)/64 cycles occur in the division". The waveform is set using
+            effect [14][4]. By placing vibrato effects on consecutive
+            divisions, the vibrato effect can be maintained. If either x or y
+            are 0, then the old vibrato values will be used.
+            */
+
+            cptr->effect = EFFECT_VIBRATO;
+            if( ( effect & 0x0F ) != 0 ) // Depth continue or change ?
+                cptr->vibraparam = (cptr->vibraparam & 0xF0) | ( effect & 0x0F );
+            if( ( effect & 0xF0 ) != 0 ) // Speed continue or change ?
+                cptr->vibraparam = (cptr->vibraparam & 0x0F) | ( effect & 0xF0 );
+
+        break;
+
+        case EFFECT_VOLSLIDE_TONEPORTA:
+            /*
+            [5]: Continue 'Slide to note', but also do Volume slide
+            Where [5][x][y] means "either slide the volume up x*(ticks - 1) or
+            slide the volume down y*(ticks - 1), at the same time as continuing
+            the last 'Slide to note'". It is illegal for both x and y to be
+            non-zero. You cannot slide outside the volume range 0..64. The
+            period-length in this channel's division is a parameter to this
+            effect, and hence is not played.
+            */
+
+            if( period != 0 )
+            {
+                cptr->portaperiod = period;
+                cptr->period = operiod;
+            }
+
+            cptr->effect = EFFECT_VOLSLIDE_TONEPORTA;
+            if( ( effect & 0xFF ) != 0 )
+                cptr->volumeslide = ( effect & 0xFF );
+
+        break;
+
+        case EFFECT_VOLSLIDE_VIBRATO:
+            /*
+            [6]: Continue 'Vibrato', but also do Volume slide
+            Where [6][x][y] means "either slide the volume up x*(ticks - 1) or
+            slide the volume down y*(ticks - 1), at the same time as continuing
+            the last 'Vibrato'". It is illegal for both x and y to be non-zero.
+            You cannot slide outside the volume range 0..64.
+            */
+
+            cptr->effect = EFFECT_VOLSLIDE_VIBRATO;
+            if( (effect & 0xFF) != 0 )
+                cptr->volumeslide = (effect & 0xFF);
+        break;
+
+        case EFFECT_SET_OFFSET:
+            /*
+            [9]: Set sample offset
+            Where [9][x][y] means "play the sample from offset x*4096 + y*256".
+            The offset is measured in words. If no sample is given, yet one is
+            still playing on this channel, it should be retriggered to the new
+            offset using the current volume.
+            */
+
+            cptr->samppos = ((effect>>4) * 4096) + ((effect&0xF)*256);
+
+        break;
+
+        case EFFECT_VOLUME_SLIDE:
+            /*
+            [10]: Volume slide
+            Where [10][x][y] means "either slide the volume up x*(ticks - 1) or
+            slide the volume down y*(ticks - 1)". If both x and y are non-zero,
+            then the y value is ignored (assumed to be 0). You cannot slide
+            outside the volume range 0..64.
+            */
+
+            cptr->effect = EFFECT_VOLUME_SLIDE;
+            cptr->volumeslide = (effect & 0xFF);
+        break;
+
+        case EFFECT_JUMP_POSITION:
+            /*
+            [11]: Position Jump
+            Where [11][x][y] means "stop the pattern after this division, and
+            continue the song at song-position x*16+y". This shifts the
+            'pattern-cursor' in the pattern table (see above). Legal values for
+            x*16+y are from 0 to 127.
+            */
+
+            mod->tablepos = (effect & 0xFF);
+            if(mod->tablepos >= mod->song.length)
+            {
+                mod->tablepos = 0;
+            }
+            mod->patternpos = 0;
+            mod->jump_loop_effect = 1;
+
+        break;
+
+        case EFFECT_SET_VOLUME:
+            /*
+            [12]: Set volume
+            Where [12][x][y] means "set current sample's volume to x*16+y".
+            Legal volumes are 0..64.
+            */
+
+            cptr->volume = (effect & 0xFF);
+        break;
+
+        case EFFECT_PATTERN_BREAK:
+            /*
+            [13]: Pattern Break
+            Where [13][x][y] means "stop the pattern after this division, and
+            continue the song at the next pattern at division x*10+y" (the 10
+            is not a typo). Legal divisions are from 0 to 63 (note Protracker
+            exception above).
+            */
+
+            mod->patternpos = ( ((effect>>4)&0xF)*10 + (effect&0xF) ) * mod->number_of_channels;
+            mod->jump_loop_effect = 1;
+            mod->tablepos++;
+            if(mod->tablepos >= mod->song.length)
+            {
+                mod->tablepos = 0;
+            }
+
+        break;
+
+        case EFFECT_EXTENDED:
+            switch( (effect>>4) & 0xF )
+            {
+                case EFFECT_E_FINE_PORTA_UP:
+                    /*
+                    [14][1]: Fineslide up
+                    Where [14][1][x] means "decrement the period of the current sample
+                    by x". The incrementing takes place at the beginning of the
+                    division, and hence there is no actual sliding. You cannot slide
+                    beyond the note B3 (period 113).
+                    */
+
+                    cptr->period -= (effect & 0xF);
+                    if( cptr->period < 113 )
+                        cptr->period = 113;
+                break;
+
+                case EFFECT_E_FINE_PORTA_DOWN:
+                    /*
+                    [14][2]: Fineslide down
+                    Where [14][2][x] means "increment the period of the current sample
+                    by x". Similar to [14][1] but shifts the pitch down. You cannot
+                    slide beyond the note C1 (period 856).
+                    */
+
+                    cptr->period += (effect & 0xF);
+                    if( cptr->period > 856 )
+                        cptr->period = 856;
+                break;
+
+                case EFFECT_E_FINE_VOLSLIDE_UP:
+                    /*
+                    [14][10]: Fine volume slide up
+                    Where [14][10][x] means "increment the volume of the current sample
+                    by x". The incrementing takes place at the beginning of the
+                    division, and hence there is no sliding. You cannot slide beyond
+                    volume 64.
+                    */
+
+                    cptr->volume += (effect & 0xF);
+                    if( cptr->volume>64 )
+                        cptr->volume = 64;
+                break;
+
+                case EFFECT_E_FINE_VOLSLIDE_DOWN:
+                    /*
+                    [14][11]: Fine volume slide down
+                    Where [14][11][x] means "decrement the volume of the current sample
+                    by x". Similar to [14][10] but lowers volume. You cannot slide
+                    beyond volume 0.
+                    */
+
+                    cptr->volume -= (effect & 0xF);
+                    if( cptr->volume > 200 )
+                        cptr->volume = 0;
+                break;
+
+                case EFFECT_E_PATTERN_LOOP:
+                    /*
+                    [14][6]: Loop pattern
+                    Where [14][6][x] means "set the start of a loop to this division if
+                    x is 0, otherwise after this division, jump back to the start of a
+                    loop and play it another x times before continuing". If the start
+                    of the loop was not set, it will default to the start of the
+                    current pattern. Hence 'loop pattern' cannot be performed across
+                    multiple patterns. Note that loops do not support nesting, and you
+                    may generate an infinite loop if you try to nest 'loop pattern's.
+                    */
+
+                    if( effect & 0xF )
+                    {
+                        if( cptr->patternloopcnt )
+                        {
+                            cptr->patternloopcnt--;
+                            if( cptr->patternloopcnt )
+                            {
+                                mod->patternpos = cptr->patternloopstartpoint;
+                                mod->jump_loop_effect = 1;
+                            }
+                            else
+                            {
+                                cptr->patternloopstartpoint = mod->patternpos ;
+                            }
+                        }
+                        else
+                        {
+                            cptr->patternloopcnt = (effect & 0xF);
+                            mod->patternpos = cptr->patternloopstartpoint;
+                            mod->jump_loop_effect = 1;
+                        }
+                    }
+                    else // Start point
+                    {
+                        cptr->patternloopstartpoint = mod->patternpos;
+                    }
+
+                break;
+
+                case EFFECT_E_PATTERN_DELAY:
+                    /*
+                    [14][14]: Delay pattern
+                    Where [14][14][x] means "after this division there will be a delay
+                    equivalent to the time taken to play x divisions after which the
+                    pattern will be resumed". The delay only relates to the
+                    interpreting of new divisions, and all effects and previous notes
+                    continue during delay.
+                    */
+
+                    mod->patterndelay = (effect & 0xF);
+                break;
+
+                case EFFECT_E_NOTE_CUT:
+                    /*
+                    [14][12]: Cut sample
+                    Where [14][12][x] means "after the current sample has been played
+                    for x ticks in this division, its volume will be set to 0". This
+                    implies that if x is 0, then you will not hear any of the sample.
+                    If you wish to insert "silence" in a pattern, it is better to use a
+                    "silence"-sample (see above) due to the lack of proper support for
+                    this effect.
+                    */
+                    cptr->effect = EFFECT_E_NOTE_CUT;
+                    cptr->cut_param = (effect & 0xF);
+                    if(!cptr->cut_param)
+                        cptr->volume = 0;
+                break;
+
+                default:
+
+                break;
+            }
+        break;
+
+        case 0xF:
+            /*
+            [15]: Set speed
+            Where [15][x][y] means "set speed to x*16+y". Though it is nowhere
+            near that simple. Let z = x*16+y. Depending on what values z takes,
+            different units of speed are set, there being two: ticks/division
+            and beats/minute (though this one is only a label and not strictly
+            true). If z=0, then what should technically happen is that the
+            module stops, but in practice it is treated as if z=1, because
+            there is already a method for stopping the module (running out of
+            patterns). If z<=32, then it means "set ticks/division to z"
+            otherwise it means "set beats/minute to z" (convention says that
+            this should read "If z<32.." but there are some composers out there
+            that defy conventions). Default values are 6 ticks/division, and
+            125 beats/minute (4 divisions = 1 beat). The beats/minute tag is
+            only meaningful for 6 ticks/division. To get a more accurate view
+            of how things work, use the following formula:
+                                     24 * beats/minute
+                  divisions/minute = -----------------
+                                      ticks/division
+            Hence divisions/minute range from 24.75 to 6120, eg. to get a value
+            of 2000 divisions/minute use 3 ticks/division and 250 beats/minute.
+            If multiple "set speed" effects are performed in a single division,
+            the ones on higher-numbered channels take precedence over the ones
+            on lower-numbered channels. This effect has a large number of
+            different implementations, but the one described here has the
+            widest usage.
+            */
+
+            if( (effect&0xFF) < 0x21 )
+            {
+                if( effect&0xFF )
+                {
+                    mod->song.speed = effect&0xFF;
+                    mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm)));
+                }
+            }
+
+            if( (effect&0xFF) >= 0x21 )
+            {
+                ///  HZ = 2 * BPM / 5
+                mod->bpm = effect&0xFF;
+                mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm)));
+            }
+
+        break;
+
+        default:
+        // Unsupported effect
+        break;
+
+    }
+
+}
+
+static void workeffect( note * nptr, channel * cptr )
+{
+    switch(cptr->effect)
+    {
+        case EFFECT_ARPEGGIO:
+
+            if( cptr->parameffect )
+            {
+                cptr->decalperiod = cptr->period - cptr->Arpperiods[cptr->ArpIndex];
+
+                cptr->ArpIndex++;
+                if( cptr->ArpIndex>2 )
+                    cptr->ArpIndex = 0;
+            }
+        break;
+
+        case EFFECT_PORTAMENTO_UP:
+
+            if(cptr->period)
+            {
+                cptr->period -= cptr->parameffect;
+
+                if( cptr->period < 113 || cptr->period > 20000 )
+                    cptr->period = 113;
+            }
+
+        break;
+
+        case EFFECT_PORTAMENTO_DOWN:
+
+            if(cptr->period)
+            {
+                cptr->period += cptr->parameffect;
+
+                if( cptr->period > 20000 )
+                    cptr->period = 20000;
+            }
+
+        break;
+
+        case EFFECT_VOLSLIDE_TONEPORTA:
+        case EFFECT_TONE_PORTAMENTO:
+
+            if( cptr->period && ( cptr->period != cptr->portaperiod ) && cptr->portaperiod )
+            {
+                if( cptr->period > cptr->portaperiod )
+                {
+                    if( cptr->period - cptr->portaperiod >= cptr->portaspeed )
+                    {
+                        cptr->period -= cptr->portaspeed;
+                    }
+                    else
+                    {
+                        cptr->period = cptr->portaperiod;
+                    }
+                }
+                else
+                {
+                    if( cptr->portaperiod - cptr->period >= cptr->portaspeed )
+                    {
+                        cptr->period += cptr->portaspeed;
+                    }
+                    else
+                    {
+                        cptr->period = cptr->portaperiod;
+                    }
+                }
+
+                if( cptr->period == cptr->portaperiod )
+                {
+                    // If the slide is over, don't let it to be retriggered.
+                    cptr->portaperiod = 0;
+                }
+            }
+
+            if( cptr->effect == EFFECT_VOLSLIDE_TONEPORTA )
+            {
+                if( cptr->volumeslide > 0x0F )
+                {
+                    cptr->volume = cptr->volume + (cptr->volumeslide>>4);
+
+                    if(cptr->volume>63)
+                        cptr->volume = 63;
+                }
+                else
+                {
+                    cptr->volume = cptr->volume - (cptr->volumeslide);
+
+                    if(cptr->volume>63)
+                        cptr->volume=0;
+                }
+            }
+        break;
+
+        case EFFECT_VOLSLIDE_VIBRATO:
+        case EFFECT_VIBRATO:
+
+            cptr->vibraperiod = ( (cptr->vibraparam&0xF) * sintable[cptr->vibrapointeur&0x1F] )>>7;
+
+            if( cptr->vibrapointeur > 31 )
+                cptr->vibraperiod = -cptr->vibraperiod;
+
+            cptr->vibrapointeur = (cptr->vibrapointeur+(((cptr->vibraparam>>4))&0xf)) & 0x3F;
+
+            if( cptr->effect == EFFECT_VOLSLIDE_VIBRATO )
+            {
+                if( cptr->volumeslide > 0xF )
+                {
+                    cptr->volume = cptr->volume+(cptr->volumeslide>>4);
+
+                    if( cptr->volume > 64 )
+                        cptr->volume = 64;
+                }
+                else
+                {
+                    cptr->volume = cptr->volume - cptr->volumeslide;
+
+                    if( cptr->volume > 64 )
+                        cptr->volume = 0;
+                }
+            }
+
+        break;
+
+        case EFFECT_VOLUME_SLIDE:
+
+            if( cptr->volumeslide > 0xF )
+            {
+                cptr->volume += (cptr->volumeslide>>4);
+
+                if( cptr->volume > 64 )
+                    cptr->volume = 64;
+            }
+            else
+            {
+                cptr->volume -= (cptr->volumeslide&0xf);
+
+                if( cptr->volume > 64 )
+                    cptr->volume = 0;
+            }
+        break;
+
+        case EFFECT_E_NOTE_CUT:
+            if(cptr->cut_param)
+                cptr->cut_param--;
+
+            if(!cptr->cut_param)
+                cptr->volume = 0;
+        break;
+
+        default:
+        break;
+
+    }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+bool jar_mod_init(jar_mod_context_t * modctx)
+{
+    muint i,j;
+
+    if( modctx )
+    {
+        memclear(modctx, 0, sizeof(jar_mod_context_t));
+        modctx->playrate = DEFAULT_SAMPLE_RATE;
+        modctx->stereo = 1;
+        modctx->stereo_separation = 1;
+        modctx->bits = 16;
+        modctx->filter = 1;
+        modctx->loopcount = 0;
+
+        for(i=0; i < PERIOD_TABLE_LENGTH - 1; i++)
+        {
+            for(j=0; j < 8; j++)
+            {
+                modctx->fullperiod[(i*8) + j] = periodtable[i] - ((( periodtable[i] - periodtable[i+1] ) / 8) * j);
+            }
+        }
+
+        return 1;
+    }
+
+    return 0;
+}
+
+bool jar_mod_setcfg(jar_mod_context_t * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter)
+{
+    if( modctx )
+    {
+        modctx->playrate = samplerate;
+
+        if( stereo )
+            modctx->stereo = 1;
+        else
+            modctx->stereo = 0;
+            
+        if(stereo_separation < 4)
+        {
+            modctx->stereo_separation = stereo_separation;
+        }
+
+        if( bits == 8 || bits == 16 )
+            modctx->bits = bits;
+        else
+            modctx->bits = 16;
+
+        if( filter )
+            modctx->filter = 1;
+        else
+            modctx->filter = 0;
+
+        return 1;
+    }
+
+    return 0;
+}
+
+// make certain that mod_data stays in memory while playing
+static bool jar_mod_load( jar_mod_context_t * modctx, void * mod_data, int mod_data_size )
+{
+    muint i, max;
+    unsigned short t;
+    sample *sptr;
+    unsigned char * modmemory,* endmodmemory;
+
+    modmemory = (unsigned char *)mod_data;
+    endmodmemory = modmemory + mod_data_size;
+    
+    
+
+    if(modmemory)
+    {
+        if( modctx )
+        {
+            memcopy(&(modctx->song.title),modmemory,1084);
+
+            i = 0;
+            modctx->number_of_channels = 0;
+            while(modlist[i].numberofchannels)
+            {
+                if(memcompare(modctx->song.signature,modlist[i].signature,4))
+                {
+                    modctx->number_of_channels = modlist[i].numberofchannels;
+                }
+
+                i++;
+            }
+
+            if( !modctx->number_of_channels )
+            {
+                // 15 Samples modules support
+                // Shift the whole datas to make it look likes a standard 4 channels mod.
+                memcopy(&(modctx->song.signature), "M.K.", 4);
+                memcopy(&(modctx->song.length), &(modctx->song.samples[15]), 130);
+                memclear(&(modctx->song.samples[15]), 0, 480);
+                modmemory += 600;
+                modctx->number_of_channels = 4;
+            }
+            else
+            {
+                modmemory += 1084;
+            }
+
+            if( modmemory >= endmodmemory )
+                return 0; // End passed ? - Probably a bad file !
+
+            // Patterns loading
+            for (i = max = 0; i < 128; i++)
+            {
+                while (max <= modctx->song.patterntable[i])
+                {
+                    modctx->patterndata[max] = (note*)modmemory;
+                    modmemory += (256*modctx->number_of_channels);
+                    max++;
+
+                    if( modmemory >= endmodmemory )
+                        return 0; // End passed ? - Probably a bad file !
+                }
+            }
+
+            for (i = 0; i < 31; i++)
+                modctx->sampledata[i]=0;
+
+            // Samples loading
+            for (i = 0, sptr = modctx->song.samples; i <31; i++, sptr++)
+            {
+                t= (sptr->length &0xFF00)>>8 | (sptr->length &0xFF)<<8;
+                sptr->length = t*2;
+
+                t= (sptr->reppnt &0xFF00)>>8 | (sptr->reppnt &0xFF)<<8;
+                sptr->reppnt = t*2;
+
+                t= (sptr->replen &0xFF00)>>8 | (sptr->replen &0xFF)<<8;
+                sptr->replen = t*2;
+
+
+                if (sptr->length == 0) continue;
+
+                modctx->sampledata[i] = (char*)modmemory;
+                modmemory += sptr->length;
+
+                if (sptr->replen + sptr->reppnt > sptr->length)
+                    sptr->replen = sptr->length - sptr->reppnt;
+
+                if( modmemory > endmodmemory )
+                    return 0; // End passed ? - Probably a bad file !
+            }
+
+            // States init
+
+            modctx->tablepos = 0;
+            modctx->patternpos = 0;
+            modctx->song.speed = 6;
+            modctx->bpm = 125;
+            modctx->samplenb = 0;
+
+            modctx->patternticks = (((long)modctx->song.speed * modctx->playrate * 5)/ (2 * modctx->bpm)) + 1;
+            modctx->patternticksaim = ((long)modctx->song.speed * modctx->playrate * 5) / (2 * modctx->bpm);
+
+            modctx->sampleticksconst = 3546894UL / modctx->playrate; //8448*428/playrate;
+
+            for(i=0; i < modctx->number_of_channels; i++)
+            {
+                modctx->channels[i].volume = 0;
+                modctx->channels[i].period = 0;
+            }
+
+            modctx->mod_loaded = 1;
+
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void jar_mod_fillbuffer( jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf )
+{
+    unsigned long i, j;
+    unsigned long k;
+    unsigned char c;
+    unsigned int state_remaining_steps;
+    int l,r;
+    int ll,lr;
+    int tl,tr;
+    short finalperiod;
+    note    *nptr;
+    channel *cptr;
+
+    if( modctx && outbuffer )
+    {
+        if(modctx->mod_loaded)
+        {
+            state_remaining_steps = 0;
+
+            if( trkbuf )
+            {
+                trkbuf->cur_rd_index = 0;
+
+                memcopy(trkbuf->name,modctx->song.title,sizeof(modctx->song.title));
+
+                for(i=0;i<31;i++)
+                {
+                    memcopy(trkbuf->instruments[i].name,modctx->song.samples[i].name,sizeof(trkbuf->instruments[i].name));
+                }
+            }
+
+            ll = modctx->last_l_sample;
+            lr = modctx->last_r_sample;
+
+            for (i = 0; i < nbsample; i++)
+            {
+                //---------------------------------------
+                if( modctx->patternticks++ > modctx->patternticksaim )
+                {
+                    if( !modctx->patterndelay )
+                    {
+                        nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]];
+                        nptr = nptr + modctx->patternpos;
+                        cptr = modctx->channels;
+
+                        modctx->patternticks = 0;
+                        modctx->patterntickse = 0;
+
+                        for(c=0;c<modctx->number_of_channels;c++)
+                        {
+                            worknote((note*)(nptr+c), (channel*)(cptr+c),(char)(c+1),modctx);
+                        }
+
+                        if( !modctx->jump_loop_effect )
+                            modctx->patternpos += modctx->number_of_channels;
+                        else
+                            modctx->jump_loop_effect = 0;
+
+                        if( modctx->patternpos == 64*modctx->number_of_channels )
+                        {
+                            modctx->tablepos++;
+                            modctx->patternpos = 0;
+                            if(modctx->tablepos >= modctx->song.length)
+                            {
+                                modctx->tablepos = 0;
+                                modctx->loopcount++; // count next loop
+                            }
+                        }
+                    }
+                    else
+                    {
+                        modctx->patterndelay--;
+                        modctx->patternticks = 0;
+                        modctx->patterntickse = 0;
+                    }
+
+                }
+
+                if( modctx->patterntickse++ > (modctx->patternticksaim/modctx->song.speed) )
+                {
+                    nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]];
+                    nptr = nptr + modctx->patternpos;
+                    cptr = modctx->channels;
+
+                    for(c=0;c<modctx->number_of_channels;c++)
+                    {
+                        workeffect(nptr+c, cptr+c);
+                    }
+
+                    modctx->patterntickse = 0;
+                }
+
+                //---------------------------------------
+
+                if( trkbuf && !state_remaining_steps )
+                {
+                    if( trkbuf->nb_of_state < trkbuf->nb_max_of_state )
+                    {
+                        memclear(&trkbuf->track_state_buf[trkbuf->nb_of_state], 0, sizeof(tracker_state));
+                    }
+                }
+
+                l=0;
+                r=0;
+
+                for(j =0, cptr = modctx->channels; j < modctx->number_of_channels ; j++, cptr++)
+                {
+                    if( cptr->period != 0 )
+                    {
+                        finalperiod = cptr->period - cptr->decalperiod - cptr->vibraperiod;
+                        if( finalperiod )
+                        {
+                            cptr->samppos += ( (modctx->sampleticksconst<<10) / finalperiod );
+                        }
+
+                        cptr->ticks++;
+
+                        if( cptr->replen<=2 )
+                        {
+                            if( (cptr->samppos>>10) >= (cptr->length) )
+                            {
+                                cptr->length = 0;
+                                cptr->reppnt = 0;
+
+                                if( cptr->length )
+                                    cptr->samppos = cptr->samppos % (((unsigned long)cptr->length)<<10);
+                                else
+                                    cptr->samppos = 0;
+                            }
+                        }
+                        else
+                        {
+                            if( (cptr->samppos>>10) >= (unsigned long)(cptr->replen+cptr->reppnt) )
+                            {
+                                cptr->samppos = ((unsigned long)(cptr->reppnt)<<10) + (cptr->samppos % ((unsigned long)(cptr->replen+cptr->reppnt)<<10));
+                            }
+                        }
+
+                        k = cptr->samppos >> 10;
+
+                        if( cptr->sampdata!=0 && ( ((j&3)==1) || ((j&3)==2) ) )
+                        {
+                            r += ( cptr->sampdata[k] *  cptr->volume );
+                        }
+
+                        if( cptr->sampdata!=0 && ( ((j&3)==0) || ((j&3)==3) ) )
+                        {
+                            l += ( cptr->sampdata[k] *  cptr->volume );
+                        }
+
+                        if( trkbuf && !state_remaining_steps )
+                        {
+                            if( trkbuf->nb_of_state < trkbuf->nb_max_of_state )
+                            {
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].number_of_tracks = modctx->number_of_channels;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].buf_index = i;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern = modctx->song.patterntable[modctx->tablepos];
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_pos = modctx->patternpos / modctx->number_of_channels;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_table_pos = modctx->tablepos;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].bpm = modctx->bpm;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].speed = modctx->song.speed;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_effect = cptr->effect_code;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_parameffect = cptr->parameffect;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_period = finalperiod;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_volume = cptr->volume;
+                                trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].instrument_number = (unsigned char)cptr->sampnum;
+                            }
+                        }
+                    }
+                }
+
+                if( trkbuf && !state_remaining_steps )
+                {
+                    state_remaining_steps = trkbuf->sample_step;
+
+                    if(trkbuf->nb_of_state < trkbuf->nb_max_of_state)
+                        trkbuf->nb_of_state++;
+                }
+                else
+                {
+                    state_remaining_steps--;
+                }
+
+                tl = (short)l;
+                tr = (short)r;
+
+                if ( modctx->filter )
+                {
+                    // Filter
+                    l = (l+ll)>>1;
+                    r = (r+lr)>>1;
+                }
+
+                if ( modctx->stereo_separation == 1 )
+                {
+                    // Left & Right Stereo panning
+                    l = (l+(r>>1));
+                    r = (r+(l>>1));
+                }
+
+                // Level limitation
+                if( l > 32767 ) l = 32767;
+                if( l < -32768 ) l = -32768;
+                if( r > 32767 ) r = 32767;
+                if( r < -32768 ) r = -32768;
+
+                // Store the final sample.
+                outbuffer[(i*2)]   = l;
+                outbuffer[(i*2)+1] = r;
+
+                ll = tl;
+                lr = tr;
+
+            }
+
+            modctx->last_l_sample = ll;
+            modctx->last_r_sample = lr;
+
+            modctx->samplenb = modctx->samplenb+nbsample;
+        }
+        else
+        {
+            for (i = 0; i < nbsample; i++)
+            {
+                // Mod not loaded. Return blank buffer.
+                outbuffer[(i*2)]   = 0;
+                outbuffer[(i*2)+1] = 0;
+            }
+
+            if(trkbuf)
+            {
+                trkbuf->nb_of_state = 0;
+                trkbuf->cur_rd_index = 0;
+                trkbuf->name[0] = 0;
+                memclear(trkbuf->track_state_buf, 0, sizeof(tracker_state) * trkbuf->nb_max_of_state);
+                memclear(trkbuf->instruments, 0, sizeof(trkbuf->instruments));
+            }
+        }
+    }
+}
+
+//resets internals for mod context
+static void jar_mod_reset( jar_mod_context_t * modctx)
+{
+    if(modctx)
+    {
+        memclear(&modctx->song, 0, sizeof(modctx->song));
+        memclear(&modctx->sampledata, 0, sizeof(modctx->sampledata));
+        memclear(&modctx->patterndata, 0, sizeof(modctx->patterndata));
+        modctx->tablepos = 0;
+        modctx->patternpos = 0;
+        modctx->patterndelay  = 0;
+        modctx->jump_loop_effect = 0;
+        modctx->bpm = 0;
+        modctx->patternticks = 0;
+        modctx->patterntickse = 0;
+        modctx->patternticksaim = 0;
+        modctx->sampleticksconst = 0;
+        modctx->loopcount = 0;
+        modctx->samplenb = 0;
+        memclear(modctx->channels, 0, sizeof(modctx->channels));
+        modctx->number_of_channels = 0;
+        modctx->mod_loaded = 0;
+        modctx->last_r_sample = 0;
+        modctx->last_l_sample = 0;
+        
+        jar_mod_init(modctx);
+    }
+}
+
+void jar_mod_unload( jar_mod_context_t * modctx)
+{
+    if(modctx)
+    {
+        if(modctx->modfile)
+        {
+            free(modctx->modfile);
+            modctx->modfile = 0;
+        }
+        jar_mod_reset(modctx);
+    }
+}
+
+
+
+mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename)
+{
+    mulong fsize = 0;
+    if(modctx->modfile)
+    {
+        free(modctx->modfile);
+        modctx->modfile = 0;
+    }
+    
+    FILE *f = fopen(filename, "rb");
+    if(f)
+    {
+        fseek(f,0,SEEK_END);
+        fsize = ftell(f);
+        fseek(f,0,SEEK_SET);
+        
+        if(fsize && fsize < 32*1024*1024)
+        {
+            modctx->modfile = malloc(fsize);
+            modctx->modfilesize = fsize;
+            memset(modctx->modfile, 0, fsize);
+            fread(modctx->modfile, fsize, 1, f);
+            fclose(f);
+            
+            if(!jar_mod_load(modctx, (void*)modctx->modfile, fsize)) fsize = 0;
+        } else fsize = 0;
+    }
+    return fsize;
+}
+
+mulong jar_mod_current_samples(jar_mod_context_t * modctx)
+{
+    if(modctx)
+        return modctx->samplenb;
+    
+    return 0;
+}
+
+// Works, however it is very slow, this data should be cached to ensure it is run only once per file
+mulong jar_mod_max_samples(jar_mod_context_t * ctx)
+{
+    jar_mod_context_t tmpctx;
+    jar_mod_init(&tmpctx);
+    if(!jar_mod_load(&tmpctx, (void*)ctx->modfile, ctx->modfilesize)) return 0;
+    
+    muint buff[2];
+    mulong lastcount = tmpctx.loopcount;
+    
+    while(1){
+        jar_mod_fillbuffer( &tmpctx, buff, 1, 0 );
+        if(tmpctx.loopcount > lastcount) break;
+    }
+    return tmpctx.samplenb;
+}
+
+// move seek_val to sample index, 0 -> jar_mod_max_samples is the range
+void jar_mod_seek_start(jar_mod_context_t * ctx)
+{
+    if(ctx)
+    {
+        jar_mod_reset(ctx);
+        jar_mod_load(ctx, ctx->modfile, ctx->modfilesize);
+    }
+}
+
+#endif // end of JAR_MOD_IMPLEMENTATION
+//-------------------------------------------------------------------------------
+
+
+#endif //end of header file

+ 24 - 2
src/raylib.h

@@ -261,8 +261,9 @@
 //----------------------------------------------------------------------------------
 #ifndef __cplusplus
 // Boolean type
-    #ifndef true
+    #if !defined(_STDBOOL_H)
         typedef enum { false, true } bool;
+        #define _STDBOOL_H
     #endif
 #endif
 
@@ -451,10 +452,29 @@ typedef struct Ray {
     Vector3 direction;
 } Ray;
 
+typedef enum { // allows errors to be & together
+    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;
+
 // Sound source type
 typedef struct Sound {
     unsigned int source;
     unsigned int buffer;
+    AudioError error; // if there was any error during the creation or use of this Sound
 } Sound;
 
 // Wave type, defines audio wave data
@@ -468,6 +488,8 @@ typedef struct Wave {
 
 typedef int RawAudioContext;
 
+
+
 // Texture formats
 // NOTE: Support depends on OpenGL version and platform
 typedef enum {
@@ -926,7 +948,7 @@ void SetMusicPitch(int index, float pitch);
 RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint);
 
 void CloseRawAudioContext(RawAudioContext ctx);
-int BufferRawAudioContext(RawAudioContext ctx, void *data, int numberElements); // returns number of elements buffered
+int BufferRawAudioContext(RawAudioContext ctx, void *data, unsigned short numberElements); // returns number of elements buffered
 
 #ifdef __cplusplus
 }