summaryrefslogtreecommitdiff
path: root/libs/raylib/src/external/jar_mod.h
diff options
context:
space:
mode:
Diffstat (limited to 'libs/raylib/src/external/jar_mod.h')
-rw-r--r--libs/raylib/src/external/jar_mod.h1596
1 files changed, 1596 insertions, 0 deletions
diff --git a/libs/raylib/src/external/jar_mod.h b/libs/raylib/src/external/jar_mod.h
new file mode 100644
index 0000000..eacd3b7
--- /dev/null
+++ b/libs/raylib/src/external/jar_mod.h
@@ -0,0 +1,1596 @@
+// 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 <kd7tck@gmail.com>
+// 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, const 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, 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
+
+// Allow custom memory allocators
+#ifndef JARMOD_MALLOC
+ #define JARMOD_MALLOC(sz) malloc(sz)
+#endif
+#ifndef JARMOD_FREE
+ #define JARMOD_FREE(p) free(p)
+#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;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+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, const 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
+
+#include <stdio.h>
+#include <stdlib.h>
+//#include <stdbool.h>
+
+// 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;
+
+ 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 bool 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->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;
+
+ return jar_mod_init(modctx);
+ }
+ return 0;
+}
+
+void jar_mod_unload( jar_mod_context_t * modctx)
+{
+ if(modctx)
+ {
+ if(modctx->modfile)
+ {
+ JARMOD_FREE(modctx->modfile);
+ modctx->modfile = 0;
+ modctx->modfilesize = 0;
+ modctx->loopcount = 0;
+ }
+ jar_mod_reset(modctx);
+ }
+}
+
+mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename)
+{
+ mulong fsize = 0;
+ if(modctx->modfile)
+ {
+ JARMOD_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 = JARMOD_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)
+{
+ mint buff[2];
+ mulong len;
+ mulong lastcount = ctx->loopcount;
+
+ while(ctx->loopcount <= lastcount)
+ jar_mod_fillbuffer(ctx, buff, 1, 0);
+
+ len = ctx->samplenb;
+ jar_mod_seek_start(ctx);
+
+ return len;
+}
+
+// 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 && ctx->modfile)
+ {
+ muchar* ftmp = ctx->modfile;
+ mulong stmp = ctx->modfilesize;
+ muint lcnt = ctx->loopcount;
+
+ if(jar_mod_reset(ctx)){
+ jar_mod_load(ctx, ftmp, stmp);
+ ctx->modfile = ftmp;
+ ctx->modfilesize = stmp;
+ ctx->loopcount = lcnt;
+ }
+ }
+}
+
+#endif // end of JAR_MOD_IMPLEMENTATION
+//-------------------------------------------------------------------------------
+
+
+#endif //end of header file \ No newline at end of file