changeset 658:c430112449a7

Move miniJSS into a subdirectory.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 16 Apr 2013 07:32:29 +0300
parents 7e25ec0bbf59
children bfad79dc1740
files Makefile.gen jloadjss.c jloadxm.c jmix_c_in.c jmix_post_c.h jmixtmpl_c.h jss.c jss.h jssmix.c jssmix.h jssmod.c jssmod.h jssplr.c jssplr.h minijss/jloadjss.c minijss/jloadxm.c minijss/jmix_c_in.c minijss/jmix_post_c.h minijss/jmixtmpl_c.h minijss/jss.c minijss/jss.h minijss/jssmix.c minijss/jssmix.h minijss/jssmod.c minijss/jssmod.h minijss/jssplr.c minijss/jssplr.h
diffstat 27 files changed, 5157 insertions(+), 5145 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.gen	Tue Apr 16 07:25:54 2013 +0300
+++ b/Makefile.gen	Tue Apr 16 07:32:29 2013 +0300
@@ -151,8 +151,11 @@
 ###
 ### miniJSS
 ###
+MINIJSS=$(DMLIB)/minijss/
+
+
 ifeq ($(JSS),yes)
-DM_CFLAGS+=-DDM_USE_JSS
+DM_CFLAGS+=-DDM_USE_JSS -I$(MINIJSS)
 
 ifeq ($(JSS_LIGHT),yes)
 DM_CFLAGS+=-DJSS_LIGHT -Os
@@ -188,7 +191,7 @@
 endif
 
 ifeq ($(DM_USE_C),yes)
-TARGETS += $(DMLIB)jmix_c.c
+TARGETS += $(MINIJSS)jmix_c.c
 DMLIB_OBJS+=jmix_c.o
 endif
 
@@ -276,6 +279,15 @@
 	@grep '#\s*include\s*\"' $< | sed 's/#\s*include\s\s*"\(.*\)"/\1/' | while read i; do if test -e "$$i"; then echo "$$i" >> $@; fi; done
 
 
+$(OBJPATH)%.o: $(MINIJSS)%.c $(MINIJSS)%.h
+	@echo " CC $<"
+	@$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS)
+
+$(OBJPATH)%.o: $(MINIJSS)%.c
+	@echo " CC $<"
+	@$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS)
+
+
 $(OBJPATH)%.o: $(DMLIB)tests/%.c $(DMLIB)tests/%.h
 	@echo " CC $<"
 	@$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS)
@@ -316,7 +328,7 @@
 ### dmlib rules
 ###
 
-$(DMLIB)jmix_c.c: $(DMLIB)jmix_c_in.c $(DMLIB)jmixtmpl_c.h $(DMLIB)jmix_post_c.h
+$(MINIJSS)jmix_c.c: $(MINIJSS)jmix_c_in.c $(MINIJSS)jmixtmpl_c.h $(MINIJSS)jmix_post_c.h
 	(echo "#include \"jssmix.h\"" && cpp $< $(DM_CFLAGS)) | sed "s/^# .*//g" > $@
 
 $(OBJPATH)dmimage.o: $(DMLIB)dmimage.c $(DMLIB)stb_image.c $(DMLIB)dmimage.h
@@ -324,15 +336,15 @@
 	@$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS)
 
 
-$(OBJPATH)jssmod.o: $(DMLIB)jssmod.c $(DMLIB)jssmod.h $(DMLIB)jss.h
+$(OBJPATH)jssmod.o: $(MINIJSS)jssmod.c $(MINIJSS)jssmod.h $(MINIJSS)jss.h
 	@echo " CC $+"
 	@$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS)
 
-$(OBJPATH)jssplr.o: $(DMLIB)jssplr.c $(DMLIB)jssplr.h $(DMLIB)jss.h $(DMLIB)jssmod.h $(DMLIB)jssmix.h
+$(OBJPATH)jssplr.o: $(MINIJSS)jssplr.c $(MINIJSS)jssplr.h $(MINIJSS)jss.h $(MINIJSS)jssmod.h $(MINIJSS)jssmix.h
 	@echo " CC $+"
 	@$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS)
 
-$(OBJPATH)jssmix.o: $(DMLIB)jssmix.c $(DMLIB)jssmix.h $(DMLIB)jss.h
+$(OBJPATH)jssmix.o: $(MINIJSS)jssmix.c $(MINIJSS)jssmix.h $(MINIJSS)jss.h
 	@echo " CC $+"
 	@$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS)
 
--- a/jloadjss.c	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,579 +0,0 @@
-/*
- * miniJSS - JSSMOD module loader
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2007-2009 Tecnic Software productions (TNSP)
- */
-#include "jssmod.h"
-#include <string.h>
-
-
-#ifdef JM_SUP_PATMODE_ALL
-#    define JM_SUP_PATMODE_1 1
-#    define JM_SUP_PATMODE_2 1
-#    define JM_SUP_PATMODE_3 1
-#    define JM_SUP_PATMODE_4 1
-#    define JM_SUP_PATMODE_5 1
-#endif
-
-
-static BOOL jsGetBufData(Uint8 **buf, size_t *bufLeft, void *data, const size_t dataSize)
-{
-    if (*bufLeft >= dataSize)
-    {
-        memcpy(data, *buf, dataSize);
-        *buf += dataSize;
-        *bufLeft -= dataSize;
-        return TRUE;
-    }
-    else
-        return FALSE;
-}
-
-
-static BOOL jsGetBufByte(Uint8 **buf, size_t *bufLeft, Uint8 *data)
-{
-    if (*bufLeft > 0)
-    {
-        *data = **buf;
-        (*buf)++;
-        (*bufLeft)--;
-        return TRUE;
-    }
-    else
-        return FALSE;
-}
-
-
-#define JSGETBUF(XV, XT) if (!jsGetBufData(buf, bufLeft, XV, sizeof(XT))) return DMERR_OUT_OF_DATA
-#define JSGETBYTE(XV) if (!jsGetBufByte(buf, bufLeft, XV)) return DMERR_OUT_OF_DATA
-
-
-#if defined(JM_SUP_PATMODE_1) || defined(JM_SUP_PATMODE_3)
-static int jssGetConvertedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
-{
-    Uint8 tmp;
-
-    JSGETBYTE(&tmp);
-
-    if (tmp == 127)
-        note->note = jsetNoteOff;
-    else if (tmp == 0)
-        note->note = jsetNotSet;
-    else
-        note->note = tmp - 1;
-
-    JSGETBYTE(&tmp);
-    note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
-
-    JSGETBYTE(&tmp);
-    note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
-
-    JSGETBYTE(&tmp);
-    note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
-
-    JSGETBYTE(&tmp);
-    note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
-
-    return DMERR_OK;
-}
-#endif
-
-
-#if defined(JM_SUP_PATMODE_2) || defined(JM_SUP_PATMODE_4)
-static int jssGetCompressedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
-{
-    Uint8 packb, tmp;
-
-    JSGETBYTE(&packb);
-    if (packb & 0x80)
-    {
-        if (packb & COMP_NOTE)
-        {
-            JSGETBYTE(&tmp);
-            if (tmp == 127)
-                note->note = jsetNoteOff;
-            else
-                note->note = tmp;
-        }
-
-        if (packb & COMP_INSTRUMENT)
-        {
-            JSGETBYTE(&tmp);
-            note->instrument = tmp;
-        }
-
-        if (packb & COMP_VOLUME)
-        {
-            JSGETBYTE(&tmp);
-            note->volume = tmp;
-        }
-
-        if (packb & COMP_EFFECT)
-        {
-            JSGETBYTE(&tmp);
-            note->effect = tmp;
-            note->param = 0;
-        }
-
-        if (packb & COMP_PARAM)
-        {
-            JSGETBYTE(&tmp);
-            note->param = tmp;
-        }
-    }
-    else
-    {
-        tmp = packb;
-
-        if (tmp == 127)
-            note->note = jsetNoteOff;
-        else if (tmp == 0)
-            note->note = jsetNotSet;
-        else
-            note->note = tmp - 1;
-
-        JSGETBYTE(&tmp);
-        note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
-
-        JSGETBYTE(&tmp);
-        note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
-
-        JSGETBYTE(&tmp);
-        note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
-
-        JSGETBYTE(&tmp);
-        note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
-    }
-
-    return DMERR_OK;
-}
-#endif
-
-
-#ifdef JM_SUP_PATMODE_2
-static int jssGetPatternCompHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
-{
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (row = 0; row < pattern->nrows; row++)
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    {
-        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetCompressedNote(&buf, bufLeft, note);
-        if (res != DMERR_OK)
-            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
-    }
-
-    return DMERR_OK;
-}
-#endif
-
-
-#ifdef JM_SUP_PATMODE_4
-static int jssGetPatternCompVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
-{
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    for (row = 0; row < pattern->nrows; row++)
-    {
-        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetCompressedNote(&buf, bufLeft, note);
-        if (res != DMERR_OK)
-            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
-    }
-
-    return DMERR_OK;
-}
-#endif
-
-
-#ifdef JM_SUP_PATMODE_1
-static int jssGetPatternRawHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
-{
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (row = 0; row < pattern->nrows; row++)
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    {
-        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetConvertedNote(&buf, bufLeft, note);
-        if (res != DMERR_OK)
-            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
-    }
-
-    return DMERR_OK;
-}
-#endif
-
-
-#ifdef JM_SUP_PATMODE_3
-static int jssGetPatternRawVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
-{
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    for (row = 0; row < pattern->nrows; row++)
-    {
-        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetConvertedNote(&buf, bufLeft, note);
-        if (res != DMERR_OK)
-            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
-    }
-
-    return DMERR_OK;
-}
-#endif
-
-
-#ifdef JM_SUP_PATMODE_5
-
-#undef JSGETBYTE
-#define JSGETBYTE(XV) if (!jsGetBufByte(&buf, bufLeft, XV)) return DMERR_OUT_OF_DATA
-
-#define JSFOREACHNOTE1                                                              \
-  for (channel = 0; channel < pattern->nchannels; channel++)                        \
-  for (row = 0; row < pattern->nrows; row++) {                                      \
-      JSSNote *note = pattern->data + (pattern->nchannels * row) + channel;
-
-#define JSFOREACHNOTE2 }
-
-static int jssGetPatternRawVertElem(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
-{
-    int row, channel;
-    Uint8 tmp;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    JSFOREACHNOTE1
-    JSGETBYTE(&tmp);
-    if (tmp == 0)
-        note->note = jsetNotSet;
-    else if (tmp == 127)
-        note->note = jsetNoteOff;
-    else
-        note->note = tmp - 1;
-    JSFOREACHNOTE2
-    
-    JSFOREACHNOTE1
-    JSGETBYTE(&tmp);
-    note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
-    JSFOREACHNOTE2
-    
-    JSFOREACHNOTE1
-    JSGETBYTE(&tmp);
-    note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
-    JSFOREACHNOTE2
-    
-    JSFOREACHNOTE1
-    JSGETBYTE(&tmp);
-    note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
-    JSFOREACHNOTE2
-    
-    JSFOREACHNOTE1
-    JSGETBYTE(&tmp);
-    note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
-    JSFOREACHNOTE2
-
-    return DMERR_OK;
-}
-#endif
-
-
-#undef JSGETBUF
-#undef JSGETBYTE
-#define JSGETBUF(XV, XT) do { \
-    if (!jsGetBufData(&buf, &bufLeft, XV, sizeof(XT)))  \
-        JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,    \
-        "Out of data at getting " # XT " (%d bytes)\n", sizeof(XT)); \
-} while (0)
-#define JSGETBYTE(XV) if (!jsGetBufByte(&buf, &bufLeft, XV)) return DMERR_OUT_OF_DATA
-
-
-#ifdef JM_SUP_EXT_INSTR
-static void jssCopyEnvelope(JSSEnvelope *e, JSSMODEnvelope *je)
-{
-    int i;
-
-    e->flags   = je->flags;
-    e->npoints = je->npoints;
-    e->sustain = je->sustain;
-    e->loopS   = je->loopS;
-    e->loopE   = je->loopE;
-
-    for (i = 0; i < je->npoints; i++)
-    {
-        e->points[i].frame = je->points[i].frame;
-        e->points[i].value = je->points[i].value;
-    }
-}
-#endif
-
-
-int jssLoadJSSMOD(Uint8 *bufStart, const size_t bufSize, JSSModule **ppModule)
-{
-    JSSModule *module;
-    JSSMODHeader jssH;
-    Uint8 *buf = bufStart;
-    size_t bufLeft = bufSize;
-    int index;
-
-    assert(ppModule != NULL);
-    assert(bufStart != NULL);
-    *ppModule = NULL;
-
-    // Check the JSSMOD header
-    memset(&jssH, 0, sizeof(jssH));
-    JSGETBUF(&jssH, JSSMODHeader);
-
-    if (memcmp(jssH.idMagic, "JM", 2) != 0)
-    {
-        JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Not a valid JSSMOD file, header signature missing!\n");
-    }
-
-    if (jssH.idVersion != JSSMOD_VERSION)
-    {
-        JSSERROR(DMERR_VERSION, DMERR_VERSION,
-        "Unsupported version of JSSMOD 0x%4x, this version only supports 0x%4x!\n",
-        jssH.idVersion, JSSMOD_VERSION);
-    }
-
-    // Allocate the module
-    module = jssAllocateModule();
-    if (module == NULL)
-    {
-        JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-        "Could not allocate memory for module structure.\n");
-    }
-    *ppModule = module;
-
-    // Copy header information
-    module->norders         = jssH.norders;
-    module->npatterns       = jssH.npatterns;
-    module->nchannels       = jssH.nchannels;
-    module->nextInstruments = jssH.nextInstruments;
-    module->ninstruments    = jssH.ninstruments;
-    module->defFlags        = jssH.defFlags;
-    module->intVersion      = jssH.intVersion;
-    module->defRestartPos   = jssH.defRestartPos;
-    module->defSpeed        = jssH.defSpeed;
-    module->defTempo        = jssH.defTempo;
-
-    // Get the orders list
-    for (index = 0; index < module->norders; index++)
-    {
-        Sint16 order;
-        JSGETBUF(&order, Sint16);
-        module->orderList[index] = order;
-    }
-
-    // Parse the patterns
-    for (index = 0; index < module->npatterns; index++)
-    {
-        JSSMODPattern jssP;
-        int result = DMERR_INVALID_DATA;
-        size_t bufSize;
-
-        // Get header and check size
-        memset(&jssP, 0, sizeof(jssP));
-        JSGETBUF(&jssP, JSSMODPattern);
-        bufSize = jssP.size;
-        if (bufLeft < jssP.size)
-        {
-            JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
-            "Out of data for pattern #%d.\n", index);
-        }
-
-        // Allocate pattern
-        module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels);
-        if (module->patterns[index] == NULL)
-        {
-            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-            "Could not allocate memory for pattern #%d.\n", index);
-        }
-
-        // Get pattern data
-        switch (jssH.patMode)
-        {
-#ifdef JM_SUP_PATMODE_1
-            case PATMODE_RAW_HORIZ:
-                result = jssGetPatternRawHoriz(buf, &bufSize, module->patterns[index]);
-                break;
-#endif
-#ifdef JM_SUP_PATMODE_2
-            case PATMODE_COMP_HORIZ:
-                result = jssGetPatternCompHoriz(buf, &bufSize, module->patterns[index]);
-                break;
-#endif
-#ifdef JM_SUP_PATMODE_3
-            case PATMODE_RAW_VERT:
-                result = jssGetPatternRawVert(buf, &bufSize, module->patterns[index]);
-                break;
-#endif
-#ifdef JM_SUP_PATMODE_4
-            case PATMODE_COMP_VERT:
-                result = jssGetPatternCompVert(buf, &bufSize, module->patterns[index]);
-                break;
-#endif
-#ifdef JM_SUP_PATMODE_5
-            case PATMODE_RAW_ELEM:
-                result = jssGetPatternRawVertElem(buf, &bufSize, module->patterns[index]);
-                break;
-#endif
-            default:
-                JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                "Unsupported pattern mode %d. Check compilation options.", jssH.patMode);
-                break;
-        }
-
-        if (bufSize > 0)
-        {
-            JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA,
-            "Unparsed data after pattern (%d bytes), possibly broken file.\n", bufSize);
-        }
-
-        if (result != DMERR_OK)
-        {
-            JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index);
-        }
-
-        buf += jssP.size;
-        bufLeft -= jssP.size;
-    }
-
-#ifdef JM_SUP_EXT_INSTR
-    // Read extended instruments
-    for (index = 0; index < module->nextInstruments; index++)
-    {
-        JSSMODExtInstrument jssE;
-        JSSExtInstrument *einst;
-        int i;
-
-        memset(&jssE, 0, sizeof(jssE));
-        JSGETBUF(&jssE, JSSMODExtInstrument);
-
-        if ((einst = jssAllocateExtInstrument()) == NULL)
-        {
-            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-            "Could not allocate extended instrument structure #%i\n", index);
-        }
-
-        module->extInstruments[index] = einst;
-
-        einst->nsamples     = jssE.nsamples;
-        einst->vibratoType  = jssE.vibratoType;
-        einst->vibratoSweep = jssE.vibratoSweep;
-        einst->vibratoDepth = jssE.vibratoDepth;
-        einst->vibratoRate  = jssE.vibratoRate;
-        einst->fadeOut      = jssE.fadeOut;
-
-        for (i = 0; i < jsetNNotes; i++)
-        {
-            int snum = jssE.sNumForNotes[i];
-            einst->sNumForNotes[i] = (snum > 0) ? snum : jsetNotSet;
-        }
-
-        jssCopyEnvelope(&(einst->volumeEnv), &jssE.volumeEnv);
-        jssCopyEnvelope(&(einst->panningEnv), &jssE.panningEnv);
-    }
-
-#ifdef JM_SUP_INSTR
-    // Read sample instrument headers
-    for (index = 0; index < module->ninstruments; index++)
-    {
-        JSSMODInstrument jssI;
-        JSSInstrument *inst;
-
-        memset(&jssI, 0, sizeof(jssI));
-        JSGETBUF(&jssI, JSSMODInstrument);
-
-        if ((inst = jssAllocateInstrument()) == NULL)
-        {
-            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-            "Could not allocate instrument structure #%i\n", index);
-        }
-
-        module->instruments[index] = inst;
-
-        inst->size          = jssI.size;
-        inst->loopS         = jssI.loopS;
-        inst->loopE         = jssI.loopE;
-        inst->volume        = jssI.volume;
-        inst->flags         = jssI.flags;
-        inst->C4BaseSpeed   = jssI.C4BaseSpeed;
-        inst->ERelNote      = jssI.ERelNote;
-        inst->EFineTune     = jssI.EFineTune;
-        inst->EPanning      = jssI.EPanning;
-        inst->hasData       = jssI.hasData;
-        inst->convFlags     = jssI.convFlags;
-    }
-
-#ifdef JM_SUP_SAMPLES
-    // Read sample data
-    for (index = 0; index < module->ninstruments; index++)
-    {
-        JSSInstrument *inst = module->instruments[index];
-
-        if (inst && inst->hasData)
-        {
-            size_t sz;
-
-            // Calculate data size
-            if (inst->flags & jsf16bit)
-                sz = inst->size * sizeof(Uint16);
-            else
-                sz = inst->size * sizeof(Uint8);
-
-            // Check if we can get as much?
-            if (bufLeft < sz)
-            {
-                JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
-                "Out of data for instrument sample #%d (%d < %d)\n",
-                index, bufLeft, sz);
-            }
-
-            // Allocate
-            if ((inst->data = dmMalloc(sz)) == NULL)
-            {
-                JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-                "Could not allocate sample data #%d\n", index);
-            }
-
-            // Copy data
-            memcpy(inst->data, buf, sz);
-            buf += sz;
-            bufLeft -= sz;
-
-            // Convert, if needed
-            if (inst->flags & jsf16bit)
-                jssDecodeSample16(inst->data, inst->size, inst->convFlags);
-            else
-                jssDecodeSample8(inst->data, inst->size, inst->convFlags);
-        }
-    }
-#else
-#    warning Not including JSSMOD sample loading!
-#endif  // JM_SUP_SAMPLES
-#else
-#    warning Not including JSSMOD instrument loading!
-#endif  // JM_SUP_INSTR
-#else
-#    warning Not including JSSMOD ext.instrument loading!
-#endif  // JM_SUP_EXT_INSTR
-
-    return DMERR_OK;
-}
--- a/jloadxm.c	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,790 +0,0 @@
-/*
- * miniJSS - Fast Tracker ][ (XM) module loader
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
- *
- * TO DO:
- * - Add support for 1.02/1.03 XM-format versions.
- *   (Not very useful, but if it's not too hard, then do it)
- */
-#include "jssmod.h"
-#include <string.h>
-
-
-/* XM value limit definitions
- */
-#define XM_MaxChannels      (32)
-#define XM_MaxPatterns      (256)
-#define XM_MaxOrders        (255)
-#define XM_MaxInstruments   (128)
-#define XM_MaxInstSamples   (16)
-#define XM_MaxEnvPoints     (12)
-#define XM_MaxNotes         (96)
-#define XM_MaxSampleVolume  (64)
-
-
-/* XM format structures
- */
-typedef struct
-{
-    char    idMagic[17];       // XM header ID "Extended Module: "
-    char    songName[20];      // Module song name
-    Uint8   unUsed1A;          // ALWAYS 0x1a
-    char    trackerName[20];   // ID-string of tracker software
-    Uint16  version;           // XM-version 0x0104
-    Uint32  headSize;          // Module header size, FROM THIS POINT!
-    Uint16  norders,           // Number of orders
-            defRestartPos,     // Default song restart position
-            nchannels,         // Number of channels
-            npatterns,         // Number of patterns
-            ninstruments,      // Number of instruments
-            flags,             // Module flags:
-                               // bit0: 0 = Amiga frequency table
-                               //       1 = Linear frequency table
-                               //
-            defSpeed,          // Default speed
-            defTempo;          // Default tempo
-    Uint8   orderList[256];    // Order list
-} XMHeader;
-
-
-typedef struct
-{
-    Uint32  headSize;          // Instrument header size (see docs!)
-    char    instName[22];      // Name/description
-    Uint8   instType;          // Type
-    Uint16  nsamples;          // Number of samples
-} XMInstrument1;
-
-
-typedef struct
-{
-    Uint16 frame, value;
-} XMEnvPoint;
-
-
-typedef struct
-{
-    Uint8 flags, npoints, sustain, loopS, loopE;
-    XMEnvPoint points[XM_MaxEnvPoints];
-} XMEnvelope;
-
-
-typedef struct
-{
-    Uint32 headSize;                  // Header size
-    Uint8 sNumForNotes[XM_MaxNotes];  // Sample numbers for notes
-    XMEnvelope volumeEnv, panningEnv;
-    Uint8 vibratoType, vibratoSweep, vibratoDepth, vibratoRate;
-
-    Uint16 fadeOut, ARESERVED;
-} XMInstrument2;
-
-
-typedef struct
-{
-    Uint32 size, loopS, loopL;
-    Uint8 volume;
-    int fineTune;
-    Uint8 type, panning;
-    int relNote;
-    Uint8 ARESERVED;
-    char sampleName[22];
-} XMSample;
-
-
-typedef struct
-{
-    Uint32 headSize;
-    Uint8 packing;
-    Uint16 nrows, size;
-} XMPattern;
-
-
-
-/* Unpack XM-format pattern from file-stream into JSS-pattern structure
- */
-#define JSGETBYTE(XV) do {                          \
-    size--;                                         \
-    if (size < 0)                                   \
-    JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,  \
-    "Unexpected end of packed pattern data.\n");    \
-    XV = dmfgetc(inFile);                           \
-} while (0)
-
-
-static int jssXMConvertNote(int val)
-{
-    if (val < 1 || val > 97)
-        return jsetNotSet;
-    else if (val == 97)
-        return jsetNoteOff;
-    else
-        return val - 1;
-}
-
-
-static int jssXMUnpackPattern(DMResource *inFile, int size, JSSPattern *pattern)
-{
-    int packb, row, channel, tmp;
-    JSSNote *pnote;
-    assert(pattern != NULL);
-
-    pnote = pattern->data;
-
-    for (row = 0; row < pattern->nrows && size > 0; row++)
-    for (channel = 0; channel < pattern->nchannels && size > 0; channel++)
-    {
-        JSGETBYTE(packb);
-        if (packb & 0x80)
-        {
-            if (packb & 0x01)
-            {
-                // PACK 0x01: Read note
-                JSGETBYTE(tmp);
-                pnote->note = jssXMConvertNote(tmp);
-            }
-
-            if (packb & 0x02)
-            {
-                // PACK 0x02: Read instrument
-                JSGETBYTE(tmp);
-                pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
-            }
-
-            if (packb & 0x04)
-            {
-                // PACK 0x04: Read volume
-                JSGETBYTE(tmp);
-                pnote->volume = (tmp >= 0x10) ? tmp - 0x10 : jsetNotSet;
-            }
-
-            if (packb & 0x08)
-            {
-                // PACK 0x08: Read effect
-                JSGETBYTE(pnote->effect);
-                pnote->param = 0;
-            }
-
-            if (packb & 0x10)
-            {
-                // PACK 0x10: Read effect parameter
-                JSGETBYTE(pnote->param);
-                if (pnote->effect == jsetNotSet && pnote->param != 0)
-                    pnote->effect = 0;
-            }
-        }
-        else
-        {
-            // All data available
-            pnote->note = jssXMConvertNote(packb & 0x7f);
-
-            // Get instrument
-            JSGETBYTE(tmp);
-            pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
-
-            // Get volume
-            JSGETBYTE(tmp);
-            pnote->volume = (tmp >= 0x10) ? tmp - 0x10 : jsetNotSet;
-
-            // Get effect
-            JSGETBYTE(pnote->effect);
-
-            // Get parameter
-            JSGETBYTE(pnote->param);
-            if (pnote->effect == 0 && pnote->param == 0)
-                pnote->effect = pnote->param = jsetNotSet;
-        }
-        pnote++;
-    }
-
-    // Check the state
-    if (size > 0)
-    {
-        // Some data left unparsed
-        JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA,
-        "Unparsed data after pattern (%i bytes), possibly broken file.\n", size);
-    }
-
-    return DMERR_OK;
-}
-
-
-/* Convert XM envelope structure to JSS envelope structure
- */
-static int jssXMConvertEnvelope(JSSEnvelope * d, XMEnvelope * s, char * e, int instr)
-{
-    int i;
-    (void) e; (void) instr;
-
-    // Convert envelope points
-    for (i = 0; i < XM_MaxEnvPoints; i++)
-    {
-        d->points[i].frame = s->points[i].frame;
-        d->points[i].value = s->points[i].value;
-    }
-    
-    // Convert other values
-    d->npoints = s->npoints;
-    d->sustain = s->sustain;
-    d->loopS = s->loopS;
-    d->loopE = s->loopE;
-    
-    // Check if the envelope is used
-    if (s->flags & 0x01)
-    {
-        // Convert envelope flags
-        d->flags = jenvfUsed;
-        if (s->flags & 0x02)
-            d->flags |= jenvfSustain;
-
-        if (s->flags & 0x04)
-            d->flags |= jenvfLooped;
-
-        // Check other values
-        if (s->npoints > XM_MaxEnvPoints)
-        {
-            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-            "Inst#%i/%s-env: nPoints > MAX, possibly broken file.\n", instr, e);
-            s->npoints = XM_MaxEnvPoints;
-        }
-
-        if ((d->flags & jenvfSustain) && s->sustain > s->npoints)
-        {
-            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-            "Inst#%i/%s-env: iSustain > nPoints (%i > %i), possibly broken file.\n",
-            instr, e, s->sustain, s->npoints);
-            s->sustain = s->npoints;
-        }
-
-        if ((d->flags & jenvfLooped) && s->loopE > s->npoints)
-        {
-            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-            "Inst#%i/%s-env: loopE > nPoints (%i > %i), possibly broken file.\n",
-            instr, e, s->loopE, s->npoints);
-            s->loopE = s->npoints;
-        }
-
-        if ((d->flags & jenvfLooped) && s->loopS > s->loopE)
-        {
-            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-            "Inst#%i/%s-env: loopS > loopE (%i > %i), possibly broken file.\n",
-            instr, e, s->loopS, s->loopE);
-            s->loopS = 0;
-        }
-    }
-
-    return DMERR_OK;
-}
-
-
-/* Load XM-format extended instrument from file-stream into JSS module's given inst
- */
-static int jssXMLoadExtInstrument(DMResource *inFile, int ninst, JSSModule *module)
-{
-    XMInstrument1 xmI1;
-    off_t pos, remainder;
-
-    // Get instrument header #1
-    pos = dmftell(inFile);
-    dmf_read_le32(inFile, &xmI1.headSize);
-    dmf_read_str(inFile, &xmI1.instName, sizeof(xmI1.instName));
-    xmI1.instType = dmfgetc(inFile);
-    dmf_read_le16(inFile, &xmI1.nsamples);
-
-    // If there are samples, there is header #2
-    if (xmI1.nsamples > 0)
-    {
-        int i, nsample, tmp;
-        int xmConvTable[XM_MaxInstruments + 1];
-        JSSExtInstrument *pEInst;
-        JSSInstrument *pInst;
-        XMInstrument2 xmI2;
-
-        // Allocate instrument
-        if ((pEInst = jssAllocateExtInstrument()) == NULL)
-        {
-            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-            "Could not allocate extended instrument structure #%i\n", ninst);
-        }
-
-        module->extInstruments[ninst] = pEInst;
-
-        // Get instrument header #2
-        dmf_read_le32(inFile, &xmI2.headSize);
-        dmf_read_str(inFile, &xmI2.sNumForNotes, sizeof(xmI2.sNumForNotes));
-
-        for (i = 0; i < XM_MaxEnvPoints; i++)
-        {
-            dmf_read_le16(inFile, &xmI2.volumeEnv.points[i].frame);
-            dmf_read_le16(inFile, &xmI2.volumeEnv.points[i].value);
-        }
-
-        for (i = 0; i < XM_MaxEnvPoints; i++)
-        {
-            dmf_read_le16(inFile, &xmI2.panningEnv.points[i].frame);
-            dmf_read_le16(inFile, &xmI2.panningEnv.points[i].value);
-        }
-
-        xmI2.volumeEnv.npoints = dmfgetc(inFile);
-        xmI2.panningEnv.npoints = dmfgetc(inFile);
-
-        xmI2.volumeEnv.sustain = dmfgetc(inFile);
-        xmI2.volumeEnv.loopS = dmfgetc(inFile);
-        xmI2.volumeEnv.loopE = dmfgetc(inFile);
-
-        xmI2.panningEnv.sustain = dmfgetc(inFile);
-        xmI2.panningEnv.loopS = dmfgetc(inFile);
-        xmI2.panningEnv.loopE = dmfgetc(inFile);
-
-        xmI2.volumeEnv.flags = dmfgetc(inFile);
-        xmI2.panningEnv.flags = dmfgetc(inFile);
-
-        xmI2.vibratoType = dmfgetc(inFile);
-        xmI2.vibratoSweep = dmfgetc(inFile);
-        xmI2.vibratoDepth = dmfgetc(inFile);
-        xmI2.vibratoRate = dmfgetc(inFile);
-
-        dmf_read_le16(inFile, &xmI2.fadeOut);
-        dmf_read_le16(inFile, &xmI2.ARESERVED);
-
-        // Skip the extra data after header #2
-        remainder = xmI1.headSize - (dmftell(inFile) - pos);
-        if (remainder > 0)
-        {
-            JSSDEBUG("xmI1#1 Skipping: %li\n", remainder);
-            dmfseek(inFile, remainder, SEEK_CUR);
-        }
-
-        // Check and convert all ext instrument information
-#ifndef JSS_LIGHT
-        pEInst->desc = jssASCIItoStr(xmI1.instName, 0, sizeof(xmI1.instName));
-#endif
-        jssXMConvertEnvelope(&pEInst->volumeEnv, &xmI2.volumeEnv, "vol", ninst);
-        jssXMConvertEnvelope(&pEInst->panningEnv, &xmI2.panningEnv, "pan", ninst);
-
-        switch (xmI2.vibratoType)
-        {
-            case 0: pEInst->vibratoType = jvibSine; break;
-            case 1: pEInst->vibratoType = jvibRamp; break;
-            case 2: pEInst->vibratoType = jvibSquare; break;
-            case 3: pEInst->vibratoType = jvibRandom; break;
-            default:
-                pEInst->vibratoType = jvibSine;
-                JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                "Invalid extinstrument vibrato type %d for inst #%d\n", ninst);
-                break;
-        }
-        pEInst->vibratoSweep = xmI2.vibratoSweep;
-        pEInst->vibratoDepth = xmI2.vibratoDepth;
-        pEInst->vibratoRate = xmI2.vibratoRate;
-        pEInst->fadeOut = xmI2.fadeOut;
-        pEInst->nsamples = xmI1.nsamples;
-
-        // Initialize the SNumForNotes conversion table
-        for (i = 0; i < XM_MaxInstruments; i++)
-            xmConvTable[i] = jsetNotSet;
-
-        // Read sample headers
-        for (nsample = 0; nsample < xmI1.nsamples; nsample++)
-        {
-            XMSample xmS;
-
-            // Read header data
-            dmf_read_le32(inFile, &xmS.size);
-            dmf_read_le32(inFile, &xmS.loopS);
-            dmf_read_le32(inFile, &xmS.loopL);
-            xmS.volume = dmfgetc(inFile);
-            xmS.fineTune = (signed char) dmfgetc(inFile);
-            xmS.type = dmfgetc(inFile);
-            xmS.panning = dmfgetc(inFile);
-            xmS.relNote = (signed char) dmfgetc(inFile);
-            xmS.ARESERVED = dmfgetc(inFile);
-            dmf_read_str(inFile, &xmS.sampleName, sizeof(xmS.sampleName));
-
-            if (xmS.size > 0)
-            {
-                // Allocate sample instrument
-                JSSDEBUG("Allocating sample #%i/%i [%i]\n",
-                ninst, nsample, module->ninstruments);
-
-                xmConvTable[nsample] = module->ninstruments;
-                pInst = module->instruments[module->ninstruments] = jssAllocateInstrument();
-                if (pInst == NULL)
-                {
-                    JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-                    "Could not allocate sample #%i/%i [%i]\n",
-                    ninst, nsample, module->ninstruments);
-                }
-                module->ninstruments++;
-            } else
-                pInst = NULL;
-
-            // Check and convert sample information
-            if (pInst != NULL)
-            {
-                // Copy values
-                if (xmS.volume > XM_MaxSampleVolume)
-                {
-                    JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                           "Samp #%i/%i: volume > MAX\n", ninst, nsample);
-                    xmS.volume = XM_MaxSampleVolume;
-                }
-
-                pInst->volume = xmS.volume;
-                pInst->ERelNote = xmS.relNote;
-                pInst->EFineTune = xmS.fineTune;
-                pInst->EPanning = xmS.panning;
-#ifndef JSS_LIGHT
-                pInst->desc = jssASCIItoStr(xmS.sampleName, 0, sizeof(xmS.sampleName));
-#endif
-
-                // Convert flags
-                switch (xmS.type & 0x03)
-                {
-                    case 0: pInst->flags = 0; break;
-                    case 1: pInst->flags = jsfLooped; break;
-                    case 2: pInst->flags = jsfLooped | jsfBiDi; break;
-                    default:
-                        pInst->flags = 0;
-                        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                        "Samp #%i/%i: Invalid sample type 0x%x\n",
-                        ninst, nsample, xmS.type);
-                        break;
-                }
-
-                if (xmS.type & 0x10)
-                {
-                    // 16-bit sample
-                    JSFSET(pInst->flags, jsf16bit);
-
-                    pInst->size = xmS.size / sizeof(Uint16);
-                    pInst->loopS = xmS.loopS / sizeof(Uint16);
-                    pInst->loopE = ((xmS.loopS + xmS.loopL) / sizeof(Uint16));
-                }
-                else
-                {
-                    // 8-bit sample
-                    pInst->size = xmS.size;
-                    pInst->loopS = xmS.loopS;
-                    pInst->loopE = (xmS.loopS + xmS.loopL);
-                }
-                
-                if (xmS.loopL == 0)
-                {
-                    // Always unset loop, if loop length is zero
-                    JSFUNSET(pInst->flags, jsfLooped);
-                }
-
-                if (pInst->flags & jsfLooped)
-                {
-                    if (pInst->loopS >= pInst->size)
-                    {
-                        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                        "Samp #%i/%i: loopS >= size (%d >= %d)\n",
-                        ninst, nsample, pInst->loopS, pInst->size);
-                        JSFUNSET(pInst->flags, jsfLooped);
-                    }
-
-                    if (pInst->loopE > pInst->size)
-                    {
-                        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                        "Samp #%i/%i: loopE > size (%d > %d)\n",
-                        ninst, nsample, pInst->loopE, pInst->size);
-                        JSFUNSET(pInst->flags, jsfLooped);
-                    }
-                }
-
-
-                // Allocate memory for sample data
-                if (pInst->flags & jsf16bit)
-                    pInst->data = dmCalloc(pInst->size, sizeof(Uint16));
-                else
-                    pInst->data = dmCalloc(pInst->size, sizeof(Uint8));
-
-                if (pInst->data == NULL)
-                    JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-                    "Could not allocate sample data #%i/%i.\n", ninst, nsample);
-            }
-        }
-
-        // Read sample data
-        for (nsample = 0; nsample < xmI1.nsamples; nsample++)
-        if (xmConvTable[nsample] != jsetNotSet)
-        {
-            pInst = module->instruments[xmConvTable[nsample]];
-            if (pInst)
-            {
-                JSSDEBUG("desc....: '%s'\n"
-                     "size....: %i\n"
-                     "loopS...: %i\n"
-                     "loopE...: %i\n"
-                     "volume..: %i\n"
-                     "flags...: %x\n",
-                     pInst->desc,
-                     pInst->size, pInst->loopS, pInst->loopE,
-                     pInst->volume, pInst->flags);
-
-                if (pInst->flags & jsf16bit)
-                {
-                    // Read sampledata
-                    if (dmfread(pInst->data, sizeof(Uint16), pInst->size, inFile) != (size_t) pInst->size)
-                        JSSERROR(DMERR_FREAD, DMERR_FREAD,
-                        "Error reading sampledata for instrument #%i/%i, %i words.\n",
-                        ninst, nsample, pInst->size);
-
-                    // Convert data
-                    jssDecodeSample16((Uint16 *) pInst->data, pInst->size,
-#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
-                               (jsampDelta | jsampSwapEndianess)
-#else
-                               (jsampDelta)
-#endif
-                    );
-                }
-                else
-                {
-                    // Read sampledata
-                    if (dmfread(pInst->data, sizeof(Uint8), pInst->size, inFile) != (size_t) pInst->size)
-                        JSSERROR(DMERR_FREAD, DMERR_FREAD,
-                        "Error reading sampledata for instrument #%i/%i, %i bytes.\n",
-                        ninst, nsample, pInst->size);
-
-                    // Convert data
-                    jssDecodeSample8((Uint8 *) pInst->data, pInst->size,
-                        (jsampDelta | jsampFlipSign));
-                }
-            }
-        }
-
-        // Apply new values to sNumForNotes values
-        for (i = 0; i < XM_MaxNotes; i++)
-        {
-            tmp = xmI2.sNumForNotes[i];
-            if (tmp >= 0 && tmp < xmI1.nsamples)
-                pEInst->sNumForNotes[i] = xmConvTable[tmp];
-            else
-            {
-                pEInst->sNumForNotes[i] = jsetNotSet;
-                JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-                "Ext.instrument #%d sNumForNotes[%d] out of range (%d).\n",
-                ninst, i, tmp);
-            }
-        }
-    }
-    else
-    {
-        // We may STILL need to skip extra data after 1st instr. header
-        remainder = xmI1.headSize - (dmftell(inFile) - pos);
-        if (remainder > 0)
-        {
-            JSSDEBUG("xmI1#2 Skipping: %li\n", remainder);
-            dmfseek(inFile, remainder, SEEK_CUR);
-        }
-    }
-
-    return 0;
-}
-
-
-/* Load XM-format module from given file-stream
- */
-int jssLoadXM(DMResource *inFile, JSSModule **ppModule)
-{
-    JSSModule *module;
-    XMHeader xmH;
-    int result, index, tmp;
-
-    assert(ppModule != NULL);
-    assert(inFile != NULL);
-    *ppModule = NULL;
-
-    /* Get XM-header and check it
-     */
-    dmf_read_str(inFile, &xmH.idMagic, sizeof(xmH.idMagic));
-    dmf_read_str(inFile, &xmH.songName, sizeof(xmH.songName));
-    xmH.unUsed1A = dmfgetc(inFile);
-    dmf_read_str(inFile, &xmH.trackerName, sizeof(xmH.trackerName));
-    dmf_read_le16(inFile, &xmH.version);
-    dmf_read_le32(inFile, &xmH.headSize);
-    dmf_read_le16(inFile, &xmH.norders);
-    dmf_read_le16(inFile, &xmH.defRestartPos);
-    dmf_read_le16(inFile, &xmH.nchannels);
-    dmf_read_le16(inFile, &xmH.npatterns);
-    dmf_read_le16(inFile, &xmH.ninstruments);
-    dmf_read_le16(inFile, &xmH.flags);
-    dmf_read_le16(inFile, &xmH.defSpeed);
-    dmf_read_le16(inFile, &xmH.defTempo);
-    dmf_read_str(inFile, &xmH.orderList, sizeof(xmH.orderList));
-
-
-    // Check the fields, none of these are considered fatal
-    if (strncmp(xmH.idMagic, "Extended Module: ", 17) != 0)
-    {
-        JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
-        "Not a FT2 Extended Module (XM), ident mismatch!\n");
-    }
-
-    if (xmH.version != 0x0104)
-    {
-        JSSWARNING(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
-        "Unsupported version of XM format 0x%04x instead of expected 0x0104.\n",
-        xmH.version);
-    }
-
-    if (xmH.unUsed1A != 0x1a)
-    {
-        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Possibly modified or corrupted XM [%x]\n", xmH.unUsed1A);
-    }
-
-    if (xmH.norders > XM_MaxOrders)
-    {
-        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Number of orders %i > %i, possibly broken module.\n",
-        xmH.norders, XM_MaxOrders);
-        xmH.norders = XM_MaxOrders;
-    }
-
-    if (xmH.norders == 0)
-    {
-        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Number of orders was zero.\n");
-    }
-
-    if (xmH.npatterns > XM_MaxPatterns)
-    {
-        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Number of patterns %i > %i, possibly broken module.\n",
-        xmH.npatterns, XM_MaxPatterns);
-        xmH.npatterns = XM_MaxPatterns;
-    }
-
-    if (xmH.npatterns == 0)
-    {
-        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Number of patterns was zero.\n");
-    }
-
-    if (xmH.nchannels <= 0 || xmH.nchannels > XM_MaxChannels)
-    {
-        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Number of channels was invalid, %i (should be 1 - %i).\n",
-        xmH.nchannels, XM_MaxChannels);
-    }
-
-    if (xmH.ninstruments <= 0 || xmH.ninstruments > XM_MaxInstruments)
-    {
-        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-        "Number of instruments was invalid, %i (should be 1 - %i).\n",
-        xmH.ninstruments, XM_MaxInstruments);
-    }
-
-    /* Okay, allocate a module structure
-     */
-    module = jssAllocateModule();
-    if (module == NULL)
-    {
-        JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-        "Could not allocate memory for module structure.\n");
-    }
-    *ppModule = module;
-
-
-    // Convert and check the header data
-    module->moduleType      = jmdtXM;
-    module->intVersion      = xmH.version;
-#ifndef JSS_LIGHT
-    module->moduleName      = jssASCIItoStr(xmH.songName, 0, sizeof(xmH.songName));
-    module->trackerName     = jssASCIItoStr(xmH.trackerName, 0, sizeof(xmH.trackerName));
-#endif
-    module->defSpeed        = xmH.defSpeed;
-    module->defTempo        = xmH.defTempo;
-    module->nextInstruments = xmH.ninstruments;
-    module->ninstruments    = 0;
-    module->npatterns       = xmH.npatterns;
-    module->norders         = xmH.norders;
-    module->nchannels       = xmH.nchannels;
-    module->defFlags        = jmdfStereo | jmdfFT2Replay;
-    module->defRestartPos   = xmH.defRestartPos;
-
-    if ((xmH.flags & 1) == 0)
-        module->defFlags |= jmdfAmigaPeriods;
-
-    // Setup the default pannings
-    for (index = 0; index < jsetNChannels; index++)
-        module->defPanning[index] = jchPanMiddle;
-
-    /* Read patterns
-     */
-    for (index = 0; index < module->npatterns; index++)
-    {
-        off_t pos, remainder;
-        XMPattern xmP;
-
-        // Get the pattern header
-        pos = dmftell(inFile);
-        dmf_read_le32(inFile, &xmP.headSize);
-        xmP.packing = dmfgetc(inFile);
-        dmf_read_le16(inFile, &xmP.nrows);
-        dmf_read_le16(inFile, &xmP.size);
-
-        // Check the header
-        if (xmP.packing != 0)
-            JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-            "Pattern #%i packing type unsupported (%i)\n",
-            index, xmP.packing);
-
-        if (xmP.nrows == 0)
-            JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
-            "Pattern #%i has %i rows, invalid data.\n",
-            index, xmP.nrows);
-
-        if (xmP.size > 0)
-        {
-            // Allocate and unpack pattern
-            module->patterns[index] = jssAllocatePattern(xmP.nrows, module->nchannels);
-            if (module->patterns[index] == NULL)
-                JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
-                "Could not allocate memory for pattern #%i\n", index);
-
-            result = jssXMUnpackPattern(inFile, xmP.size, module->patterns[index]);
-            if (result != 0)
-                JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index);
-        }
-
-        // Skip extra data (if the file is damaged)
-        remainder = xmP.headSize - (dmftell(inFile) - pos);
-        if (remainder > 0)
-        {
-            JSSDEBUG("xmP Skipping: %li\n", remainder);
-            dmfseek(inFile, remainder, SEEK_CUR);
-        }
-    }
-
-    // Allocate the empty pattern
-    module->patterns[jsetMaxPatterns] = jssAllocatePattern(64, module->nchannels);
-
-    /* Convert song orders list by replacing nonexisting patterns
-     * with pattern number jsetMaxPatterns.
-     */
-    for (index = 0; index < module->norders; index++)
-    {
-        tmp = xmH.orderList[index];
-        if (tmp >= module->npatterns || module->patterns[tmp] == NULL)
-            tmp = jsetMaxPatterns;
-
-        module->orderList[index] = tmp;
-    }
-
-    /* Read instruments
-     */
-    for (index = 0; index < module->nextInstruments; index++)
-    {
-        result = jssXMLoadExtInstrument(inFile, index, module);
-        if (result != 0)
-            JSSERROR(result, result, "Errors while reading instrument #%i\n", index);
-    }
-
-    return DMERR_OK;
-}
--- a/jmix_c_in.c	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * miniJSS - Mixing routines in C
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
- */
-
-//#define JMIXER_DEBUG fprintf(stderr, "[%.8x:%.8x / %.8x]: %d\n", FP_GETH(tmpPos), FP_GETL(tmpPos), endPos, sp[FP_GETH(tmpPos)]);
-#define JMIXER_DEBUG
-#define JMIXER_ADDBUF_TYPE Sint32
-
-
-/* Mono / Linear Interpolation
- */
-#define JMIXER_SAMPLE_TYPE Sint16
-#define JMIXER_INIT        
-#define JMIXER_FUNC \
-    const Sint32 vol = (FP_GETH(tmpVolume) * mixer->globalVol) / 256; \
-    memmove(&tl[0], &tl[1], 4 * sizeof(tl[0])); \
-    tl[4] = (((Sint32) sp[FP_GETH(tmpPos)]) * vol * 3 + tl[3] * 2 + tl[2] + tl[1] + tl[0]) / (256 * 8); *(ap++) += tl[4]; \
-
-#define JMIXER_NAME        jvmMix_Mono_C_FW
-#define JMIXER_NEXT        FP_ADD(tmpPos, tmpDelta);
-#define JMIXER_ENDCOND     (tmpPos.dw < endPos.dw)
-#include "jmixtmpl_c.h"
-
-#define JMIXER_NAME        jvmMix_Mono_C_BW
-#define JMIXER_NEXT        FP_SUB(tmpPos, tmpDelta);
-#define JMIXER_ENDCOND     (tmpPos.dw > endPos.dw)
-#include "jmixtmpl_c.h"
-
-
-#undef JMIXER_SAMPLE_TYPE
-#undef JMIXER_INIT
-#undef JMIXER_FUNC
-
-
-/* Stereo / Linear Interpolation
- */
-#define JMIXER_SAMPLE_TYPE Sint16
-#define JMIXER_INIT
-
-#define JMIXER_FUNC \
-    const Sint32 vol_l = (FP_GETH(tmpVolume) * mixer->globalVol) / 256, \
-                 vol_r = (FP_GETH(tmpVolume) * mixer->globalVol) / 256; \
-    memmove(&tl[0], &tl[1], 4 * sizeof(tl[0])); \
-    memmove(&tr[0], &tr[1], 4 * sizeof(tr[0])); \
-    tl[4] = (((Sint32) sp[FP_GETH(tmpPos)]) * vol_l * 3 + tl[3] * 2 + tl[2] + tl[1] + tl[0]) / (256 * 8); *(ap++) += tl[4]; \
-    tr[4] = (((Sint32) sp[FP_GETH(tmpPos)]) * vol_r * 3 + tr[3] * 2 + tr[2] + tr[1] + tr[0]) / (256 * 8); *(ap++) += tr[4];
-
-#define JMIXER_NAME        jvmMix_Stereo_C_FW
-#define JMIXER_NEXT        FP_ADD(tmpPos, tmpDelta);
-#define JMIXER_ENDCOND     (tmpPos.dw < endPos.dw)
-#include "jmixtmpl_c.h"
-
-#define JMIXER_NAME        jvmMix_Stereo_C_BW
-#define JMIXER_NEXT        FP_SUB(tmpPos, tmpDelta);
-#define JMIXER_ENDCOND     (tmpPos.dw > endPos.dw)
-#include "jmixtmpl_c.h"
-
-
-#undef JMIXER_SAMPLE_TYPE
-#undef JMIXER_INIT
-#undef JMIXER_FUNC
-
-
-/* Post processing functions
- */
-#define JMIXER_CLAMP \
-    if (t < JVM_LIMIT_16_NEG) t = JVM_LIMIT_16_NEG; else    \
-    if (t > JVM_LIMIT_16_POS) t = JVM_LIMIT_16_POS;
-
-#define JMIXER_NAME jvmPostProcess_U8_C
-#define JMIXER_TYPE Uint8
-#define JMIXER_FUNCTION *(sp++) = (t + JVM_ADD_16) >> 8;
-#include "jmix_post_c.h"
-
-
-#define JMIXER_NAME jvmPostProcess_S8_C
-#define JMIXER_TYPE Sint8
-#define JMIXER_FUNCTION *(sp++) = t >> 8;
-#include "jmix_post_c.h"
-
-
-#define JMIXER_NAME jvmPostProcess_U16_C
-#define JMIXER_TYPE Uint16
-#define JMIXER_FUNCTION *(sp++) = t + JVM_ADD_16;
-#include "jmix_post_c.h"
-
-
-#define JMIXER_NAME jvmPostProcess_S16_C
-#define JMIXER_TYPE Sint16
-#define JMIXER_FUNCTION *(sp++) = t;
-#include "jmix_post_c.h"
--- a/jmix_post_c.h	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * miniJSS - Post-processing routine template
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2005-2007 Tecnic Software productions (TNSP)
- */
-
-#ifdef JMIXER_HEADER
-void JMIXER_NAME (JMIXER_ADDBUF_TYPE *, void *, const int);
-#else
-void JMIXER_NAME (JMIXER_ADDBUF_TYPE *addBuffer, void *mixBuffer, const int mixLength)
-{
-    int strideLength = mixLength;
-    JMIXER_ADDBUF_TYPE *ap = addBuffer, t;
-    JMIXER_TYPE *sp = (JMIXER_TYPE *) mixBuffer;
-
-    while (strideLength--)
-    {
-        t = *(ap++);
-        JMIXER_CLAMP
-        JMIXER_FUNCTION
-    }
-}
-#endif
-
-#undef JMIXER_NAME
-#undef JMIXER_TYPE
-#undef JMIXER_FUNCTION
--- a/jmixtmpl_c.h	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * miniJSS - Mixing routine in C, innerloop template
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
- */
-
-int JMIXER_NAME (JSSMixer *mixer, JSSChannel *chn, JMIXER_ADDBUF_TYPE *addBuffer, const int mixLength, const DMFixedPoint endPos)
-#ifdef JMIXER_HEADER
-;
-#else
-{
-    const DMFixedPoint tmpDelta = chn->chDeltaO, tmpDeltaV = chn->chDeltaV;
-    DMFixedPoint tmpPos = chn->chPos,
-                 tmpVolume = chn->chVolume;
-    JMIXER_ADDBUF_TYPE
-        *ap = addBuffer,
-        *tr = chn->chPrevR,
-        *tl = chn->chPrevL;
-    int strideLength = 0;
-
-    JMIXER_SAMPLE_TYPE *sp = (JMIXER_SAMPLE_TYPE *) chn->chData;
-
-    JMIXER_INIT
-
-    if (chn->chVolumeD > 0)
-    {
-        const int tmpLength = chn->chVolumeD < mixLength ? chn->chVolumeD : mixLength;
-        chn->chVolumeD -= tmpLength;
-
-        while (strideLength < tmpLength && JMIXER_ENDCOND)
-        {
-            JMIXER_FUNC
-            JMIXER_DEBUG
-            JMIXER_NEXT
-            FP_ADD(tmpVolume, tmpDeltaV);
-            strideLength++;
-        }
-    }
-    
-    while (strideLength < mixLength && JMIXER_ENDCOND)
-    {
-        JMIXER_FUNC
-        JMIXER_DEBUG
-        JMIXER_NEXT
-        strideLength++;
-    }
-
-    chn->chPos = tmpPos;
-    chn->chVolume = tmpVolume;
-    return strideLength;
-}
-#endif
-
-#undef JMIXER_NAME
-#undef JMIXER_NEXT
-#undef JMIXER_ENDCOND
-
--- a/jss.c	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * miniJSS - General functions
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
- */
-#include "jss.h"
-#include <stdarg.h>
-
-
-/* Memory and error handling functions
- */
-
-BOOL jssWarningIsFatal, jssErrorIsFatal;
-
-#ifndef JSS_LIGHT
-void (*jssError) (int code, const char *filename, int linen, const char *fmt, ...);
-void (*jssWarning) (int code, const char *filename, int linen, const char *fmt, ...);
-#endif
-
-
-void jssDefaultPrint(int code, const char *filename, int linen, const char *fmt)
-{
-    fprintf(stderr, "JSS");
-    if (filename)
-        fprintf(stderr, "[%s:%i]", filename, linen);
-    fprintf(stderr, "%s", fmt);
-    if (code > 0)
-        fprintf(stderr, "(%i)", code);
-    fprintf(stderr, ": ");
-}
-
-
-void jssDefaultError(int code, const char *filename, int linen, const char *fmt, ...)
-{
-    va_list ap;
-    jssDefaultPrint(code, filename, linen, "E");
-
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-}
-
-
-void jssDefaultWarning(int code, const char *filename, int linen, const char *fmt, ...)
-{
-    va_list ap;
-    jssDefaultPrint(code, filename, linen, "W");
-
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-}
-
-
-/* System initialization
- */
-int jssInit(void)
-{
-    // Error handling
-    jssWarningIsFatal = FALSE;
-    jssErrorIsFatal = TRUE;
-
-#ifndef JSS_LIGHT
-    jssError = jssDefaultError;
-    jssWarning = jssDefaultWarning;
-#endif
-
-    // Allocate global tables
-
-    return DMERR_OK;
-}
-
-
-/* System shutdown
- */
-int jssClose(void)
-{
-    return DMERR_OK;
-}
--- a/jss.h	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * miniJSS - Main header file
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
- */
-#ifndef JSS_H
-#define JSS_H
-
-#include "dmlib.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* Locking
- */
-#ifdef JSS_SUP_THREADS
-#    define JSS_LOCK(Q) dmMutexLock((Q)->mutex)
-#    define JSS_UNLOCK(Q) dmMutexUnlock((Q)->mutex)
-#else
-#    warning DANGER! JSS threads support not included!
-#    define JSS_LOCK(Q)
-#    define JSS_UNLOCK(Q)
-#endif
-
-
-// Global settings
-#define jsetNChannels   (64)        // Max number of channels
-#define jsetNotSet      (-1)        // A global "not set" constant
-#define jsetMinVol      (0)
-#define jsetMaxVol      (255)
-
-// Sample instrument flags
-#define jsfLooped       (0x01)      // Is sample looped
-#define jsfBiDi         (0x02)      // Bi-directional loop?
-#define jsf16bit        (0x04)      // 16-bit?
-
-#define JSFSET(a, b)    do { a |= b; } while (0)
-#define JSFUNSET(a, b)  do { a &= (0xff ^ b); } while (0)
-
-// Panning position
-#define jchPanLeft      (0x00)      // Leftmost pan
-#define jchPanMiddle    (0x80)      // Center pan
-#define jchPanRight     (0xff)      // Rightmost pan
-
-// Audio formats
-enum
-{
-    JSS_AUDIO_U8,           // 8-bit formats
-    JSS_AUDIO_S8,
-    JSS_AUDIO_U16,          // 16-bit formats
-    JSS_AUDIO_S16,
-    JSS_AUDIO_U32,          // 32-bit (24+padding)
-    JSS_AUDIO_S32
-} JSS_AUDIO_FMT;
-
-
-enum
-{
-    JSS_AUDIO_MONO = 1,
-    JSS_AUDIO_STEREO = 2
-} JSS_AUDIO_CHANNELS;
-
-
-/* System initialization and shutdown
- */
-int    jssInit(void);       // Initialization. Call before anything else!
-int    jssClose(void);      // Shutdown. Do not call ANY JSS routines after this!
-
-
-/* Error handling routines and related variables
- */
-extern BOOL     jssWarningIsFatal,  // if TRUE, warnings are considered fatal -> function returns
-                jssErrorIsFatal;    // if FALSE, error is considered non-fatal. this may cause strange problems.
-
-#ifndef JSS_LIGHT
-extern void     (*jssError)(int code, const char *filename, int linen, const char *fmt, ...);
-extern void     (*jssWarning)(int code, const char *filename, int linen, const char *fmt, ...);
-#endif
-
-
-/* If JSS_LIGHT is NOT defined, we add code for verbose error-, warning-
- * and debug-messages. Otherwise use a macro stub.
- */
-#ifndef JSS_LIGHT
-#  define JSSERROR(MEVAL, MRET, ...) do { jssError(MEVAL, __FILE__, (int) __LINE__, __VA_ARGS__); if (jssErrorIsFatal) return MRET; } while (0)
-#  define JSSWARNING(MEVAL, MRET, ...) do { jssWarning(MEVAL, __FILE__, (int) __LINE__, __VA_ARGS__); if (jssWarningIsFatal) return MRET; } while (0)
-#  ifdef JSS_DEBUG
-#    define JSSDEBUG(...) do { fprintf(stderr, "[%s:%d]: ", __FILE__, (int) __LINE__); fprintf(stderr, __VA_ARGS__); } while (0)
-#  else
-#    define JSSDEBUG(...) do { } while (0)
-#  endif // NDEBUG
-#else
-#  define JSSERROR(MEVAL, MRET, ...) do { return MRET; } while (0)
-#  define JSSWARNING(MEVAL, MRET, ...) do { } while (0)
-#  define JSSDEBUG(...) do { } while (0)
-#endif // JSS_LIGHT
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // JSS_H
--- a/jssmix.c	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,712 +0,0 @@
-/*
- * miniJSS - Mixing device and channel handling
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
- */
-#include "jssmix.h"
-#include <string.h>
-
-
-#ifdef DM_USE_C
-#define JMIXER_HEADER
-#include "jmix_c_in.c"
-#undef JMIXER_HEADER
-#endif
-
-#undef DM_USE_SIMD
-
-#ifdef DM_USE_SIMD
-#define JMIXER_HEADER
-#include "jmix_mmx_in.c"
-#undef JMIXER_HEADER
-#endif
-
-
-typedef struct
-{
-    int    mixerID;
-    int    outFormat;
-    int    outChannels;
-
-    int    (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
-    int    (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
-    void   (*jvmPostProcess)(JMIXER_ADDBUF_TYPE *, void *, const int);
-} JSSMixingRoutine;
-
-
-/* This table should be sorted from fastest to slowest, e.g. MMX/x86
- * optimized routines first, pure C versions last.
- */
-static JSSMixingRoutine jvmMixRoutines[] =
-{
-#ifdef DM_USE_SIMD
-{ JMIX_MMX, JSS_AUDIO_U8,  JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U8_MMX },
-{ JMIX_MMX, JSS_AUDIO_S8,  JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S8_MMX },
-{ JMIX_MMX, JSS_AUDIO_U8,  JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U8_MMX },
-{ JMIX_MMX, JSS_AUDIO_S8,  JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S8_MMX },
-
-{ JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U16_MMX },
-{ JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S16_MMX },
-{ JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U16_MMX },
-{ JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S16_MMX },
-#endif
-
-#ifdef DM_USE_C
-{ JMIX_C,   JSS_AUDIO_U8,  JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U8_C },
-{ JMIX_C,   JSS_AUDIO_S8,  JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S8_C },
-{ JMIX_C,   JSS_AUDIO_U8,  JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U8_C },
-{ JMIX_C,   JSS_AUDIO_S8,  JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S8_C },
-
-{ JMIX_C,   JSS_AUDIO_U16, JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U16_C },
-{ JMIX_C,   JSS_AUDIO_S16, JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S16_C },
-{ JMIX_C,   JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U16_C },
-{ JMIX_C,   JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S16_C },
-#endif
-};
-
-static const int jvmNMixRoutines = sizeof(jvmMixRoutines) / sizeof(jvmMixRoutines[0]);
-
-
-static int jvmFindMixRoutine(int outFormat, int outChannels, int mixerID)
-{
-    int i;
-    
-    for (i = 0; i < jvmNMixRoutines; i++)
-    {
-        if (jvmMixRoutines[i].outFormat == outFormat &&
-            jvmMixRoutines[i].outChannels == outChannels &&
-            (mixerID == JMIX_AUTO || jvmMixRoutines[i].mixerID == mixerID))
-            return i;
-    }
-    
-    return -1;
-}
-
-
-JSSMixer *jvmInit(const int outFormat, const int outChannels, const int outFreq, const int mixerID)
-{
-    JSSMixer *mixer;
-    int mixerIdx;
-
-    // Check settings
-    if (outChannels < 1)
-    {
-        JSSERROR(DMERR_INVALID_ARGS, NULL,
-        "Invalid number of channels %d\n", outChannels);
-    }
-    
-    if (outFreq < 4000)
-    {
-        JSSERROR(DMERR_INVALID_ARGS, NULL,
-        "Invalid mixing frequency %d\n", outFreq);
-    }
-    
-    /* Select mixing routines:
-     * Here we try to choose the most fitting mixing routines
-     * from the compiled in routines, unless caller is forcing
-     * us to select specific ones.
-     */
-    if (mixerID == JMIX_AUTO)
-    {
-        mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_SSE);
-        if (mixerIdx < 0)
-            mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_MMX);
-        if (mixerIdx < 0)
-            mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_AUTO);
-    }
-    else
-    {
-        mixerIdx = jvmFindMixRoutine(outFormat, outChannels, mixerID);
-    }
-
-    if (mixerIdx < 0)
-    {
-        JSSERROR(DMERR_INVALID_ARGS, NULL,
-        "Could not find mixing routine for outFormat=%d, outChannels=%d, outFreq=%d.\n",
-        outFormat, outChannels, outFreq);
-        return NULL;
-    }
-    
-    // Allocate a mixer device structure
-    mixer = dmMalloc0(sizeof(JSSMixer));
-    if (mixer == NULL)
-    {
-        JSSERROR(DMERR_MALLOC, NULL,
-        "Could not allocate mixing device structure.\n");
-    }
-
-    // Initialize variables
-#ifdef JSS_SUP_THREADS
-    mixer->mutex = dmCreateMutex();
-#endif
-    mixer->outFormat = outFormat;
-    mixer->outFreq = outFreq;
-    mixer->outChannels = outChannels;
-    
-    mixer->jvmMixChannel_FW = jvmMixRoutines[mixerIdx].jvmMixChannel_FW;
-    mixer->jvmMixChannel_BW = jvmMixRoutines[mixerIdx].jvmMixChannel_BW;
-    mixer->jvmPostProcess = jvmMixRoutines[mixerIdx].jvmPostProcess;
-    
-    // Allocate addBuffer
-    mixer->addBufSize = outChannels * outFreq * 2;
-    mixer->addBuffer = dmMalloc(mixer->addBufSize * sizeof(JMIXER_ADDBUF_TYPE));
-    if (mixer->addBuffer == NULL)
-    {
-        JSSERROR(DMERR_MALLOC, NULL,
-        "Could not allocate mixing addition buffer.\n");
-    }
-    
-    return mixer;
-}
-
-
-int jvmClose(JSSMixer * mixer)
-{
-    if (mixer == NULL)
-        return DMERR_NULLPTR;
-
-    // Deallocate resources
-#ifdef JSS_SUP_THREADS
-    dmDestroyMutex(mixer->mutex);
-#endif
-    dmFree(mixer->addBuffer);
-
-    memset(mixer, 0, sizeof(JSSMixer));
-    dmFree(mixer);
-
-    return DMERR_OK;
-}
-
-
-int jvmGetSampleSize(JSSMixer *mixer)
-{
-    int sampSize = 1;
-    assert(mixer);
-    
-    switch (mixer->outChannels)
-    {
-        case JSS_AUDIO_STEREO:
-        case JSS_AUDIO_MONO:
-            sampSize = mixer->outChannels;
-            break;
-        default:
-            JSSERROR(DMERR_INVALID_ARGS, -1,
-            "outChannels=%d not stereo or mono!\n", mixer->outChannels);
-            break;
-    }
-    
-    switch (mixer->outFormat)
-    {
-        case JSS_AUDIO_U16: sampSize *= sizeof(Uint16); break;
-        case JSS_AUDIO_S16: sampSize *= sizeof(Sint16); break;
-        case JSS_AUDIO_U8:  sampSize *= sizeof(Uint8); break;
-        case JSS_AUDIO_S8:  sampSize *= sizeof(Sint8); break;
-        default:
-            JSSERROR(DMERR_INVALID_ARGS, -1,
-            "outFormat=%d is not supported!\n", mixer->outFormat);
-    }
-    
-    return sampSize;
-}
-
-
-int jvmGetSampleRes(JSSMixer *mixer)
-{
-    int sampRes = 0;
-    
-    assert(mixer);
-    
-    switch (mixer->outFormat)
-    {
-        case JSS_AUDIO_U16: case JSS_AUDIO_S16: sampRes = 16; break;
-        case JSS_AUDIO_U8:  case JSS_AUDIO_S8:  sampRes = 8; break;
-        default:
-            JSSERROR(DMERR_INVALID_ARGS, -1,
-            "outFormat=%d is not supported!\n", mixer->outFormat);
-    }
-    
-    return sampRes;
-}
-
-
-static void jvmMixChannel(JSSMixer *mixer, JSSChannel *chn, JMIXER_ADDBUF_TYPE *addBuffer, const int mixLength)
-{
-    int mixDone = mixLength, mixResult;
-    JMIXER_ADDBUF_TYPE *ab = addBuffer;
-    
-    if (!chn->chPlaying || chn->chMute)
-        return;
-
-    DBG("%s(%p, %d)\n", __FUNCTION__, chn, mixLength);
-    
-    while (mixDone > 0)
-    {
-        if (chn->chDirection)
-        {
-            // Channel is playing FORWARDS
-            if (chn->chFlags & jsfLooped)
-            {
-                // Sample is looped
-                if (chn->chFlags & jsfBiDi)
-                {
-                    // Bi-directional loop
-                    if (chn->chPos.dw >= chn->chLoopE.dw)
-                    {
-                        DMFixedPoint end;
-                        FP_ADD_R(end, chn->chLoopE, chn->chLoopE);
-                        FP_SUB_R(chn->chPos, end, chn->chPos);
-                        chn->chDirection = FALSE;
-                    }
-                }
-                else
-                {
-                    // Normal forward loop
-                    if (chn->chPos.dw >= chn->chLoopE.dw)
-                    {
-                        DMFixedPoint diff;
-                        FP_SUB_R(diff, chn->chPos, chn->chLoopE);
-                        FP_ADD_R(chn->chPos, chn->chLoopS, diff);
-                    }
-                }
-            }
-            else
-            {
-                // Normal (non-looped) sample
-                if (chn->chPos.dw >= chn->chSize.dw)
-                {
-                    chn->chPlaying = FALSE;
-                    return;
-                }
-            }
-        }
-        else
-        {
-            // Channel is playing BACKWARDS
-            if (chn->chFlags & jsfLooped)
-            {
-                // Sample is looped
-                if (chn->chFlags & jsfBiDi)
-                {
-                    // Bi-directional loop
-                    if (chn->chPos.dw <= chn->chLoopS.dw)
-                    {
-                        DMFixedPoint start;
-                        FP_ADD_R(start, chn->chLoopS, chn->chLoopS);
-                        FP_SUB_R(chn->chPos, start, chn->chPos);
-                        chn->chDirection = TRUE;
-                    }
-                }
-                else
-                {
-                    // Normal forward loop
-                    if (chn->chPos.dw <= chn->chLoopS.dw)
-                    {
-                        DMFixedPoint diff;
-                        FP_SUB_R(diff, chn->chLoopE, chn->chLoopS);
-                        FP_ADD(chn->chPos, diff);
-                    }
-                }
-            }
-            else
-            {
-                // Normal (non-looped) sample
-                if (chn->chPos.dw <= 0)
-                {
-                    chn->chPlaying = FALSE;
-                    return;
-                }
-            }
-        }
-        
-        // Call the mixing innerloop functions
-        if (chn->chDirection)
-        {
-            DBG("MIX_FW[%p : %d : ", ab, mixDone);
-            if (chn->chFlags & jsfLooped)
-            {
-                DBG("%d (%x)] {loop}\n", FP_GETH(chn->chLoopE), FP_GETH(chn->chLoopE));
-                mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn,
-                    ab, mixDone, chn->chLoopE);
-            }
-            else
-            {
-                DBG("%d (%x)]\n", FP_GETH(chn->chSize), FP_GETH(chn->chSize));
-                mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn,
-                    ab, mixDone, chn->chSize);
-            }
-        }
-        else
-        {
-            DBG("MIX_BW[%p : %d : ", ab, mixDone);
-            if (chn->chFlags & jsfLooped)
-            {
-                DBG("%d (%x)] {loop}\n", chn->chLoopS, chn->chLoopS);
-                mixResult = mixer->jvmMixChannel_BW(mixer, chn,
-                    ab, mixDone, chn->chLoopS);
-            }
-            else
-            {
-                static const DMFixedPoint zero = { 0 };
-                DBG("%d (%x)]\n", 0, 0);
-                mixResult = mixer->jvmMixChannel_BW(mixer, chn,
-                    ab, mixDone, zero);
-            }
-        }
-        
-        mixDone -= mixResult;
-        ab += mixResult * mixer->outChannels;
-    }
-
-#ifdef JSS_DEBUG
-    if (mixDone < 0)
-        JSSWARNING(DMERR_BOUNDS,, "mixDone < 0 in mixing logic loop.\n");
-#endif
-}
-
-
-void jvmRenderAudio(JSSMixer *mixer, void *mixBuffer, const int mixLength)
-{
-    int i, blockLength, mixLeft;
-    JMIXER_ADDBUF_TYPE *ab;
-
-    JSS_LOCK(mixer);
-
-    assert(mixer != NULL);
-    assert(mixBuffer != NULL);
-    assert(mixLength > 0);
-    assert(mixLength * mixer->outChannels <= mixer->addBufSize);
-
-    // Clear mixer->addBuffer
-    memset(mixer->addBuffer, 0, mixLength * mixer->outChannels * sizeof(JMIXER_ADDBUF_TYPE));
-    
-    ab = mixer->addBuffer;
-    mixLeft = mixLength;
-    while (mixLeft > 0)
-    {
-        // Check for callbacks
-        blockLength = mixLeft;
-
-        if (mixer->cbFunction)
-        {
-            if (mixer->cbCounter <= 0)
-            {
-                mixer->cbFunction(mixer, mixer->cbData);
-                mixer->cbCounter = mixer->cbFreq;
-            }
-
-            if (mixer->cbCounter < blockLength)
-                blockLength = mixer->cbCounter;
-        }
-
-        // Do mixing
-        for (i = 0; i < jsetNChannels; i++)
-        {
-            JSSChannel *chn = &(mixer->channels[i]);
-            if (chn->chPlaying && !chn->chMute)
-                jvmMixChannel(mixer, chn, ab, blockLength);
-        }
-
-/*
-            if (chn->chPlaying)
-            {
-                if (!chn->chMute)
-                    jvmMixChannel(mixer, chn, ab, blockLength);
-                else
-                    jvmAdvanceChannel(mixer, chn, blockLength);
-            }
-*/
-        
-        ab += blockLength * mixer->outChannels;
-        mixLeft -= blockLength;
-        mixer->cbCounter -= blockLength;
-    }
-
-    // Post-process
-    mixer->jvmPostProcess(mixer->addBuffer, mixBuffer, mixLength * mixer->outChannels);
-
-    JSS_UNLOCK(mixer);
-}
-
-
-int jvmSetCallback(JSSMixer * mixer, void (*cbFunction) (void *, void *), void *cbData)
-{
-    assert(mixer);
-
-    if (cbFunction == NULL)
-        JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "NULL pointer given as cbFunction");
-
-    JSS_LOCK(mixer);
-
-    mixer->cbFunction = cbFunction;
-    mixer->cbData = cbData;
-
-    JSS_UNLOCK(mixer);
-
-    return DMERR_OK;
-}
-
-
-void jvmRemoveCallback(JSSMixer * mixer)
-{
-    assert(mixer);
-
-    JSS_LOCK(mixer);
-
-    mixer->cbFunction = NULL;
-    mixer->cbData = NULL;
-    mixer->cbFreq = mixer->cbCounter = 0;
-
-    JSS_UNLOCK(mixer);
-}
-
-
-int jvmSetCallbackFreq(JSSMixer * mixer, const int cbFreq)
-{
-    assert(mixer);
-
-    JSS_LOCK(mixer);
-    
-    mixer->cbFreq = cbFreq;
-    mixer->cbCounter = 0;
-
-//fprintf(stderr, "set(outFreq = %d, cbFreq = %d) = %d\n", mixer->outFreq, cbFreq, mixer->cbFreq);
-    
-    JSS_UNLOCK(mixer);
-    return DMERR_OK;
-}
-
-
-/* Channel manipulation routines
- */
-void jvmPlay(JSSMixer * mixer, const int channel)
-{
-    JSS_LOCK(mixer);
-    mixer->channels[channel].chPlaying = TRUE;
-    JSS_UNLOCK(mixer);
-}
-
-
-void jvmStop(JSSMixer * mixer, const int channel)
-{
-    JSS_LOCK(mixer);
-    mixer->channels[channel].chPlaying = FALSE;
-    JSS_UNLOCK(mixer);
-}
-
-
-void jvmReset(JSSMixer * mixer, const int channel)
-{
-    JSSChannel *c;
-    
-    JSS_LOCK(mixer);
-    c = &mixer->channels[channel];
-
-    c->chDirection = TRUE;
-    c->chPos.dw    = c->chDeltaO.dw = 0;
-
-    JSS_UNLOCK(mixer);
-}
-
-
-void jvmSetSample(JSSMixer * mixer, const int channel,
-          void *data, const Sint32 size, const Sint32 loopS,
-          const Sint32 loopE, const int flags)
-{
-    JSSChannel *c;
-    
-    JSS_LOCK(mixer);
-    c = &mixer->channels[channel];
-    
-    FP_SETHL(c->chSize, size, 0);
-    FP_SETHL(c->chLoopS, loopS, 0);
-    FP_SETHL(c->chLoopE, loopE, 0);
-    c->chData      = data;
-    c->chFlags     = flags;
-    c->chDirection = TRUE;
-    c->chPos.dw    = c->chDeltaO.dw = 0;
-    
-    JSS_UNLOCK(mixer);
-}
-
-
-void jvmSetFreq(JSSMixer * mixer, const int channel, const int freq)
-{
-    JSS_LOCK(mixer);
-
-    mixer->channels[channel].chFreq = freq;
-    
-    if (mixer->outFreq > 0)
-    {
-        DMFixedPoint a, b;
-        FP_SETHL(a, freq, 0);
-        FP_CONV(b, mixer->outFreq);
-        FP_DIV_R(mixer->channels[channel].chDeltaO, a, b);
-    }
-    else
-    {
-        FP_SET(mixer->channels[channel].chDeltaO, 0);
-    }
-
-    JSS_UNLOCK(mixer);
-}
-
-
-int jvmGetFreq(JSSMixer * mixer, const int channel)
-{
-    int tmp;
-
-    JSS_LOCK(mixer);
-    tmp = mixer->channels[channel].chFreq;
-    JSS_UNLOCK(mixer);
-
-    return tmp;
-}
-
-
-void jvmSetVolume(JSSMixer * mixer, const int channel, const int volume)
-{
-    JSS_LOCK(mixer);
-    FP_SETHL(mixer->channels[channel].chVolume, volume, 0);
-    mixer->channels[channel].chVolumeD = 0;
-    mixer->channels[channel].chDeltaV.dw = 0;
-    JSS_UNLOCK(mixer);
-}
-
-
-void jvmSetVolumeRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len)
-{
-    int tmp;
-    DMFixedPoint a, b;
-    JSS_LOCK(mixer);
-    FP_SETHL(mixer->channels[channel].chVolume, start, 0);
-
-    tmp = mixer->channels[channel].chVolumeD = 
-        len > 0 ? ((mixer->outFreq * len) / 1000) : mixer->cbFreq;
-
-    FP_SETHL(a, (end - start), 0);
-    FP_CONV(b, tmp);
-    FP_DIV_R(mixer->channels[channel].chDeltaV, a, b);
-
-    JSS_UNLOCK(mixer);
-}
-
-
-int jvmGetVolume(JSSMixer * mixer, const int channel)
-{
-    int tmp;
-
-    JSS_LOCK(mixer);
-    tmp = FP_GETH(mixer->channels[channel].chVolume);
-    JSS_UNLOCK(mixer);
-
-    return tmp;
-}
-
-
-void jvmSetPos(JSSMixer * mixer, const int channel, const Sint32 pos)
-{
-    JSS_LOCK(mixer);
-    FP_SETHL(mixer->channels[channel].chPos, pos, 0);
-    JSS_UNLOCK(mixer);
-}
-
-
-Sint32 jvmGetPos(JSSMixer * mixer, const int channel)
-{
-    Sint32 tmp;
-
-    JSS_LOCK(mixer);
-    tmp = FP_GETH(mixer->channels[channel].chPos);
-    JSS_UNLOCK(mixer);
-
-    return tmp;
-}
-
-
-void jvmSetPan(JSSMixer * mixer, const int channel, const int panning)
-{
-    JSS_LOCK(mixer);
-    FP_SETHL(mixer->channels[channel].chPanning, panning, 0);
-    mixer->channels[channel].chPanningD = 0;
-    mixer->channels[channel].chDeltaP.dw = 0;
-    JSS_UNLOCK(mixer);
-}
-
-
-void jvmSetPanRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len)
-{
-    int tmp;
-    DMFixedPoint a, b;
-    JSS_LOCK(mixer);
-
-    FP_SETHL(mixer->channels[channel].chPanning, start, 0);
-
-    tmp = mixer->channels[channel].chPanningD = 
-        len > 0 ? ((mixer->outFreq * len) / 1000) : mixer->cbFreq;
-
-    FP_SETHL(a, (end - start), 0);
-    FP_CONV(b, tmp);
-    FP_DIV_R(mixer->channels[channel].chDeltaP, a, b);
-
-    JSS_UNLOCK(mixer);
-}
-
-
-int jvmGetPan(JSSMixer * mixer, const int channel)
-{
-    int tmp;
-
-    JSS_LOCK(mixer);
-    tmp = FP_GETH(mixer->channels[channel].chPanning);
-    JSS_UNLOCK(mixer);
-
-    return tmp;
-}
-
-
-void jvmMute(JSSMixer * mixer, const int channel, const BOOL mute)
-{
-    JSS_LOCK(mixer);
-    mixer->channels[channel].chMute = mute;
-    JSS_UNLOCK(mixer);
-}
-
-
-BOOL jvmGetMute(JSSMixer * mixer, const int channel)
-{
-    BOOL tmp;
-
-    JSS_LOCK(mixer);
-    tmp = mixer->channels[channel].chMute;
-    JSS_UNLOCK(mixer);
-
-    return tmp;
-}
-
-
-void jvmClear(JSSMixer * mixer, const int channel)
-{
-    JSS_LOCK(mixer);
-    memset(&mixer->channels[channel], 0, sizeof(JSSChannel));
-    JSS_UNLOCK(mixer);
-}
-
-
-void jvmSetGlobalVol(JSSMixer * mixer, const int volume)
-{
-    JSS_LOCK(mixer);
-    mixer->globalVol = volume;
-    JSS_UNLOCK(mixer);
-}
-
-
-int jvmGetGlobalVol(JSSMixer * mixer)
-{
-    int tmp;
-
-    JSS_LOCK(mixer);
-    tmp = mixer->globalVol;
-    JSS_UNLOCK(mixer);
-
-    return tmp;
-}
--- a/jssmix.h	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-/*
- * miniJSS - Mixing device and channel handling
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
- */
-#ifndef JSSMIX_H
-#define JSSMIX_H
-#include "jss.h"
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//#define DBG(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
-#define DBG(...) do { /* stub */ } while (0)
-
-
-// Mixing limits
-#define JVM_LIMIT_16_POS    (32767)
-#define JVM_LIMIT_16_NEG    (-32767)
-#define JVM_ADD_16          (32768)
-
-
-#define JMIXER_ADDBUF_TYPE Sint32
-
-
-// A channel data structure
-typedef struct
-{
-    DMFixedPoint
-            chSize,         // Length of sample in UNITS
-            chLoopS,        // Loop start in UNITS
-            chLoopE,        // Loop end in UNITS
-
-            chPos,          // Current position in sample, 32:32 fixpoint
-            chDeltaO,       // Delta in 32:32 UNSIGNED! (chDirection)
-            chVolume,       // Volume
-            chDeltaV,
-            chPanning,      // Panning
-            chDeltaP;
-    int     chVolumeD,
-            chPanningD;
-
-    int     chFreq;         // Frequency of sampel in Hz
-
-    void    *chData;        // Pointer to data
-
-    BOOL    chPlaying,      // TRUE = playing
-            chMute,         // TRUE = muted
-            chDirection;    // TRUE = playing forwards, FALSE = backwards
-
-    int     chFlags;        // Flags
-
-    JMIXER_ADDBUF_TYPE chPrevR[5], chPrevL[5];
-} JSSChannel;
-
-
-// Channel table
-typedef int JSSChannelTable[jsetNChannels];
-
-
-// Virtual software mixer "device" structure
-typedef struct _JSSMixer JSSMixer;
-
-struct _JSSMixer
-{
-    int             outFormat,
-                    outChannels,
-                    outFreq,
-                    globalVol;
-    JSSChannel      channels[jsetNChannels];
-    
-    int             addBufSize;
-    JMIXER_ADDBUF_TYPE *addBuffer;
-
-    // Callback handling
-    int             cbFreq, cbCounter;
-    void            *cbData;
-    void            (*cbFunction)(void *, void *);
-
-    // Mixing routine pointers
-    int    (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
-    int    (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
-    void   (*jvmPostProcess)(JMIXER_ADDBUF_TYPE *, void *, const int);
-
-    // Device locking
-#ifdef JSS_SUP_THREADS
-    DMMutex *mutex;
-#endif
-};
-
-
-/* Enums
- */
-enum {
-    JMIX_AUTO = 0,
-    JMIX_C,
-    JMIX_MMX,
-    JMIX_SSE
-};
-
-
-/* Main routines
- */
-JSSMixer *  jvmInit(const int outFormat, const int outChannels, const int outFreq, const int mixerID);
-int         jvmClose(JSSMixer *mixer);
-
-int         jvmSetCallback(JSSMixer *mixer, void (*cbFunction)(void *, void *), void *cbData);
-void        jvmRemoveCallback(JSSMixer *mixer);
-int         jvmSetCallbackFreq(JSSMixer *mixer, const int cbFreq);
-Sint32      jvmGetLastCBBufPos(JSSMixer *mixer, const int cbHandle);
-Sint32      jvmGetNextCBBufPos(JSSMixer *mixer, const int cbHandle);
-
-int         jvmGetSampleSize(JSSMixer *mixer);
-int         jvmGetSampleRes(JSSMixer *mixer);
-
-void        jvmRenderAudio(JSSMixer *mixer, void *mixBuffer, const int mixLength);
-
-
-/*
-int         jvmAddCallback(JSSMixer *mixer, void (*cbFunction)(JSSMixer *, void *));
-int         jvmRemoveCallback(JSSMixer *mixer, int cbHandle);
-int         jvmSetCallbackFreq(JSSMixer *mixer, int cbHandle, int cbFreq);
-
-int         jvmAllocChannels(JSSMixer *mixer, int nChannels);
-int         jvmFreeChannels(JSSMixer *mixer, int iHandle);
-int         jvmGetChannelTable(JSSMixer *mixer, int iHandle, JSSChannelTable *pTable);
-*/
-
-
-/* Channel manipulation routines
- */
-void        jvmPlay(JSSMixer *mixer, const int channel);
-void        jvmStop(JSSMixer *mixer, const int channel);
-void        jvmReset(JSSMixer * mixer, const int channel);
-
-void        jvmSetSample(JSSMixer *mixer, const int channel,
-            void *data, const Sint32 size, const Sint32 loopS,
-            const Sint32 loopE, const int flags);
-
-void        jvmSetFreq(JSSMixer *mixer, const int channel, const int freq);
-int         jvmGetFreq(JSSMixer *mixer, const int channel);
-
-void        jvmSetVolume(JSSMixer *mixer, const int channel, const int volume);
-void        jvmSetVolumeRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len);
-int         jvmGetVolume(JSSMixer *mixer, const int channel);
-
-void        jvmSetPos(JSSMixer *mixer, const int channel, const Sint32 pos);
-Sint32      jvmGetPos(JSSMixer *mixer, const int channel);
-
-void        jvmSetPan(JSSMixer *mixer, const int channel, const int panning);
-void        jvmSetPanRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len);
-int         jvmGetPan(JSSMixer *mixer, const int channel);
-
-void        jvmMute(JSSMixer *mixer, const int channel, const BOOL mute);
-BOOL        jvmGetMute(JSSMixer *mixer, const int channel);
-
-void        jvmClear(JSSMixer *mixer, const int channel);
-void        jvmClearChannels(JSSMixer *mixer);
-
-void        jvmSetGlobalVol(JSSMixer *mixer, const int volume);
-int         jvmGetGlobalVol(JSSMixer *mixer);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // JSSMIX_H
--- a/jssmod.c	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,447 +0,0 @@
-/*
- * miniJSS - Module structure and handling routines
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
- */
-#include "jssmod.h"
-#include <string.h>
-
-
-#ifndef JSS_LIGHT
-/* Take given data until maxlen reached, make a string
- */
-char *jssASCIItoStr(char * sdata, const char endByte, const size_t maxLen)
-{
-    size_t i, k;
-    char *res;
-
-    for (i = 0; sdata[i] && i < maxLen; i++);
-
-    res = (char *) dmMalloc(i + 1);
-    if (res == NULL)
-        return NULL;
-
-    for (k = 0; sdata[k] != endByte && k < i; k++)
-        res[k] = sdata[k];
-
-    res[k] = 0;
-
-    return res;
-}
-
-
-/* Encodes a given 8-bit sample
- */
-BOOL jssEncodeSample8(Uint8 * data, const size_t len, const int ops)
-{
-    size_t count = len;
-    Sint8 t, value = 0;
-    
-    while (count--)
-    {
-        t = *data;
-        
-        if (ops & jsampFlipSign)
-            t ^= 0x80;
-
-        if (ops & jsampDelta)
-        {
-            int n = t - value;
-            value = t;
-            t = n;
-        }
-        
-        *(data++) = t;
-    }
-
-    return TRUE;
-}
-
-
-/* Decodes a given 16-bit sample
- */
-BOOL jssEncodeSample16(Uint16 * data, const size_t len, const int ops)
-{
-    // "Split" the 16-bit samples into 8-bit halves
-    if (ops & jsampSplit)
-    {
-        // Allocate temporary processing buffer
-        size_t count, bufSize = len * sizeof(Sint16);
-        Uint8 *bp1, *bp2;
-        Sint16 *sdata, *tmpBuf = dmMalloc(bufSize);
-        if (tmpBuf == NULL) return FALSE;
-
-        sdata = tmpBuf;
-        bp1 = (Uint8 *) data;
-        bp2 = bp1 + len;
-        count = len;
-
-        while (count--)
-        {
-            Sint16 t = (*sdata++);
-            *bp1++ = t >> 8;
-            *bp2++ = t & 0xff;
-        }
-        
-        memcpy(data, tmpBuf, bufSize);
-        dmFree(tmpBuf);
-        
-        return jssEncodeSample8((Uint8 *) data, bufSize, ops);
-    }
-    else
-    {
-        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
-        size_t count = len;
-        
-        while (count--)
-        {
-            if (ops & jsampSwapEndianess)
-            {
-                p = *sdata;
-                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
-            } else
-                t = *sdata;
-            
-            if (ops & jsampDelta)
-            {
-                int n = t - value;
-                value = t;
-                t = n;
-            }
-            
-            if (ops & jsampFlipSign)
-                t ^= 0x8000;
-            
-            *(sdata++) = t;
-        }
-    }
-    return TRUE;
-}
-
-#endif
-
-
-/* Decodes a given 8-bit sample
- */
-BOOL jssDecodeSample8(Uint8 * data, const size_t len, const int ops)
-{
-    size_t count = len;
-    Sint8 t, value = 0;
-    
-    while (count--)
-    {
-        t = *data;
-        
-        if (ops & jsampDelta)
-            t = value = t + value;
-        
-        if (ops & jsampFlipSign)
-            t ^= 0x80;
-        
-        *(data++) = t;
-    }
-    return TRUE;
-}
-
-
-/* Decodes a given 16-bit sample
- */
-BOOL jssDecodeSample16(Uint16 * data, const size_t len, const int ops)
-{
-    if (ops & jsampSplit)
-    {
-        size_t count, bufSize = len * sizeof(Uint16);
-        Uint8 *bp1, *bp2;
-        Sint16 *tmpBuf, *sdata;
-        
-        if (!jssDecodeSample8((Uint8 *) data, bufSize, ops))
-            return FALSE;
-        
-        tmpBuf = dmMalloc(bufSize);
-        if (tmpBuf == NULL) return FALSE;
-        memcpy(tmpBuf, data, bufSize);
-        
-        sdata = (Sint16 *) data;
-        bp1 = (Uint8 *) tmpBuf;
-        bp2 = bp1 + len;
-        count = len;
-        while (count--)
-        {
-            *sdata++ = (*bp1++ << 8) | (*bp2++ & 0xff);
-        }
-        
-        dmFree(tmpBuf);
-    }
-    else
-    {
-        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
-        size_t count = len;
-        while (count--)
-        {
-            if (ops & jsampSwapEndianess)
-            {
-                p = *sdata;
-                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
-            } else
-                t = *sdata;
-            
-            if (ops & jsampDelta)
-                t = value = t + value;
-            
-            if (ops & jsampFlipSign)
-                t ^= 0x8000;
-            
-            *(sdata++) = t;
-        }
-    }
-    return TRUE;
-}
-
-
-/* Convert sample data from U8 to S16
- */
-int jssConvertSampleTo16(void **dst, void * src, const size_t len)
-{
-    size_t count = len;
-    Uint8 *in = (Uint8 *) src;
-    Sint16 *out;
-
-    *dst = out = dmMalloc(sizeof(Sint16) * len);
-    if (out == NULL)
-        return DMERR_MALLOC;
-    
-    while (count--)
-    {
-        *(out++) = (*(in++) * 256) - 32768;
-    }
-    
-    return DMERR_OK;
-}
-
-/* Converts the given module in preparation for playing it.
- * This involves sample format conversion (8 to 16 bit, etc.)
- *
- * NOTICE! The converted module can only be saved in JSSMOD
- * format, but this is not recommended.
- */
-int jssConvertModuleForPlaying(JSSModule *module)
-{
-    int i;
-    if (module == NULL)
-        return DMERR_NULLPTR;
-
-    // Convert instruments
-    for (i = 0; i < module->ninstruments; i++)
-    {
-        JSSInstrument *inst = module->instruments[i];
-        if (inst != NULL && inst->data != NULL)
-        {
-            int res;
-            void *data = NULL;
-
-            if (inst->flags & jsf16bit)
-                continue;
-
-            if ((res = jssConvertSampleTo16(&data, inst->data, inst->size)) != DMERR_OK)
-                return res;
-
-            inst->flags |= jsf16bit;
-            dmFree(inst->data);
-            inst->data = data;
-        }
-    }
-    
-    return DMERR_OK;
-}
-
-
-/* Allocates a new module structure or returns errorvalue if failed.
- * Memory is allocated only for the basic structure. Sample- and pattern
- * areas must be allocated separately with appropriate routines.
- */
-JSSModule *jssAllocateModule(void)
-{
-    int i;
-    JSSModule *module;
-
-    // Allocate module structure
-    module = dmMalloc0(sizeof(JSSModule));
-    if (module == NULL)
-        return NULL;
-
-    // Initialize structure
-    for (i = 0; i < jsetNChannels; i++)
-        module->defPanning[i] = jchPanMiddle;
-
-    for (i = 0; i < jsetMaxOrders; i++)
-        module->orderList[i] = jsetOrderEnd;
-
-    // Allocate mutex
-#ifdef JSS_SUP_THREADS
-    module->mutex = dmCreateMutex();
-#endif
-
-    return module;
-}
-
-
-/* Frees a given module structure, freeing all memory areas
- * that were allocated for it (including patterns, samples, etc.)
- */
-int jssFreeModule(JSSModule * module)
-{
-    int i;
-
-    if (module == NULL)
-        return DMERR_NULLPTR;
-    
-    // Free strings
-#ifndef JSS_LIGHT
-    dmFree(module->moduleName);
-    dmFree(module->trackerName);
-#endif
-
-    // Free patterns
-    for (i = 0; i < module->npatterns; i++)
-    {
-        if (module->patterns[i])
-        {
-            JSSPattern *pat = module->patterns[i];
-            dmFree(pat->data);
-            dmFree(pat);
-            module->patterns[i] = NULL;
-        }
-    }
-
-    // Free the "empty" pattern
-    JSSPattern *pat = module->patterns[jsetMaxPatterns];
-    if (pat != NULL)
-    {
-        dmFree(pat->data);
-        dmFree(pat);
-        module->patterns[i] = NULL;
-    }
-
-    // Free instruments
-    for (i = 0; i < module->ninstruments; i++)
-    {
-        if (module->instruments[i])
-        {
-            JSSInstrument *inst = module->instruments[i];
-#ifndef JSS_LIGHT
-            dmFree(inst->desc);
-#endif
-            dmFree(inst->data);
-            dmFree(inst);
-            module->instruments[i] = NULL;
-        }
-    }
-
-    // Free extended instruments
-    for (i = 0; i < module->nextInstruments; i++)
-    {
-        if (module->extInstruments[i])
-        {
-            JSSExtInstrument *ext = module->extInstruments[i];
-#ifndef JSS_LIGHT
-            dmFree(ext->desc);
-#endif
-            dmFree(ext);
-            module->extInstruments[i] = NULL;
-        }
-    }
-
-    // Free mutex
-#ifdef JSS_SUP_THREADS
-    dmDestroyMutex(module->mutex);
-#endif
-    
-    // Free the module structure
-    memset(module, 0, sizeof(JSSModule));
-    dmFree(module);
-
-    return DMERR_OK;
-}
-
-
-/* Allocates and initializes a internal pattern structure.
- */
-JSSPattern *jssAllocatePattern(int nrows, int nchannels)
-{
-    int i, j;
-    JSSPattern *res;
-    JSSNote *pnote;
-
-    // Check arguments
-    if (nrows <= 0 || nchannels <= 0)
-        JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid nrows=%i or nchannels=%i.\n", nrows, nchannels);
-
-    // Allocate a pattern structure
-    res = dmMalloc0(sizeof(JSSPattern));
-    if (res == NULL)
-        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern structure.\n");
-
-    // Allocate notedata
-    res->data = dmCalloc(nrows * nchannels, sizeof(JSSNote));
-    if (res->data == NULL)
-    {
-        dmFree(res);
-        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern data (nrows=%i, nchannels=%i).\n", nrows,
-             nchannels);
-    }
-
-    // Initialize
-    res->nrows = nrows;
-    res->nchannels = nchannels;
-
-    pnote = res->data;
-    for (j = 0; j < nrows; j++)
-    {
-        for (i = 0; i < nchannels; i++)
-        {
-            pnote->note = pnote->instrument = pnote->volume =
-                pnote->effect = pnote->param = jsetNotSet;
-
-            pnote++;
-        }
-    }
-
-    // OK, return pointer to struct
-    return res;
-}
-
-
-/* Allocates and initializes internal "normal" instrument structure.
- */
-JSSInstrument *jssAllocateInstrument(void)
-{
-    JSSInstrument *res;
-
-    // Allocate a instrument structure
-    res = dmMalloc0(sizeof(JSSInstrument));
-    if (res == NULL)
-        return NULL;
-
-    return res;
-}
-
-
-/* Allocates and initializes "extended" instrument structure.
- */
-JSSExtInstrument *jssAllocateExtInstrument(void)
-{
-    int i;
-    JSSExtInstrument *res;
-
-    // Allocate a instrument structure
-    res = dmMalloc0(sizeof(JSSExtInstrument));
-    if (res == NULL)
-        return NULL;
-
-    for (i = 0; i < jsetNNotes; i++)
-    {
-        res->sNumForNotes[i] = jsetNotSet;
-    }
-    
-    return res;
-}
--- a/jssmod.h	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,315 +0,0 @@
-/*
- * miniJSS - Module structure and handling routines
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2009 Tecnic Software productions (TNSP)
- */
-#ifndef JSSMOD_H
-#define JSSMOD_H
-
-#include "jss.h"
-#include "dmres.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Max data size definitions
-#define jsetMaxRows         (256)   // Max number of rows
-#define jsetMaxPatterns     (256)   // Max patterns
-#define jsetMaxTracks       (jsetMaxPatterns * jsetMaxPatterns) // Max tracks
-#define jsetMaxInstruments  (512)
-#define jsetMaxOrders       (260)
-#define jsetNNotes          (11 * 12)
-#define jsetMinNote         (0)     // Smallest note number
-#define jsetMaxNote         (jsetNNotes - 1)
-#define jsetMaxEnvPoints    (32)    // Max number of envelope points
-
-
-// Instrument envelope flags
-#define jenvfUsed           (0x01)  // Envelope is used
-#define jenvfSustain        (0x02)  // Envelope has a sustain point (XM) or has sustain loop (IT)
-#define jenvfLooped         (0x04)  // Envelope is looped
-
-// Instrument vibrato waveform types
-#define jvibSine            (0)
-#define jvibRamp            (1)
-#define jvibSquare          (2)
-#define jvibRandom          (3)
-
-// General stuff
-#define jsetNoteOff         (-2)
-#define jsetOrderEnd        (-1)
-#define jsetOrderSkip       (-2)
-
-
-// General module flags
-#define jmdfAmigaPeriods    (0x0001)    // Use non-linear periods (Amiga)
-#define jmdfAmigaLimits     (0x0002)    // Use Amiga-styled valuelimits
-#define jmdfStereo          (0x0008)    // Use stereo output, if possible
-
-#define jmdfFT2Replay       (0x0010)    // Use FT2 replaying bugs/features
-#define jmdfST300Slides     (0x0020)    // Use Scream Tracker 3.00 slides
-#define jmdfByteLStart      (0x0040)    // LStart is in BYTES instead of WORDS (MOD only)
-
-
-// Module format types
-enum JMDT
-{
-    jmdtMOD = 1,
-    jmdtS3M,
-    jmdtXM,
-    jmdtIT
-};
-
-
-// Bits for sample conversion routines
-#define jsampDelta          (0x01)
-#define jsampFlipSign       (0x02)
-#define jsampSwapEndianess  (0x04)
-#define jsampSplit          (0x08)
-
-
-// Internal instrument structure
-typedef struct
-{
-    int size,               // Length in units
-        loopS,              // Loop start position in units
-        loopE;              // Loop end position in units
-    int volume,             // Volume [jsetMinVol...jsetMaxVol]
-        flags,              // Flags - see jss.h jsfXXXX
-        C4BaseSpeed,        // C4BaseSpeed
-        ERelNote,           // Extended: Relative note value
-        EFineTune,          // Extended: Fine-tune value
-        EPanning;           // Extended: Panning
-#ifndef JSS_LIGHT
-    char *desc;
-#endif
-    void *data;             // Sample data
-
-    BOOL hasData;
-    int convFlags;
-} JSSInstrument;
-
-
-// Envelope point structure
-typedef struct
-{
-    int frame, value;
-} JSSEnvelopePoint;
-
-
-// Envelope structure
-typedef struct
-{
-    int flags,
-        npoints,
-        sustain,
-        loopS,
-        loopE;
-    JSSEnvelopePoint points[jsetMaxEnvPoints];
-} JSSEnvelope;
-
-
-// Extended instrument
-typedef struct
-{
-#ifndef JSS_LIGHT
-    char *desc;
-#endif
-    int nsamples, sNumForNotes[jsetNNotes];
-    JSSEnvelope volumeEnv, panningEnv;
-    int vibratoType,
-        vibratoSweep,
-        vibratoDepth,
-        vibratoRate,
-        fadeOut;
-} JSSExtInstrument;
-
-
-// Internal pattern structures
-typedef struct
-{
-    int note,
-        instrument,
-        volume,
-        effect,
-        param;
-} JSSNote;
-
-
-typedef struct
-{
-    int nrows, nchannels;
-    JSSNote *data;
-} JSSPattern;
-
-
-// Module structure
-typedef struct
-{
-    int     moduleType;     // Type of the module
-    char    *moduleName;    // Title/name
-    char    *trackerName;   // Tracker software name
-    int     defSpeed,       // Initial values
-            defTempo,
-            defFlags,
-            defRestartPos,
-            intVersion,     // Format's internal version
-            nchannels,
-            ninstruments,
-            nextInstruments,
-            npatterns,
-            norders;
-
-    int     defPanning[jsetNChannels];
-    int     orderList[jsetMaxOrders];
-    JSSPattern       *patterns[jsetMaxPatterns + 1];
-    JSSInstrument    *instruments[jsetMaxInstruments];
-    JSSExtInstrument *extInstruments[jsetMaxInstruments];
-
-#ifdef JSS_SUP_THREADS
-    DMMutex *mutex;
-#endif
-
-} JSSModule;
-
-
-#ifdef JSS_SUP_JSSMOD
-
-#define JSSMOD_VERSION    (0x10)
-
-enum
-{
-    PATMODE_RAW_HORIZ = 1,
-    PATMODE_COMP_HORIZ,
-    PATMODE_RAW_VERT,
-    PATMODE_COMP_VERT,
-    PATMODE_RAW_ELEM,
-    PATMODE_LAST    
-};
-
-/* JSSMOD typedefs
- */
-typedef struct
-{
-    char  idMagic[2];     // "JM"
-    Uint8 idVersion;      // 0x10 for 1.0, etc.
-    Sint16
-        norders,            // Number of orders in orderlist
-        npatterns,          // Number of patterns
-        nchannels,          // Number of channels
-        nextInstruments,    // Number of extended instruments
-        ninstruments,       // Number of sample-instruments
-        defFlags,           // Flags field: see jmdf* flags
-        intVersion,         // Internal version, format dependant
-        defRestartPos,      // Default restart position in orderlist
-        defSpeed,           // Default speed (ticks/row)
-        defTempo,           // Default tempo (BPM)
-        patMode;            // Pattern data format mode
-
-    /*
-    - After this, norders long orders table will follow, of type:
-      Sint16 orderList[norders];
-    
-    - Pattern data, format depends on patMode.
-    
-    - Extended instruments (*)
-    
-    - Sample instrument headers (*)
-    
-    - Sample data (format depends) (*)
-    
-    (*) Items are optional and may have been omitted. Fields in the 
-        module and other headers are used to indicate if these items exist.
-    */
-} JSSMODHeader;
-
-
-typedef struct
-{
-    Uint16 frame, value;
-} JSSMODEnvelopePoint;
-
-
-typedef struct
-{
-    int flags,
-        npoints,
-        sustain,
-        loopS,
-        loopE;
-    JSSMODEnvelopePoint points[jsetMaxEnvPoints];
-} JSSMODEnvelope;
-
-
-typedef struct
-{
-    Sint16 nsamples;
-    Uint8 sNumForNotes[jsetNNotes];
-    JSSMODEnvelope volumeEnv, panningEnv;
-    Sint16 vibratoType,
-            vibratoSweep,
-            vibratoDepth,
-            vibratoRate;
-    int     fadeOut;
-} JSSMODExtInstrument;
-
-
-typedef struct
-{
-    int     size,           // Length in units
-            loopS,          // Loop start position in units
-            loopE;          // Loop end position in units
-    Sint16 volume,         // Volume [jsetMinVol...jsetMaxVol]
-            flags,          // Flags - see jss.h jsfXXXX
-            C4BaseSpeed,    // C4BaseSpeed
-            ERelNote,       // Extended: Relative note value
-            EFineTune,      // Extended: Fine-tune value
-            EPanning;       // Extended: Panning
-    Uint8 convFlags,      // Conversion flags .. jsampXXXX
-            hasData;        // != 0 if there is sample data
-} JSSMODInstrument;
-
-
-#define COMP_NOTE           (0x01)
-#define COMP_INSTRUMENT     (0x02)
-#define COMP_VOLUME         (0x04)
-#define COMP_EFFECT         (0x08)
-#define COMP_PARAM          (0x10)
-#define COMP_ALL (COMP_NOTE | COMP_INSTRUMENT | COMP_VOLUME | COMP_EFFECT | COMP_PARAM)
-
-
-typedef struct
-{
-    size_t nrows, size;
-} JSSMODPattern;
-
-#endif
-
-#ifndef JSS_LIGHT
-char*               jssASCIItoStr(char *, const char, const size_t);
-BOOL                jssEncodeSample8(Uint8 *, const size_t, const int);
-BOOL                jssEncodeSample16(Uint16 *, const size_t, const int);
-#endif
-BOOL                jssDecodeSample8(Uint8 *, const size_t, const int);
-BOOL                jssDecodeSample16(Uint16 *, const size_t, const int);
-int                 jssConvertModuleForPlaying(JSSModule *module);
-JSSModule *         jssAllocateModule(void);
-int                 jssFreeModule(JSSModule *);
-JSSPattern *        jssAllocatePattern(int, int);
-JSSInstrument *     jssAllocateInstrument(void);
-JSSExtInstrument *  jssAllocateExtInstrument(void);
-
-#ifdef JSS_SUP_XM
-int        jssLoadXM(DMResource *, JSSModule **);
-#endif
-
-#ifdef JSS_SUP_JSSMOD
-int        jssLoadJSSMOD(Uint8 *, const size_t, JSSModule **);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // JSSMOD_H
--- a/jssplr.c	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1579 +0,0 @@
-/*
- * miniJSS - Module playing routines
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
- */
-#include "jssplr.h"
-
-// FIXME!! FIX ME!
-#include <math.h>
-
-/* Miscellaneous tables
- */
-static const Uint8 jmpSineTab[32] =
-{
-      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
-};
-
-
-static const Sint16 jmpXMAmigaPeriodTab[13 * 8] = {
-    907, 900, 894, 887, 881, 875, 868, 862, 856, 850, 844, 838,
-    832, 826, 820, 814, 808, 802, 796, 791, 785, 779, 774, 768,
-    762, 757, 752, 746, 741, 736, 730, 725, 720, 715, 709, 704,
-    699, 694, 689, 684, 678, 675, 670, 665, 660, 655, 651, 646,
-    640, 636, 632, 628, 623, 619, 614, 610, 604, 601, 597, 592,
-    588, 584, 580, 575, 570, 567, 563, 559, 555, 551, 547, 543,
-    538, 535, 532, 528, 524, 520, 516, 513, 508, 505, 502, 498,
-    494, 491, 487, 484, 480, 477, 474, 470, 467, 463, 460, 457,
-
-    453, 450, 447, 443, 440, 437, 434, 431
-};
-
-
-#define jmpNMODEffectTable (36)
-static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-
-/* Helper functions
- */
-static int jmpNoteToAmigaPeriod(int note, int finetune)
-{
-    int tmp = dmClamp(note + finetune + 8, 0, 103);
-    return jmpXMAmigaPeriodTab[tmp];
-}
-
-
-static int jmpGetPeriodFromNote(JSSPlayer *mp, int note, int finetune)
-{
-    int res;
-
-    if (JMPGETMODFLAGS(mp, jmdfAmigaPeriods))
-    {
-        int mfinetune = finetune / 16,
-            mnote = (note % 12) * 8,
-            moctave = note / 12,
-            period1, period2;
-        
-        period1 = jmpNoteToAmigaPeriod(mnote, mfinetune);
-        
-        if (finetune < 0)
-        {
-            mfinetune--;
-            finetune = -finetune;
-        } else
-            mfinetune++;
-        
-        period2 = jmpNoteToAmigaPeriod(mnote, mfinetune);
-        
-        mfinetune = finetune & 15;
-        period1 *= (16 - mfinetune);
-        period2 *= mfinetune;
-        
-        res = ((period1 + period2) * 2) >> moctave;
-        
-//fprintf(stderr, "jmpGetAmigaPeriod(%d, %d) = %d\n", note, finetune, res);
-    }
-    else
-    {
-//fprintf(stderr, "jmpGetLinearPeriod(%d, %d) = %d\n", note, finetune, res);
-        //res = ((120 - note) << 6) - (finetune / 2);
-        res = 7680 - (note * 64) - (finetune / 2);
-        if (res < 1) res = 1;
-    }
-
-    return res;
-}
-
-
-static void jmpCSetPitch(JSSPlayer *mp, JSSPlayerChannel *chn, int value)
-{
-    if (value > 0)
-    {
-        if (JMPGETMODFLAGS(mp, jmdfAmigaPeriods))
-        {
-            // Frequency = 8363*1712 / Period
-            chn->cfreq = chn->freq = 14317456 / value;
-        }
-        else
-        {
-            // Frequency = Frequency = 8363*2^((6*12*16*4 - Period) / (12*16*4))
-            chn->cfreq = chn->freq = 8363.0f * pow(2.0f, (4608.0f - (double) value) / 768.0f);
-        }
-
-        JMPSETNDFLAGS(cdfNewFreq);
-    }
-}
-
-
-static void jmpCSetVolume(JSSPlayerChannel *chn, int channel, int volume)
-{
-    (void) channel;
-
-    chn->volume = dmClamp(volume, mpMinVol, mpMaxVol);
-    JMPSETNDFLAGS(cdfNewVolume);
-}
-
-
-static BOOL jmpExecEnvelope(JSSEnvelope *env, JSSPlayerEnvelope *pe, BOOL keyOff)
-{
-    int point;
-
-    if (!pe->exec)
-        return FALSE;
-    
-    // Find current point, if not last point
-    for (point = 0; point < env->npoints - 1; point++)
-    {
-        if (pe->frame < env->points[point + 1].frame)
-            break;
-    }
-
-    if (env->flags & jenvfLooped && pe->frame >= env->points[env->loopE].frame)
-    {
-        point = env->loopS;
-        pe->frame = env->points[env->loopS].frame;
-        pe->value = env->points[point].value;
-    }
-
-    // Check for last point
-    if (pe->frame >= env->points[env->npoints - 1].frame)
-    {
-        point = env->npoints - 1;
-        pe->exec = FALSE;
-        pe->value = env->points[point].value;
-    }
-    else
-    {
-        // Linearly interpolate the value between current and next point
-        JSSEnvelopePoint
-            *ep1 = &env->points[point],
-            *ep2 = &env->points[point + 1];
-
-        int delta = ep2->frame - ep1->frame;
-        if (delta > 0)
-            pe->value = ep1->value + ((ep2->value - ep1->value) * (pe->frame - ep1->frame)) / delta;
-        else
-            pe->value = ep1->value;
-    }
-
-    if (pe->exec)
-    {
-        // The frame counter IS processed even if the envelope is not!
-        if ((env->flags & jenvfSustain) && point == env->sustain &&
-            env->points[point].frame == env->points[env->sustain].frame)
-        {
-            if (keyOff)
-                pe->frame++;
-        } else
-            pe->frame++;
-    }
-        
-    return TRUE;
-}
-
-
-static void jmpProcessExtInstrument(JSSPlayerChannel *chn, int channel)
-{
-    JSSExtInstrument *inst = chn->extInstrument;
-    (void) channel;
-
-    // Get the instrument for envelope data
-    if (!inst) return;
-
-    // Process the autovibrato
-    /*
-       FIXME fix me FIX me!!! todo.
-     */
-
-    if (inst->volumeEnv.flags & jenvfUsed)
-    {
-        // Process the instrument volume fadeout
-        if (chn->keyOff && chn->fadeOutVol > 0 && inst->fadeOut > 0)
-        {
-            int tmp = chn->fadeOutVol - inst->fadeOut;
-            if (tmp < 0) tmp = 0;
-            chn->fadeOutVol = tmp;
-
-            JMPSETNDFLAGS(cdfNewVolume);
-        }
-
-        // Execute the volume envelope
-        if (jmpExecEnvelope(&inst->volumeEnv, &chn->volumeEnv, chn->keyOff))
-            JMPSETNDFLAGS(cdfNewVolume);
-    }
-    else
-    {
-        // If the envelope is not used, set max volume
-        chn->volumeEnv.value = mpMaxVol;
-        chn->fadeOutVol = chn->keyOff ? 0 : mpMaxFadeoutVol;
-        JMPSETNDFLAGS(cdfNewVolume);
-    }
-
-    if (inst->panningEnv.flags & jenvfUsed)
-    {
-        // Process the panning envelope
-        if (jmpExecEnvelope(&inst->panningEnv, &chn->panningEnv, chn->keyOff))
-            JMPSETNDFLAGS(cdfNewPanPos);
-    }
-    else
-    {
-        // If the envelope is not used, set center panning
-        if (chn->panningEnv.value != mpPanCenter)
-        {
-            chn->panningEnv.value = mpPanCenter;
-            JMPSETNDFLAGS(cdfNewPanPos);
-        }
-    }
-}
-
-
-/*
- * The player
- */
-JSSPlayer *jmpInit(JSSMixer *pDevice)
-{
-    JSSPlayer *mp;
-    
-    // Allocate a player structure
-    mp = dmMalloc0(sizeof(JSSPlayer));
-    if (mp == NULL)
-        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate memory for player structure.\n");
-
-    // Set variables
-    mp->device = pDevice;
-
-#ifdef JSS_SUP_THREADS
-    mp->mutex = dmCreateMutex();
-#endif
-    
-    return mp;
-}
-
-
-int jmpClose(JSSPlayer * mp)
-{
-    if (mp == NULL)
-        return DMERR_NULLPTR;
-
-    // Stop player
-    jmpStop(mp);
-
-    // Deallocate resources
-#ifdef JSS_SUP_THREADS
-    dmDestroyMutex(mp->mutex);
-#endif
-
-    // Clear structure
-    memset(mp, 0, sizeof(JSSPlayer));
-    dmFree(mp);
-
-    return DMERR_OK;
-}
-
-
-/* Reset the envelopes for given channel.
- */
-static void jmpResetEnvelope(JSSPlayerEnvelope *env)
-{
-    env->frame = env->value = 0;
-    env->exec = TRUE;
-}
-
-
-/* Clear module player structure
- */
-void jmpClearChannel(JSSPlayerChannel *chn)
-{
-    memset(chn, 0, sizeof(JSSPlayerChannel));
-
-    chn->note            = jsetNotSet;
-    chn->ninstrument     = jsetNotSet;
-    chn->nextInstrument  = jsetNotSet;
-    chn->panning         = mpPanCenter;
-    chn->panningEnv.value          = mpPanCenter;
-}
-
-
-void jmpClearPlayer(JSSPlayer * mp)
-{
-    int i;
-    assert(mp != NULL);
-    JSS_LOCK(mp);
-
-    // Initialize general variables
-    mp->patternDelay      = 0;
-    mp->newRowSet         = FALSE;
-    mp->newOrderSet       = FALSE;
-    mp->tick              = jsetNotSet;
-    mp->row               = 0;
-    mp->lastPatLoopRow    = 0;
-
-    // Initialize channel data
-    for (i = 0; i < jsetNChannels; i++)
-        jmpClearChannel(&mp->channels[i]);
-    
-    JSS_UNLOCK(mp);
-}
-
-
-/* Set module
- */
-void jmpSetModule(JSSPlayer * mp, JSSModule * module)
-{
-    assert(mp != NULL);
-    JSS_LOCK(mp);
-
-    jmpStop(mp);
-    jmpClearPlayer(mp);
-
-    mp->module = module;
-
-    JSS_UNLOCK(mp);
-}
-
-
-/* Stop playing
- */
-void jmpStop(JSSPlayer * mp)
-{
-    assert(mp != NULL);
-    JSS_LOCK(mp);
-
-    if (mp->isPlaying)
-    {
-        jvmRemoveCallback(mp->device);
-        mp->isPlaying = FALSE;
-    }
-    
-    JSS_UNLOCK(mp);
-}
-
-
-/* Resume playing
- */
-void jmpResume(JSSPlayer * mp)
-{
-    assert(mp != NULL);
-    JSS_LOCK(mp);
-
-    if (!mp->isPlaying)
-    {
-        int result = jvmSetCallback(mp->device, jmpExec, (void *) mp);
-        if (result != DMERR_OK)
-            JSSERROR(result,, "Could not initialize callback for player.\n");
-
-        mp->isPlaying = TRUE;
-    }
-
-    JSS_UNLOCK(mp);
-}
-
-
-/* Sets new order using given value as reference.
- * Jumps over skip-points and invalid values, loops
- * to first order if enabled.
- */
-static void jmpSetNewOrder(JSSPlayer * mp, int order)
-{
-    BOOL orderOK;
-    int pattern;
-    
-    pattern = jsetOrderEnd;
-    mp->order = jsetNotSet;
-    orderOK = FALSE;
-
-    while (!orderOK)
-    {
-        if (order < 0 || order >= mp->module->norders)
-        {
-            jmpStop(mp);
-            orderOK = TRUE;
-        }
-        else
-        {
-            pattern = mp->module->orderList[order];
-            if (pattern == jsetOrderSkip)
-            {
-                order++;
-            }
-            else
-            if (pattern >= mp->module->npatterns || pattern == jsetOrderEnd)
-            {
-                jmpStop(mp);
-                orderOK = TRUE;
-            }
-            else
-            {
-                // All OK
-                orderOK = TRUE;
-                mp->pattern = mp->module->patterns[pattern];
-                mp->npattern = pattern;
-                mp->order = order;
-            }
-        }
-    }
-}
-
-
-/* Set new tempo-value of the player.
- */
-static void jmpSetTempo(JSSPlayer * mp, int tempo)
-{
-    assert(mp != NULL);
-    JSS_LOCK(mp);
-    assert(mp->device != NULL);
-
-    mp->tempo = tempo;
-    jvmSetCallbackFreq(mp->device, (mp->device->outFreq * 5) / (tempo * 2));
-    JSS_UNLOCK(mp);
-}
-
-
-static void jmpClearChannels(JSSPlayer * mp)
-{
-    int i;
-    assert(mp != NULL);
-    JSS_LOCK(mp);
-    assert(mp->device != NULL);
-    assert(mp->module != NULL);
-
-    for (i = 0; i < mp->module->nchannels; i++)
-        jvmStop(mp->device, i);
-
-    // Initialize channel data
-    for (i = 0; i < jsetNChannels; i++)
-        jmpClearChannel(&mp->channels[i]);
-
-    JSS_UNLOCK(mp);
-}
-
-
-/* Starts playing module from a given ORDER.
- */
-static int jmpPlayStart(JSSPlayer *mp)
-{
-    int result;
-
-    mp->speed = mp->module->defSpeed;
-    jmpSetTempo(mp, mp->module->defTempo);
-
-    result = jvmSetCallback(mp->device, jmpExec, (void *) mp);
-    if (result != DMERR_OK)
-    {
-        JSSERROR(result, result, "Could not initialize callback for player.\n");
-    }
-
-    mp->isPlaying = TRUE;
-    return DMERR_OK;
-}
-
-
-int jmpChangeOrder(JSSPlayer *mp, int order)
-{
-    assert(mp != NULL);
-
-    JSS_LOCK(mp);
-    assert(mp->module != NULL);
-
-    jmpClearChannels(mp);
-    jmpClearPlayer(mp);
-
-    jmpSetNewOrder(mp, order);
-    if (mp->order == jsetNotSet)
-    {
-        JSS_UNLOCK(mp);
-        JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
-             "Could not start playing from given order #%i\n", order);
-    }
-
-    JSS_UNLOCK(mp);
-    return DMERR_OK;
-}
-
-
-int jmpPlayOrder(JSSPlayer * mp, int order)
-{
-    int result;
-    assert(mp != NULL);
-
-    JSS_LOCK(mp);
-    assert(mp->module != NULL);
-
-    // Stop if already playing
-    jmpStop(mp);
-    jmpClearChannels(mp);
-    jmpClearPlayer(mp);
-
-    // Check starting order
-    jmpSetNewOrder(mp, order);
-    if (mp->order == jsetNotSet)
-    {
-        JSS_UNLOCK(mp);
-        JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
-             "Could not start playing from given order #%i\n", order);
-    }
-
-    if ((result = jmpPlayStart(mp)) != DMERR_OK)
-    {
-        JSS_UNLOCK(mp);
-        return result;
-    }
-
-    JSS_UNLOCK(mp);
-    return DMERR_OK;
-}
-
-
-/* Play given pattern
- */
-int jmpPlayPattern(JSSPlayer * mp, int pattern)
-{
-    int result;
-    assert(mp != NULL);
-    JSS_LOCK(mp);
-    assert(mp->module != NULL);
-
-    // Stop if already playing
-    jmpStop(mp);
-    jmpClearPlayer(mp);
-
-    mp->npattern = pattern;
-
-    if ((result = jmpPlayStart(mp)) != DMERR_OK)
-    {
-        JSS_UNLOCK(mp);
-        return result;
-    }
-
-    JSS_UNLOCK(mp);
-    return DMERR_OK;
-}
-
-
-/* Set volume for given module channel.
- */
-static void jmpSetVolume(JSSPlayerChannel * chn, int channel, int volume)
-{
-    (void) channel;
-
-    chn->volume = chn->cvolume = dmClamp(volume, mpMinVol, mpMaxVol);
-    JMPSETNDFLAGS(cdfNewVolume);
-}
-
-#define jmpChangeVolume(Q, Z, X) jmpSetVolume(Q, Z, chn->volume + (X))
-
-
-/* Change the pitch of given channel by ADelta.
- */
-static void jmpChangePitch(JSSPlayerChannel *chn, int channel, int delta)
-{
-    int value;
-    (void) channel;
-
-    // Calculate new pitch and check it
-    value = chn->pitch + delta;
-    if (value < 0)
-        value = 0;
-
-    chn->pitch = value;
-    JMPSETNDFLAGS(cdfNewPitch);
-}
-
-
-/* Do a note portamento (pitch slide) effect for given module channel.
- */
-static void jmpDoPortamento(JSSPlayerChannel * chn, int channel)
-{
-    (void) channel;
-
-    // Check for zero parameter
-    if (chn->iLastPortaToNoteParam == 0)
-    {
-        JMPSETNDFLAGS(cdfNewPitch);
-        return;
-    }
-
-    /* Slide the pitch of channel to the destination value
-     * with speed of iLastPortaToNoteParam[] * 4 and stop when it equals.
-     */
-    if (chn->pitch < chn->iLastPortaToNotePitch)
-    {
-        // Increase pitch UP
-        jmpChangePitch(chn, channel, chn->iLastPortaToNoteParam * 4);
-        if (chn->pitch > chn->iLastPortaToNotePitch)
-            chn->pitch = chn->iLastPortaToNotePitch;
-    }
-    else
-    if (chn->pitch > chn->iLastPortaToNotePitch)
-    {
-        // Decrease pitch DOWN
-        jmpChangePitch(chn, channel, -(chn->iLastPortaToNoteParam * 4));
-        if (chn->pitch < chn->iLastPortaToNotePitch)
-            chn->pitch = chn->iLastPortaToNotePitch;
-    }
-}
-
-
-/* Do a tremolo effect for given module channel.
- */
-static void jmpDoTremolo(JSSPlayerChannel *chn, int channel)
-{
-    (void) channel;
-    
-    if (chn->tremolo.depth != 0 && chn->tremolo.speed != 0)
-    {
-        int delta, tmp = chn->tremolo.pos & 31;
-
-        switch (chn->tremolo.wc & 3)
-        {
-            case 0:
-                delta = jmpSineTab[tmp];
-                break;
-            case 1:
-                tmp <<= 3;
-                delta = (chn->tremolo.pos < 0) ? 255 - tmp : tmp;
-                break;
-            case 2:
-                delta = 255;
-                break;
-            case 3:
-                delta = jmpSineTab[tmp];
-                break;
-        }
-
-        delta = (delta * chn->tremolo.depth) >> 6;
-        jmpCSetVolume(chn, channel, chn->cvolume + (chn->tremolo.pos >= 0 ? delta : -delta));
-
-        chn->tremolo.pos += chn->tremolo.speed;
-        if (chn->tremolo.pos > 31)
-            chn->tremolo.pos -= 64;
-    }
-}
-
-
-/* Do a vibrato effect for given module channel.
- */
-static void jmpDoVibrato(JSSPlayerChannel *chn, int channel)
-{
-    (void) channel;
-
-    if (chn->vibrato.depth != 0 && chn->vibrato.speed != 0)
-    {
-        int delta, tmp = chn->vibrato.pos & 31;
-
-        switch (chn->vibrato.wc & 3)
-        {
-            case 0:
-                delta = jmpSineTab[tmp];
-                break;
-            case 1:
-                tmp <<= 3;
-                delta = (chn->vibrato.pos < 0) ? 255 - tmp : tmp;
-                break;
-            case 2:
-                delta = 255;
-                break;
-            case 3:
-                delta = jmpSineTab[tmp];
-                break;
-        }
-
-        delta = ((delta * chn->vibrato.depth) >> 7) << 2;
-        chn->freq = chn->cfreq + (chn->vibrato.pos >= 0 ? delta : -delta);
-        JMPSETNDFLAGS(cdfNewFreq);
-
-        chn->vibrato.pos += chn->vibrato.speed;
-        if (chn->vibrato.pos > 31)
-            chn->vibrato.pos -= 64;
-    }
-}
-
-
-/* Do a volume slide effect for given module channel.
- */
-static void jmpDoVolumeSlide(JSSPlayerChannel * chn, int channel, int param)
-{
-    int paramX, paramY;
-
-    JMPMAKEPARAM(param, paramX, paramY)
-
-    if (paramY == 0)
-        jmpChangeVolume(chn, channel, paramX);
-    if (paramX == 0)
-        jmpChangeVolume(chn, channel, -paramY);
-}
-
-static void jmpTriggerNote(JSSPlayer * mp, JSSPlayerChannel *chn, BOOL newExtInstrument);
-
-
-static void jmpDoMultiRetrigNote(JSSPlayer *mp, JSSPlayerChannel *chn, int channel)
-{
-    if (chn->lastMultiRetrigParamY != 0 &&
-       (mp->tick % chn->lastMultiRetrigParamY) == 0)
-    {
-        BOOL change = TRUE;
-        int volume = chn->volume;
-        switch (chn->lastMultiRetrigParamX)
-        {
-            case 0x1: volume -= 1; break;
-            case 0x2: volume -= 2; break;
-            case 0x3: volume -= 4; break;
-            case 0x4: volume -= 8; break;
-            case 0x5: volume -= 16; break;
-            case 0x6: volume  = (volume * 2) / 3; break;
-            case 0x7: volume /= 2; break;
-
-            case 0x9: volume += 1; break;
-            case 0xA: volume += 2; break;
-            case 0xB: volume += 4; break;
-            case 0xC: volume += 8; break;
-            case 0xD: volume += 16; break;
-            case 0xE: volume  = (volume * 3) / 2; break;
-            case 0xF: volume *= 2; break;
-            default: change = FALSE;
-        }
-        jmpTriggerNote(mp, chn, FALSE);
-        if (change)
-            jmpSetVolume(chn, channel, volume);
-    }
-}
-
-
-/* Execute a pattern loop effect/command for given module channel.
- *
- * This routine works for most of the supported formats, as they
- * use the 'standard' implementation ascending from MOD. However,
- * here is included also a slightly kludgy implementation of the
- * FT2 patloop bug.
- */
-static void jmpDoPatternLoop(JSSPlayer * mp, JSSPlayerChannel *chn, int channel, int paramY)
-{
-    (void) channel;
-
-    // Check what we need to do
-    if (paramY > 0)
-    {
-        // SBx/E6x loops 'x' times
-        if (chn->iPatLoopCount == 1)
-            chn->iPatLoopCount = 0;
-        else
-        {
-            // Check if we need to set the count
-            if (chn->iPatLoopCount == 0)
-                chn->iPatLoopCount = paramY + 1;
-
-            // Loop to specified row
-            chn->iPatLoopCount--;
-            mp->newRow = chn->iPatLoopRow;
-            mp->newRowSet = TRUE;
-        }
-    }
-    else
-    {
-        // SB0/E60 sets the loop start point
-        chn->iPatLoopRow = mp->row;
-
-        // This is here because of the infamous FT2 patloop bug
-        mp->lastPatLoopRow = mp->row;
-    }
-}
-
-
-/* Do arpeggio effect
- */
-static void jmpDoArpeggio(JSSPlayer * mp, JSSPlayerChannel *chn, int channel, int paramY, int paramX)
-{
-    JSSInstrument *inst = chn->instrument;
-    (void) channel;
-
-    if (inst != NULL)
-    {
-        int tmp = chn->note;
-        if (tmp == jsetNotSet || tmp == jsetNoteOff)
-            return;
-
-        switch (mp->tick & 3)
-        {
-            case 1:
-                tmp += paramX;
-                break;
-            case 2:
-                tmp += paramY;
-                break;
-        }
-
-        tmp = dmClamp(tmp + inst->ERelNote, 0, 119);
-        jmpCSetPitch(mp, chn, jmpGetPeriodFromNote(mp, tmp, inst->EFineTune));
-    }
-}
-
-
-/* Trigger a new note on the given channel.
- * Separate function used from various places where note
- * triggering is needed (retrig, multi-retrig, etc.)
- */
-static void jmpTriggerNote(JSSPlayer * mp, JSSPlayerChannel *chn, BOOL newExtInstrument)
-{
-    if (chn->nextInstrument >= 0 &&
-        chn->nextInstrument < mp->module->nextInstruments &&
-        mp->module->extInstruments[chn->nextInstrument] != NULL)
-    {
-        chn->extInstrument = mp->module->extInstruments[chn->nextInstrument];
-    }
-    else
-    {
-        chn->extInstrument = NULL;
-        chn->instrument    = NULL;
-        chn->ninstrument   = jsetNotSet;
-    }
-
-    if (chn->extInstrument != NULL)
-    {
-        int tmp = chn->extInstrument->sNumForNotes[chn->note];
-
-        if (tmp >= 0 && tmp < mp->module->ninstruments &&
-            mp->module->instruments[tmp] != NULL)
-        {
-            if (chn->ninstrument != tmp)
-                JMPSETNDFLAGS(cdfNewInstr);
-            else
-                JMPSETNDFLAGS(cdfPlay);
-
-            chn->ninstrument = tmp;
-            chn->instrument  = mp->module->instruments[tmp];
-
-            if (newExtInstrument)
-            {
-                chn->volume  = chn->instrument->volume;
-                chn->panning = chn->instrument->EPanning;
-                JMPSETNDFLAGS(cdfNewPanPos | cdfNewVolume);
-            }
-        }
-    }
-
-    if (chn->instrument != NULL)
-    {
-        int tmp;
-        JSSInstrument *inst = chn->instrument;
-
-        // Save old pitch for later use
-        chn->oldPitch = chn->pitch;
-
-        chn->position = 0;
-
-        // Compute new pitch
-        tmp = dmClamp(chn->note + inst->ERelNote, 0, 119);
-        chn->pitch = jmpGetPeriodFromNote(mp, tmp, inst->EFineTune);
-        JMPSETNDFLAGS(cdfNewPitch | cdfPlay | cdfNewPos);
-    }
-    else
-    {
-        chn->volume = 0;
-        chn->panning = jchPanMiddle;
-        JMPSETNDFLAGS(cdfStop | cdfNewPanPos | cdfNewVolume);
-    }
-}
-
-
-/*
- * Process a new pattern row
- */
-static void jmpProcessNewRow(JSSPlayer * mp, int channel)
-{
-    JSSNote *currNote;
-    BOOL newNote = FALSE, newExtInstrument = FALSE, volumePortaSet = FALSE;
-    char effect;
-    int param, paramX, paramY;
-    JSSPlayerChannel *chn = &(mp->channels[channel]);
-
-    JMPGETNOTE(currNote, mp->row, channel);
-
-    // Check for a new note/keyoff here
-    if (currNote->note == jsetNoteOff)
-        chn->keyOff = TRUE;
-    else
-    if (currNote->note >= 0 && currNote->note <= 96)
-    {
-        newNote = TRUE;
-        chn->oldNote = chn->note;
-        chn->note = currNote->note;
-        chn->keyOff = FALSE;
-    }
-
-    // Check for new instrument
-    if (currNote->instrument != jsetNotSet)
-    {
-        /* Envelopes and ext.instrument fadeout are initialized always if
-         * new instrument is set, even if the instrument does not exist.
-         */
-        jmpResetEnvelope(&chn->volumeEnv);
-        jmpResetEnvelope(&chn->panningEnv);
-        chn->keyOff = FALSE;
-        chn->fadeOutVol = mpMaxFadeoutVol;
-
-        JMPSETNDFLAGS(cdfNewPanPos | cdfPlay | cdfNewVolume);
-
-        // We save the instrument number here for later use
-        chn->nextInstrument = currNote->instrument;
-        newExtInstrument = TRUE;
-    }
-
-    if (newNote)
-    {
-        jmpTriggerNote(mp, chn, newExtInstrument);
-    }
-
-    // Process the volume column
-    JMPMAKEPARAM(currNote->volume, paramX, paramY);
-
-    switch (paramX)
-    {
-        case 0x0:
-        case 0x1:
-        case 0x2:
-        case 0x3:
-        case 0x4:
-            jmpSetVolume(chn, channel, currNote->volume);
-            break;
-
-        case 0x7:        // Dx = Fine Volumeslide Down : IMPL.VERIFIED
-            jmpChangeVolume(chn, channel, -paramY);
-            break;
-
-        case 0x8:        // Ux = Fine Volumeslide Up : IMPL.VERIFIED
-            jmpChangeVolume(chn, channel, paramY);
-            break;
-
-        case 0x9:        // Sx = Set vibrato speed : IMPL.VERIFIED
-            chn->vibrato.speed = paramY;
-            break;
-
-        case 0xa:        // Vx = Vibrato : IMPL.VERIFIED
-            if (paramY)
-                chn->vibrato.depth = paramY;
-            break;
-
-        case 0xe:        // Mx = Porta To Note : IMPL.VERIFIED
-            if (paramY)
-                chn->iLastPortaToNoteParam = paramY * 16;
-
-            if (currNote->note != jsetNotSet && currNote->note != jsetNoteOff)
-            {
-                chn->lastPortaToNoteNote = chn->note;
-                chn->iLastPortaToNotePitch = chn->pitch;
-                chn->pitch = chn->oldPitch;
-                chn->note = chn->oldNote;
-                JMPUNSETNDFLAGS(cdfNewPitch | cdfPlay);
-                volumePortaSet = TRUE;
-            }
-            break;
-    }
-
-    // ...And finally process the Normal effects
-    if (currNote->effect == jsetNotSet)
-        return;
-
-    param = currNote->param;
-    JMPMAKEPARAM(param, paramX, paramY);
-    JMPGETEFFECT(effect, currNote->effect);
-
-    switch (effect)
-    {
-        case '0':        // 0xy = Arpeggio
-            jmpDoArpeggio(mp, chn, channel, paramX, paramY);
-            break;
-
-        case 'W':        // Used widely in demo-music as MIDAS Sound System sync-command
-        case 'Q':        // SoundTracker/OpenCP: Qxx = Set LP filter resonance
-        case 'Z':        // SoundTracker/OpenCP: Zxx = Set LP filter cutoff freq
-            break;
-
-        case '1':
-        case '2':        // 1xy = Portamento Up, 2xy = Portamento Down : IMPL.VERIFIED
-            if (param)
-                chn->iLastPortaParam = param;
-            break;
-
-        case '3':        // 3xy = Porta To Note
-            if (volumePortaSet)
-                break;
-
-            if (param)
-                chn->iLastPortaToNoteParam = param;
-
-            if (currNote->note != jsetNotSet && currNote->note != jsetNoteOff)
-            {
-                chn->lastPortaToNoteNote = chn->note;
-                chn->iLastPortaToNotePitch = chn->pitch;
-                chn->pitch = chn->oldPitch;
-                chn->note = chn->oldNote;
-                JMPUNSETNDFLAGS(cdfNewPitch | cdfPlay);
-            }
-            break;
-
-        case '4':        // 4xy = Vibrato : IMPL.VERIFIED
-            if (paramX)
-                chn->vibrato.speed = paramX;
-
-            if (paramY)
-                chn->vibrato.depth = paramY;
-
-            if ((chn->vibrato.wc & 4) == 0)
-                chn->vibrato.pos = 0;
-            break;
-
-        case '5':        // 5xy = Portamento + Volume Slide
-        case '6':        // 6xy = Vibrato + Volume slide
-            if (param)
-                chn->iLastVolSlideParam = param;
-            break;
-
-        case '7':        // 7xy = Tremolo
-            if (paramX)
-                chn->tremolo.speed = paramX;
-
-            if (paramY)
-                chn->tremolo.depth = paramY;
-
-            if ((chn->tremolo.wc & 4) == 0)
-                chn->tremolo.pos = 0;
-            break;
-
-        case '8':        // 8xx = Set Panning
-            chn->panning = param;
-            JMPSETNDFLAGS(cdfNewPanPos);
-            break;
-
-        case '9':        // 9xx = Set Sample Offset : IMPL.VERIFIED
-            if (param != 0)
-                chn->lastSampleOffsetParam = param;
-            if (chn->newDataFlags & cdfNewPitch && chn->instrument != NULL)
-            {
-                int pos = chn->lastSampleOffsetParam * 0x100,
-                    end = (chn->instrument->flags & jsfLooped) ?
-                          chn->instrument->loopE : chn->instrument->size;
-                if (pos <= end)
-                {
-                    chn->position = pos;
-                    JMPSETNDFLAGS(cdfNewPos);
-                }
-                else
-                {
-                    JMPSETNDFLAGS(cdfStop);
-                }
-            }
-            break;
-
-        case 'A':        // Axy = Volume Slide : IMPL.VERIFIED
-            if (param)
-                chn->iLastVolSlideParam = param;
-            break;
-
-        case 'B':        // Bxx = Pattern Jump : IMPL.VERIFIED
-            mp->newOrder = param;
-            mp->newOrderSet = TRUE;
-            mp->jumpFlag = TRUE;
-            mp->lastPatLoopRow = 0;
-            break;
-
-        case 'C':        // Cxx = Set Volume : IMPL.VERIFIED
-            jmpSetVolume(chn, channel, param);
-            break;
-
-        case 'D':        // Dxx = Pattern Break : IMPL.VERIFIED
-            // Compute the new row
-            mp->newRow = (paramX * 10) + paramY;
-            if (mp->newRow >= mp->pattern->nrows)
-                mp->newRow = 0;
-
-            mp->newRowSet = TRUE;
-
-            // Now we do some tricky tests
-            if (!mp->breakFlag && !mp->jumpFlag)
-            {
-                mp->newOrder = mp->order + 1;
-                mp->newOrderSet = TRUE;
-            }
-
-            mp->breakFlag = TRUE;
-            break;
-
-        case 'E':         // Exy = Special Effects
-            switch (paramX)
-            {
-                case 0x00:    // E0x - Set filter (NOT SUPPORTED)
-                    JMPDEBUG("Set Filter used, UNSUPPORTED");
-                    break;
-
-                case 0x01:    // E1x - Fine Portamento Up
-                    if (paramY)
-                        chn->iCLastFinePortamentoUpParam = paramY;
-
-                    jmpChangePitch(chn, channel, -(chn->iCLastFinePortamentoUpParam * 4));
-                    break;
-
-                case 0x02:    // E2x - Fine Portamento Down
-                    if (paramY)
-                        chn->iCLastFinePortamentoDownParam = paramY;
-
-                    jmpChangePitch(chn, channel, (chn->iCLastFinePortamentoDownParam * 4));
-                    break;
-
-                case 0x03:    // E3x - Glissando Control (NOT SUPPORTED)
-                    break;
-
-                case 0x04:    // E4x - Set Vibrato waveform
-                    chn->vibrato.wc = paramY;
-                    break;
-
-                case 0x05:    // E5x - Set Finetune
-                    JMPDEBUG("Set Finetune used, UNIMPLEMENTED");
-                    break;
-
-                case 0x06:    // E6x - Set Pattern Loop
-                    jmpDoPatternLoop(mp, chn, channel, paramY);
-                    break;
-
-                case 0x07:    // E7x - Set Tremolo waveform
-                    chn->tremolo.wc = paramY;
-                    break;
-
-                case 0x08:    // E8x - Set Pan Position
-                    chn->panning = (paramY * 16);
-                    JMPSETNDFLAGS(cdfNewPanPos);
-                    break;
-
-                case 0x09:    // E9x - Retrig note
-                    if (mp->tick == paramY)
-                        jmpTriggerNote(mp, chn, FALSE);
-                    break;
-
-                case 0x0a:    // EAx - Fine Volumeslide Up
-                    if (paramY)
-                        chn->iCLastFineVolumeslideUpParam = paramY;
-
-                    jmpChangeVolume(chn, channel, chn->iCLastFineVolumeslideUpParam);
-                    break;
-
-                case 0x0b:    // EBx - Fine Volumeslide Down
-                    if (paramY)
-                        chn->iCLastFineVolumeslideDownParam = paramY;
-                    jmpChangeVolume(chn, channel, -(chn->iCLastFineVolumeslideDownParam));
-                    break;
-
-                case 0x0c:    // ECx - Set Note Cut (NOT PROCESSED IN TICK0)
-                    break;
-
-                case 0x0d:    // EDx - Set Note Delay : IMPL.VERIFIED
-                    if (paramY > 0)
-                    {
-                        // Save the ND-flags, then clear
-                        chn->iSaveNDFlags = chn->newDataFlags;
-                        chn->newDataFlags = 0;
-                        // TODO .. does this only affect NOTE or also instrument?
-                    }
-                    break;
-
-                case 0x0e:    // EEx - Set Pattern Delay : IMPL.VERIFIED
-                    mp->patternDelay = paramY;
-                    break;
-
-                case 0x0f:    // EFx - Invert Loop (NOT SUPPORTED)
-                    JMPDEBUG("Invert Loop used, UNSUPPORTED");
-                    break;
-
-                default:
-                    JMPDEBUG("Unsupported special command used");
-            }
-            break;
-
-        case 'F':        // Fxy = Set Speed / Tempo : IMPL.VERIFIED
-            if (param > 0)
-            {
-                if (param < 0x20)
-                    mp->speed = param;
-                else
-                    jmpSetTempo(mp, param);
-            }
-            break;
-
-        case 'G':        // Gxx = Global Volume
-            mp->globalVol = param;
-            JMPSETNDFLAGS(cdfNewGlobalVol);
-            break;
-
-
-        case 'H':        // Hxx = Global Volume Slide
-            JMPDEBUG("Global Volume Slide used, UNIMPLEMENTED");
-            break;
-
-        case 'K':        // Kxx = Key-off (Same as key-off note)
-            chn->keyOff = TRUE;
-            break;
-
-        case 'L':        // Lxx = Set Envelope Position
-            JMPDEBUG("Set Envelope Position used, NOT verified with FT2");
-            chn->panningEnv.frame = param;
-            chn->volumeEnv.frame = param;
-            chn->panningEnv.exec = TRUE;
-            chn->volumeEnv.exec = TRUE;
-            break;
-
-        case 'R':        // Rxy = Multi Retrig note
-            if (paramX != 0)
-                chn->lastMultiRetrigParamX = paramX;
-            if (paramY != 0)
-                chn->lastMultiRetrigParamY = paramY;
-            break;
-
-        case 'T':        // Txy = Tremor
-            if (param)
-                chn->iLastTremorParam = param;
-            break;
-
-        case 'X':        // Xxy = Extra Fine Portamento
-            switch (paramX)
-            {
-                case 0x01:    // X1y - Extra Fine Portamento Up
-                    if (paramY)
-                        chn->iCLastExtraFinePortamentoUpParam = paramY;
-
-                    jmpChangePitch(chn, channel, - chn->iCLastExtraFinePortamentoUpParam);
-                    break;
-
-                case 0x02:    // X2y - Extra Fine Portamento Down
-                    if (paramY)
-                        chn->iCLastExtraFinePortamentoDownParam = paramY;
-
-                    jmpChangePitch(chn, channel, chn->iCLastExtraFinePortamentoUpParam);
-                    break;
-
-                default:
-                    JMPDEBUG("Unsupported value in Extra Fine Portamento command!");
-                    break;
-            }
-            break;
-
-        default:
-            JMPDEBUG("Unsupported effect");
-            break;
-    }
-}
-
-
-static void jmpProcessEffects(JSSPlayer * mp, int channel)
-{
-    JSSPlayerChannel *chn = &(mp->channels[channel]);
-    JSSNote *currNote;
-    int param, paramX, paramY, tmp;
-    char effect;
-
-    // Process the volume column effects
-    JMPGETNOTE(currNote, mp->row, channel);
-    JMPMAKEPARAM(currNote->volume, paramX, paramY);
-
-    switch (paramX)
-    {
-        case 0x05: // -x = Volumeslide Down : IMPL.VERIFIED
-            jmpChangeVolume(chn, channel, -paramY);
-            break;
-
-        case 0x06: // +x = Volumeslide Down : IMPL.VERIFIED
-            jmpChangeVolume(chn, channel, paramY);
-            break;
-
-        case 0x0a: // Vx = Vibrato : IMPL.VERIFIED
-            jmpDoVibrato(chn, channel);
-            break;
-
-        case 0x0e: // Mx = Porta To Note : IMPL.VERIFIED
-            jmpDoPortamento(chn, channel);
-            break;
-    }
-
-    // ...And finally process the Normal effects
-    if (currNote->effect == jsetNotSet)
-        return;
-    
-    param = currNote->param;
-    JMPMAKEPARAM(param, paramX, paramY);
-    JMPGETEFFECT(effect, currNote->effect);
-
-    switch (effect)
-    {
-        case '0': // 0xy = Arpeggio
-            jmpDoArpeggio(mp, chn, channel, paramX, paramY);
-            break;
-
-        case '1': // 1xy = Portamento Up
-            if (chn->iLastPortaParam > 0)
-                jmpChangePitch(chn, channel, -(chn->iLastPortaParam * 4));
-            break;
-
-        case '2': // 2xy = Portamento Down
-            if (chn->iLastPortaParam > 0)
-                jmpChangePitch(chn, channel, (chn->iLastPortaParam * 4));
-            break;
-
-        case '3': // 3xy = Porta To Note
-            jmpDoPortamento(chn, channel);
-            break;
-
-        case '4': // 4xy = Vibrato
-            jmpDoVibrato(chn, channel);
-            break;
-
-        case '5': // 5xy = Portamento + Volume Slide
-            jmpDoPortamento(chn, channel);
-            jmpDoVolumeSlide(chn, channel, chn->iLastVolSlideParam);
-            break;
-
-        case '6': // 6xy = Vibrato + Volume Slide
-            jmpDoVibrato(chn, channel);
-            jmpDoVolumeSlide(chn, channel, chn->iLastVolSlideParam);
-            break;
-
-        case '7': // 7xy = Tremolo
-            jmpDoTremolo(chn, channel);
-            break;
-
-        case 'A': // Axy = Volume slide
-            jmpDoVolumeSlide(chn, channel, chn->iLastVolSlideParam);
-            break;
-
-        case 'E': // Exy = Special Effects
-            switch (paramX)
-            {
-                case 0x09:    // E9x - Retrig note
-                    if (mp->tick == paramY)
-                        jmpTriggerNote(mp, chn, FALSE);
-                    break;
-
-                case 0x0c: // ECx - Set Note Cut
-                    if (mp->tick == paramY)
-                        jmpSetVolume(chn, channel, jsetMinVol);
-                    break;
-
-                case 0x0d: // EDx - Set Note Delay
-                    if (mp->tick == paramY)
-                        chn->newDataFlags = chn->iSaveNDFlags;
-                    break;
-            }
-            break;
-
-        case 'R':        // Rxy = Multi Retrig note
-            jmpDoMultiRetrigNote(mp, chn, channel);
-            break;
-
-        case 'T': // Txy = Tremor
-            JMPMAKEPARAM(chn->iLastTremorParam, paramX, paramY)
-            paramX++;
-            paramY++;
-            tmp = chn->iTremorCount % (paramX + paramY);
-
-            if (tmp < paramX)
-                jmpCSetVolume(chn, channel, chn->cvolume);
-            else
-                jmpCSetVolume(chn, channel, jsetMinVol);
-
-            chn->iTremorCount = tmp + 1;
-            break;
-    }
-}
-
-
-/* This is the main processing callback-loop of a module player.
- * It processes the ticks, calling the needed jmpProcessNewRow()
- * and jmpProcessEffects() methods for processing the module playing.
- */
-void jmpExec(void *pDEV, void *pMP)
-{
-    JSSPlayer *mp;
-    JSSMixer *dev;
-    int channel;
-
-    // Check some things via assert()
-    mp = (JSSPlayer *) pMP;
-    JSS_LOCK(mp);
-
-    dev = (JSSMixer *) pDEV;
-
-    // Check if we are playing
-    if (!mp->isPlaying)
-        goto out;
-
-    // Clear channel new data flags
-    mp->jumpFlag = FALSE;
-    mp->breakFlag = FALSE;
-
-    for (channel = 0; channel < jsetNChannels; channel++)
-        mp->channels[channel].newDataFlags = 0;
-
-//fprintf(stderr, "1: tick=%d, order=%d, iPattern=%d, row=%d\n", mp->tick, mp->order, mp->npattern, mp->row);
-
-    // Check for init-tick
-    if (mp->tick == jsetNotSet)
-    {
-        // Initialize pattern
-        mp->newRow = 0;
-        mp->newRowSet = TRUE;
-        mp->tick = mp->speed;
-        mp->patternDelay = 0;
-    }
-
-//fprintf(stderr, "2: tick=%d, order=%d, iPattern=%d, row=%d\n", mp->tick, mp->order, mp->npattern, mp->row);
-
-    // Check if we are playing
-    if (!mp->isPlaying)
-        goto out;
-
-    assert(mp->pattern);
-
-    // Update the tick
-    mp->tick++;
-    if (mp->tick >= mp->speed)
-    {
-        // Re-init tick counter
-        mp->tick = 0;
-
-        // Check pattern delay
-        if (mp->patternDelay > 0)
-            mp->patternDelay--;
-        else
-        {
-            // New pattern row
-            if (mp->newRowSet)
-            {
-                mp->row = mp->newRow;
-                mp->newRowSet = FALSE;
-            } else
-                mp->row++;
-
-            // Check for end of pattern
-            if (mp->row >= mp->pattern->nrows)
-            {
-                // Go to next order
-                if (mp->order != jsetNotSet)
-                    jmpSetNewOrder(mp, mp->order + 1);
-                else
-                    mp->isPlaying = FALSE;
-                
-                // Check for FT2 quirks
-                if (JMPGETMODFLAGS(mp, jmdfFT2Replay))
-                    mp->row = mp->lastPatLoopRow;
-                else
-                    mp->row = 0;
-            }
-
-            if (!mp->isPlaying)
-                goto out;
-
-            // Check current order
-            if (mp->newOrderSet)
-            {
-                jmpSetNewOrder(mp, mp->newOrder);
-                mp->newOrderSet = FALSE;
-            }
-
-//fprintf(stderr, "3: tick=%d, order=%d, iPattern=%d, row=%d\n", mp->tick, mp->order, mp->npattern, mp->row);
-
-            if (!mp->isPlaying)
-                goto out;
-
-            // TICK #0: Process new row
-            for (channel = 0; channel < mp->module->nchannels; channel++)
-                jmpProcessNewRow(mp, channel);
-        } // patternDelay
-    } // tick
-    else
-    {
-        // Implement FT2's pattern delay-effect: don't update effects while on patdelay
-        if (!JMPGETMODFLAGS(mp, jmdfFT2Replay) ||
-            (JMPGETMODFLAGS(mp, jmdfFT2Replay) && mp->patternDelay <= 0))
-        {
-            // TICK n: Process the effects
-            for (channel = 0; channel < mp->module->nchannels; channel++)
-                jmpProcessEffects(mp, channel);
-        }
-    }
-
-    // Check if playing has stopped
-    if (!mp->isPlaying)
-        goto out;
-
-    // Update player data to audio device/mixer
-    for (channel = 0; channel < mp->module->nchannels; channel++)
-    {
-        JSSPlayerChannel *chn = &mp->channels[channel];
-
-        // Process extended instruments
-        jmpProcessExtInstrument(chn, channel);
-        
-        // Check NDFlags and update channel data
-        int flags = chn->newDataFlags;
-        if (!flags)
-            continue;
-
-        // Check if we stop?
-        if (flags & cdfStop)
-        {
-            jvmStop(mp->device, channel);
-        }
-        else
-        {
-            // No, handle other flags
-            if (flags & cdfNewInstr)
-            {
-                JSSInstrument *instr = chn->instrument;
-                if (instr != NULL)
-                {
-                    jvmSetSample(mp->device, channel,
-                        instr->data, instr->size,
-                        instr->loopS, instr->loopE,
-                        instr->flags);
-                }
-            }
-
-            if (flags & cdfPlay)
-            {
-                jvmReset(mp->device, channel);
-                jvmPlay(mp->device, channel);
-            }
-
-            if (flags & cdfNewPitch)
-                jmpCSetPitch(mp, chn, chn->pitch);
-                
-            if (flags & (cdfNewFreq | cdfNewPitch))
-                jvmSetFreq(mp->device, channel, chn->freq);
-
-            if (flags & cdfNewPos)
-                jvmSetPos(mp->device, channel, chn->position);
-
-            if (flags & cdfNewVolume)
-            {
-                BOOL init = flags & (cdfNewInstr | cdfPlay);
-                jvmSetVolumeRamp(mp->device, channel,
-                    init ? 0 : jvmGetVolume(mp->device, channel),
-                    (chn->fadeOutVol * chn->volumeEnv.value * chn->volume) / (16 * 65536),
-                    init ? 5 : 0);
-            }
-
-            if (flags & cdfNewPanPos)
-            {
-                jvmSetPanRamp(mp->device, channel,
-                    jvmGetPan(mp->device, channel),
-                    chn->panning + (((chn->panningEnv.value - 32) * (128 - abs(chn->panning - 128))) / 32),
-                    0);
-            }
-
-            if (flags & cdfNewGlobalVol)
-                jvmSetGlobalVol(mp->device, mp->globalVol);
-        }
-    }
-
-out:
-    JSS_UNLOCK(mp);
-}
--- a/jssplr.h	Tue Apr 16 07:25:54 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-/*
- * miniJSS - Module playing routines
- * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
- */
-#ifndef JSSPLR_H
-#define JSSPLR_H
-
-#include "jss.h"
-#include "jssmod.h"
-#include "jssmix.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Player general constants
-#define mpMinVol            (0)
-#define mpMaxVol            (64)
-#define mpPanCenter         (0)
-//#define mpMaxFadeoutVol     (65535)
-#define mpMaxFadeoutVol     (32767)
-
-
-// Channel New-Data flags
-#define cdfNONE             (0x000)    // Set nothing
-#define cdfNewInstr         (0x001)    // Set a new instrument
-#define cdfNewPitch         (0x002)    // Set a new pitch
-#define cdfNewPos           (0x004)    // Set a new position
-#define cdfNewVolume        (0x008)    // Set a new volume
-#define cdfNewPanPos        (0x010)    // Set a new panning position
-#define cdfNewGlobalVol     (0x020)    // Set a new global volume
-#define cdfNewFreq          (0x040)    // New frequency
-#define cdfPlay             (0x100)    // Start or restart playing sample
-#define cdfStop             (0x200)    // Stop channel playing
-
-
-// Player envelope data
-typedef struct
-{
-    int frame, value;
-    BOOL exec;
-} JSSPlayerEnvelope;
-
-
-typedef struct
-{
-    int pos, speed, depth, wc;
-} JSSPlayerVData;
-
-
-// Player channel structure
-typedef struct
-{
-    JSSPlayerEnvelope volumeEnv, panningEnv;
-
-    JSSPlayerVData vibrato, tremolo;
-        
-    BOOL    keyOff;
-
-    JSSInstrument  *instrument;           // Instrument
-    JSSExtInstrument *extInstrument;       // ExtInstrument
-    int     ninstrument,
-            nextInstrument,
-            note,                 // Current note
-            pitch,                // Pitch (NOT actual frequency!)
-            freq, cfreq,
-            oldPitch,
-            oldNote,
-            position,             // Sample position
-            volume, cvolume,      // Volume
-            panning,              // Panning position
-
-            newDataFlags,         // New data flags
-
-            fadeOutVol,
-
-            iCAutoVib_Frame,
-
-            iCLastFineVolumeslideUpParam,
-            iCLastFineVolumeslideDownParam,
-            iCLastExtraFinePortamentoUpParam,
-            iCLastExtraFinePortamentoDownParam,
-            iCLastFinePortamentoUpParam,
-            iCLastFinePortamentoDownParam,
-
-            iPatLoopRow,            // Pattern loop start row
-            iPatLoopCount,          // Pattern loop count
-        
-            iLastPortaParam,        // Last portamento effect parameter
-            iLastPortaToNoteParam,  // Last porta-to-note parameter
-            iLastPortaToNotePitch,  // Last porta-to-note pitch
-            lastPortaToNoteNote,
-
-            iLastTremorParam,
-            iTremorCount,
-            iLastSampleOffset,
-            iLastVolSlideParam,
-
-            lastMultiRetrigParamX,
-            lastMultiRetrigParamY,
-
-            lastSampleOffsetParam,
-            
-            iSaveNDFlags;           // For notedelay-effect
-} JSSPlayerChannel;
-
-
-// Struct holding all player related information
-typedef struct _JSSPlayer
-{
-    // General variables
-    int     tempo,              // Current values
-            speed,
-            tick,
-            order,
-            npattern,
-            row,
-            globalVol,
-            options;            // Playing option flags
-    BOOL    isPlaying;          // Are we playing?
-
-    int     newOrder,           // NEW order number
-            newRow;             // NEW row number
-    BOOL    newOrderSet,        // TRUE if new order has been set
-            newRowSet;          // TRUE if new row has been set
-
-    int     patternDelay,       // Pattern delay tick-counter
-            lastPatLoopRow;     // Latest set pattern loop row (any channel)
-
-    BOOL    jumpFlag,           // Pattern jump flag
-            breakFlag;          // Pattern break flag
-
-    // All channels for this player
-//    int     nchannels;
-    JSSPlayerChannel channels[jsetNChannels];
-
-
-    // Module and sounddevice specific
-    JSSModule *module;          // Current module in this player
-    JSSPattern *pattern;        // Current pattern
-    JSSMixer *device;           // Pointer to mixing device structure
-
-#ifdef JSS_SUP_THREADS
-    DMMutex *mutex;
-#endif
-
-} JSSPlayer;
-
-
-/* External functions for end users
- */
-int             jmpPlayOrder(JSSPlayer *, int);
-int             jmpChangeOrder(JSSPlayer *, int);
-int             jmpPlayPattern(JSSPlayer *, int);
-void            jmpStop(JSSPlayer *);
-void            jmpResume(JSSPlayer *);
-void            jmpSetModule(JSSPlayer *, JSSModule *);
-JSSPlayer *     jmpInit(JSSMixer *);
-int             jmpClose(JSSPlayer *);
-void            jmpExec(void *, void *);
-
-
-/* Helper macros
- */
-#define JMPMAKEPARAM(AIVAL, AVALX, AVALY) { AVALX = (((AIVAL) >> 4) & 0x0f); AVALY = ((AIVAL) & 0x0f); }
-#define JMPSETNDFLAGS(IVAL) chn->newDataFlags |= IVAL
-#define JMPUNSETNDFLAGS(IVAL) chn->newDataFlags &= (~(IVAL))
-#define JMPGETNOTE(MNOTE, MROW, MCHAN) assert(mp); assert(mp->pattern); assert((MROW) >= 0); assert((MROW) < mp->pattern->nrows); MNOTE = &mp->pattern->data[(mp->pattern->nchannels * MROW) + (MCHAN)]
-#define JMPGETEFFECT(MEFF, MIEFF) if ((MIEFF >= 0) && (MIEFF < jmpNMODEffectTable)) MEFF = jmpMODEffectTable[MIEFF]; else MEFF = 0
-#define JMPGETMODFLAGS(Q, Z) ((Q->module->defFlags & (Z)) == (Z))
-
-
-/* Debugging macros
- */
-#if !defined(JSS_LIGHT) && defined(JSS_DEBUG)
-#  define JMPDEBUG(QQ) { fprintf(stderr, "[o=%03d:p=%03d:r=%03d] (%c/%x:%x) %s\n", mp->order, mp->npattern, mp->row, effect, currNote->effect, currNote->param, QQ ); }
-#else
-#  define JMPDEBUG(QQ) // stub
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // JSSPLR_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jloadjss.c	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,579 @@
+/*
+ * miniJSS - JSSMOD module loader
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2007-2009 Tecnic Software productions (TNSP)
+ */
+#include "jssmod.h"
+#include <string.h>
+
+
+#ifdef JM_SUP_PATMODE_ALL
+#    define JM_SUP_PATMODE_1 1
+#    define JM_SUP_PATMODE_2 1
+#    define JM_SUP_PATMODE_3 1
+#    define JM_SUP_PATMODE_4 1
+#    define JM_SUP_PATMODE_5 1
+#endif
+
+
+static BOOL jsGetBufData(Uint8 **buf, size_t *bufLeft, void *data, const size_t dataSize)
+{
+    if (*bufLeft >= dataSize)
+    {
+        memcpy(data, *buf, dataSize);
+        *buf += dataSize;
+        *bufLeft -= dataSize;
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+
+static BOOL jsGetBufByte(Uint8 **buf, size_t *bufLeft, Uint8 *data)
+{
+    if (*bufLeft > 0)
+    {
+        *data = **buf;
+        (*buf)++;
+        (*bufLeft)--;
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+
+#define JSGETBUF(XV, XT) if (!jsGetBufData(buf, bufLeft, XV, sizeof(XT))) return DMERR_OUT_OF_DATA
+#define JSGETBYTE(XV) if (!jsGetBufByte(buf, bufLeft, XV)) return DMERR_OUT_OF_DATA
+
+
+#if defined(JM_SUP_PATMODE_1) || defined(JM_SUP_PATMODE_3)
+static int jssGetConvertedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
+{
+    Uint8 tmp;
+
+    JSGETBYTE(&tmp);
+
+    if (tmp == 127)
+        note->note = jsetNoteOff;
+    else if (tmp == 0)
+        note->note = jsetNotSet;
+    else
+        note->note = tmp - 1;
+
+    JSGETBYTE(&tmp);
+    note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
+
+    JSGETBYTE(&tmp);
+    note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
+
+    JSGETBYTE(&tmp);
+    note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
+
+    JSGETBYTE(&tmp);
+    note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
+
+    return DMERR_OK;
+}
+#endif
+
+
+#if defined(JM_SUP_PATMODE_2) || defined(JM_SUP_PATMODE_4)
+static int jssGetCompressedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
+{
+    Uint8 packb, tmp;
+
+    JSGETBYTE(&packb);
+    if (packb & 0x80)
+    {
+        if (packb & COMP_NOTE)
+        {
+            JSGETBYTE(&tmp);
+            if (tmp == 127)
+                note->note = jsetNoteOff;
+            else
+                note->note = tmp;
+        }
+
+        if (packb & COMP_INSTRUMENT)
+        {
+            JSGETBYTE(&tmp);
+            note->instrument = tmp;
+        }
+
+        if (packb & COMP_VOLUME)
+        {
+            JSGETBYTE(&tmp);
+            note->volume = tmp;
+        }
+
+        if (packb & COMP_EFFECT)
+        {
+            JSGETBYTE(&tmp);
+            note->effect = tmp;
+            note->param = 0;
+        }
+
+        if (packb & COMP_PARAM)
+        {
+            JSGETBYTE(&tmp);
+            note->param = tmp;
+        }
+    }
+    else
+    {
+        tmp = packb;
+
+        if (tmp == 127)
+            note->note = jsetNoteOff;
+        else if (tmp == 0)
+            note->note = jsetNotSet;
+        else
+            note->note = tmp - 1;
+
+        JSGETBYTE(&tmp);
+        note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
+
+        JSGETBYTE(&tmp);
+        note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
+
+        JSGETBYTE(&tmp);
+        note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
+
+        JSGETBYTE(&tmp);
+        note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
+    }
+
+    return DMERR_OK;
+}
+#endif
+
+
+#ifdef JM_SUP_PATMODE_2
+static int jssGetPatternCompHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
+{
+    int row, channel;
+
+    assert(buf != NULL);
+    assert(pattern != NULL);
+
+    for (row = 0; row < pattern->nrows; row++)
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    {
+        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        int res = jssGetCompressedNote(&buf, bufLeft, note);
+        if (res != DMERR_OK)
+            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
+    }
+
+    return DMERR_OK;
+}
+#endif
+
+
+#ifdef JM_SUP_PATMODE_4
+static int jssGetPatternCompVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
+{
+    int row, channel;
+
+    assert(buf != NULL);
+    assert(pattern != NULL);
+
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    for (row = 0; row < pattern->nrows; row++)
+    {
+        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        int res = jssGetCompressedNote(&buf, bufLeft, note);
+        if (res != DMERR_OK)
+            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
+    }
+
+    return DMERR_OK;
+}
+#endif
+
+
+#ifdef JM_SUP_PATMODE_1
+static int jssGetPatternRawHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
+{
+    int row, channel;
+
+    assert(buf != NULL);
+    assert(pattern != NULL);
+
+    for (row = 0; row < pattern->nrows; row++)
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    {
+        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        int res = jssGetConvertedNote(&buf, bufLeft, note);
+        if (res != DMERR_OK)
+            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
+    }
+
+    return DMERR_OK;
+}
+#endif
+
+
+#ifdef JM_SUP_PATMODE_3
+static int jssGetPatternRawVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
+{
+    int row, channel;
+
+    assert(buf != NULL);
+    assert(pattern != NULL);
+
+    for (channel = 0; channel < pattern->nchannels; channel++)
+    for (row = 0; row < pattern->nrows; row++)
+    {
+        JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
+        int res = jssGetConvertedNote(&buf, bufLeft, note);
+        if (res != DMERR_OK)
+            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
+    }
+
+    return DMERR_OK;
+}
+#endif
+
+
+#ifdef JM_SUP_PATMODE_5
+
+#undef JSGETBYTE
+#define JSGETBYTE(XV) if (!jsGetBufByte(&buf, bufLeft, XV)) return DMERR_OUT_OF_DATA
+
+#define JSFOREACHNOTE1                                                              \
+  for (channel = 0; channel < pattern->nchannels; channel++)                        \
+  for (row = 0; row < pattern->nrows; row++) {                                      \
+      JSSNote *note = pattern->data + (pattern->nchannels * row) + channel;
+
+#define JSFOREACHNOTE2 }
+
+static int jssGetPatternRawVertElem(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
+{
+    int row, channel;
+    Uint8 tmp;
+
+    assert(buf != NULL);
+    assert(pattern != NULL);
+
+    JSFOREACHNOTE1
+    JSGETBYTE(&tmp);
+    if (tmp == 0)
+        note->note = jsetNotSet;
+    else if (tmp == 127)
+        note->note = jsetNoteOff;
+    else
+        note->note = tmp - 1;
+    JSFOREACHNOTE2
+    
+    JSFOREACHNOTE1
+    JSGETBYTE(&tmp);
+    note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
+    JSFOREACHNOTE2
+    
+    JSFOREACHNOTE1
+    JSGETBYTE(&tmp);
+    note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
+    JSFOREACHNOTE2
+    
+    JSFOREACHNOTE1
+    JSGETBYTE(&tmp);
+    note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
+    JSFOREACHNOTE2
+    
+    JSFOREACHNOTE1
+    JSGETBYTE(&tmp);
+    note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
+    JSFOREACHNOTE2
+
+    return DMERR_OK;
+}
+#endif
+
+
+#undef JSGETBUF
+#undef JSGETBYTE
+#define JSGETBUF(XV, XT) do { \
+    if (!jsGetBufData(&buf, &bufLeft, XV, sizeof(XT)))  \
+        JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,    \
+        "Out of data at getting " # XT " (%d bytes)\n", sizeof(XT)); \
+} while (0)
+#define JSGETBYTE(XV) if (!jsGetBufByte(&buf, &bufLeft, XV)) return DMERR_OUT_OF_DATA
+
+
+#ifdef JM_SUP_EXT_INSTR
+static void jssCopyEnvelope(JSSEnvelope *e, JSSMODEnvelope *je)
+{
+    int i;
+
+    e->flags   = je->flags;
+    e->npoints = je->npoints;
+    e->sustain = je->sustain;
+    e->loopS   = je->loopS;
+    e->loopE   = je->loopE;
+
+    for (i = 0; i < je->npoints; i++)
+    {
+        e->points[i].frame = je->points[i].frame;
+        e->points[i].value = je->points[i].value;
+    }
+}
+#endif
+
+
+int jssLoadJSSMOD(Uint8 *bufStart, const size_t bufSize, JSSModule **ppModule)
+{
+    JSSModule *module;
+    JSSMODHeader jssH;
+    Uint8 *buf = bufStart;
+    size_t bufLeft = bufSize;
+    int index;
+
+    assert(ppModule != NULL);
+    assert(bufStart != NULL);
+    *ppModule = NULL;
+
+    // Check the JSSMOD header
+    memset(&jssH, 0, sizeof(jssH));
+    JSGETBUF(&jssH, JSSMODHeader);
+
+    if (memcmp(jssH.idMagic, "JM", 2) != 0)
+    {
+        JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Not a valid JSSMOD file, header signature missing!\n");
+    }
+
+    if (jssH.idVersion != JSSMOD_VERSION)
+    {
+        JSSERROR(DMERR_VERSION, DMERR_VERSION,
+        "Unsupported version of JSSMOD 0x%4x, this version only supports 0x%4x!\n",
+        jssH.idVersion, JSSMOD_VERSION);
+    }
+
+    // Allocate the module
+    module = jssAllocateModule();
+    if (module == NULL)
+    {
+        JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+        "Could not allocate memory for module structure.\n");
+    }
+    *ppModule = module;
+
+    // Copy header information
+    module->norders         = jssH.norders;
+    module->npatterns       = jssH.npatterns;
+    module->nchannels       = jssH.nchannels;
+    module->nextInstruments = jssH.nextInstruments;
+    module->ninstruments    = jssH.ninstruments;
+    module->defFlags        = jssH.defFlags;
+    module->intVersion      = jssH.intVersion;
+    module->defRestartPos   = jssH.defRestartPos;
+    module->defSpeed        = jssH.defSpeed;
+    module->defTempo        = jssH.defTempo;
+
+    // Get the orders list
+    for (index = 0; index < module->norders; index++)
+    {
+        Sint16 order;
+        JSGETBUF(&order, Sint16);
+        module->orderList[index] = order;
+    }
+
+    // Parse the patterns
+    for (index = 0; index < module->npatterns; index++)
+    {
+        JSSMODPattern jssP;
+        int result = DMERR_INVALID_DATA;
+        size_t bufSize;
+
+        // Get header and check size
+        memset(&jssP, 0, sizeof(jssP));
+        JSGETBUF(&jssP, JSSMODPattern);
+        bufSize = jssP.size;
+        if (bufLeft < jssP.size)
+        {
+            JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
+            "Out of data for pattern #%d.\n", index);
+        }
+
+        // Allocate pattern
+        module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels);
+        if (module->patterns[index] == NULL)
+        {
+            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+            "Could not allocate memory for pattern #%d.\n", index);
+        }
+
+        // Get pattern data
+        switch (jssH.patMode)
+        {
+#ifdef JM_SUP_PATMODE_1
+            case PATMODE_RAW_HORIZ:
+                result = jssGetPatternRawHoriz(buf, &bufSize, module->patterns[index]);
+                break;
+#endif
+#ifdef JM_SUP_PATMODE_2
+            case PATMODE_COMP_HORIZ:
+                result = jssGetPatternCompHoriz(buf, &bufSize, module->patterns[index]);
+                break;
+#endif
+#ifdef JM_SUP_PATMODE_3
+            case PATMODE_RAW_VERT:
+                result = jssGetPatternRawVert(buf, &bufSize, module->patterns[index]);
+                break;
+#endif
+#ifdef JM_SUP_PATMODE_4
+            case PATMODE_COMP_VERT:
+                result = jssGetPatternCompVert(buf, &bufSize, module->patterns[index]);
+                break;
+#endif
+#ifdef JM_SUP_PATMODE_5
+            case PATMODE_RAW_ELEM:
+                result = jssGetPatternRawVertElem(buf, &bufSize, module->patterns[index]);
+                break;
+#endif
+            default:
+                JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                "Unsupported pattern mode %d. Check compilation options.", jssH.patMode);
+                break;
+        }
+
+        if (bufSize > 0)
+        {
+            JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA,
+            "Unparsed data after pattern (%d bytes), possibly broken file.\n", bufSize);
+        }
+
+        if (result != DMERR_OK)
+        {
+            JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index);
+        }
+
+        buf += jssP.size;
+        bufLeft -= jssP.size;
+    }
+
+#ifdef JM_SUP_EXT_INSTR
+    // Read extended instruments
+    for (index = 0; index < module->nextInstruments; index++)
+    {
+        JSSMODExtInstrument jssE;
+        JSSExtInstrument *einst;
+        int i;
+
+        memset(&jssE, 0, sizeof(jssE));
+        JSGETBUF(&jssE, JSSMODExtInstrument);
+
+        if ((einst = jssAllocateExtInstrument()) == NULL)
+        {
+            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+            "Could not allocate extended instrument structure #%i\n", index);
+        }
+
+        module->extInstruments[index] = einst;
+
+        einst->nsamples     = jssE.nsamples;
+        einst->vibratoType  = jssE.vibratoType;
+        einst->vibratoSweep = jssE.vibratoSweep;
+        einst->vibratoDepth = jssE.vibratoDepth;
+        einst->vibratoRate  = jssE.vibratoRate;
+        einst->fadeOut      = jssE.fadeOut;
+
+        for (i = 0; i < jsetNNotes; i++)
+        {
+            int snum = jssE.sNumForNotes[i];
+            einst->sNumForNotes[i] = (snum > 0) ? snum : jsetNotSet;
+        }
+
+        jssCopyEnvelope(&(einst->volumeEnv), &jssE.volumeEnv);
+        jssCopyEnvelope(&(einst->panningEnv), &jssE.panningEnv);
+    }
+
+#ifdef JM_SUP_INSTR
+    // Read sample instrument headers
+    for (index = 0; index < module->ninstruments; index++)
+    {
+        JSSMODInstrument jssI;
+        JSSInstrument *inst;
+
+        memset(&jssI, 0, sizeof(jssI));
+        JSGETBUF(&jssI, JSSMODInstrument);
+
+        if ((inst = jssAllocateInstrument()) == NULL)
+        {
+            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+            "Could not allocate instrument structure #%i\n", index);
+        }
+
+        module->instruments[index] = inst;
+
+        inst->size          = jssI.size;
+        inst->loopS         = jssI.loopS;
+        inst->loopE         = jssI.loopE;
+        inst->volume        = jssI.volume;
+        inst->flags         = jssI.flags;
+        inst->C4BaseSpeed   = jssI.C4BaseSpeed;
+        inst->ERelNote      = jssI.ERelNote;
+        inst->EFineTune     = jssI.EFineTune;
+        inst->EPanning      = jssI.EPanning;
+        inst->hasData       = jssI.hasData;
+        inst->convFlags     = jssI.convFlags;
+    }
+
+#ifdef JM_SUP_SAMPLES
+    // Read sample data
+    for (index = 0; index < module->ninstruments; index++)
+    {
+        JSSInstrument *inst = module->instruments[index];
+
+        if (inst && inst->hasData)
+        {
+            size_t sz;
+
+            // Calculate data size
+            if (inst->flags & jsf16bit)
+                sz = inst->size * sizeof(Uint16);
+            else
+                sz = inst->size * sizeof(Uint8);
+
+            // Check if we can get as much?
+            if (bufLeft < sz)
+            {
+                JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
+                "Out of data for instrument sample #%d (%d < %d)\n",
+                index, bufLeft, sz);
+            }
+
+            // Allocate
+            if ((inst->data = dmMalloc(sz)) == NULL)
+            {
+                JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+                "Could not allocate sample data #%d\n", index);
+            }
+
+            // Copy data
+            memcpy(inst->data, buf, sz);
+            buf += sz;
+            bufLeft -= sz;
+
+            // Convert, if needed
+            if (inst->flags & jsf16bit)
+                jssDecodeSample16(inst->data, inst->size, inst->convFlags);
+            else
+                jssDecodeSample8(inst->data, inst->size, inst->convFlags);
+        }
+    }
+#else
+#    warning Not including JSSMOD sample loading!
+#endif  // JM_SUP_SAMPLES
+#else
+#    warning Not including JSSMOD instrument loading!
+#endif  // JM_SUP_INSTR
+#else
+#    warning Not including JSSMOD ext.instrument loading!
+#endif  // JM_SUP_EXT_INSTR
+
+    return DMERR_OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jloadxm.c	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,790 @@
+/*
+ * miniJSS - Fast Tracker ][ (XM) module loader
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
+ *
+ * TO DO:
+ * - Add support for 1.02/1.03 XM-format versions.
+ *   (Not very useful, but if it's not too hard, then do it)
+ */
+#include "jssmod.h"
+#include <string.h>
+
+
+/* XM value limit definitions
+ */
+#define XM_MaxChannels      (32)
+#define XM_MaxPatterns      (256)
+#define XM_MaxOrders        (255)
+#define XM_MaxInstruments   (128)
+#define XM_MaxInstSamples   (16)
+#define XM_MaxEnvPoints     (12)
+#define XM_MaxNotes         (96)
+#define XM_MaxSampleVolume  (64)
+
+
+/* XM format structures
+ */
+typedef struct
+{
+    char    idMagic[17];       // XM header ID "Extended Module: "
+    char    songName[20];      // Module song name
+    Uint8   unUsed1A;          // ALWAYS 0x1a
+    char    trackerName[20];   // ID-string of tracker software
+    Uint16  version;           // XM-version 0x0104
+    Uint32  headSize;          // Module header size, FROM THIS POINT!
+    Uint16  norders,           // Number of orders
+            defRestartPos,     // Default song restart position
+            nchannels,         // Number of channels
+            npatterns,         // Number of patterns
+            ninstruments,      // Number of instruments
+            flags,             // Module flags:
+                               // bit0: 0 = Amiga frequency table
+                               //       1 = Linear frequency table
+                               //
+            defSpeed,          // Default speed
+            defTempo;          // Default tempo
+    Uint8   orderList[256];    // Order list
+} XMHeader;
+
+
+typedef struct
+{
+    Uint32  headSize;          // Instrument header size (see docs!)
+    char    instName[22];      // Name/description
+    Uint8   instType;          // Type
+    Uint16  nsamples;          // Number of samples
+} XMInstrument1;
+
+
+typedef struct
+{
+    Uint16 frame, value;
+} XMEnvPoint;
+
+
+typedef struct
+{
+    Uint8 flags, npoints, sustain, loopS, loopE;
+    XMEnvPoint points[XM_MaxEnvPoints];
+} XMEnvelope;
+
+
+typedef struct
+{
+    Uint32 headSize;                  // Header size
+    Uint8 sNumForNotes[XM_MaxNotes];  // Sample numbers for notes
+    XMEnvelope volumeEnv, panningEnv;
+    Uint8 vibratoType, vibratoSweep, vibratoDepth, vibratoRate;
+
+    Uint16 fadeOut, ARESERVED;
+} XMInstrument2;
+
+
+typedef struct
+{
+    Uint32 size, loopS, loopL;
+    Uint8 volume;
+    int fineTune;
+    Uint8 type, panning;
+    int relNote;
+    Uint8 ARESERVED;
+    char sampleName[22];
+} XMSample;
+
+
+typedef struct
+{
+    Uint32 headSize;
+    Uint8 packing;
+    Uint16 nrows, size;
+} XMPattern;
+
+
+
+/* Unpack XM-format pattern from file-stream into JSS-pattern structure
+ */
+#define JSGETBYTE(XV) do {                          \
+    size--;                                         \
+    if (size < 0)                                   \
+    JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,  \
+    "Unexpected end of packed pattern data.\n");    \
+    XV = dmfgetc(inFile);                           \
+} while (0)
+
+
+static int jssXMConvertNote(int val)
+{
+    if (val < 1 || val > 97)
+        return jsetNotSet;
+    else if (val == 97)
+        return jsetNoteOff;
+    else
+        return val - 1;
+}
+
+
+static int jssXMUnpackPattern(DMResource *inFile, int size, JSSPattern *pattern)
+{
+    int packb, row, channel, tmp;
+    JSSNote *pnote;
+    assert(pattern != NULL);
+
+    pnote = pattern->data;
+
+    for (row = 0; row < pattern->nrows && size > 0; row++)
+    for (channel = 0; channel < pattern->nchannels && size > 0; channel++)
+    {
+        JSGETBYTE(packb);
+        if (packb & 0x80)
+        {
+            if (packb & 0x01)
+            {
+                // PACK 0x01: Read note
+                JSGETBYTE(tmp);
+                pnote->note = jssXMConvertNote(tmp);
+            }
+
+            if (packb & 0x02)
+            {
+                // PACK 0x02: Read instrument
+                JSGETBYTE(tmp);
+                pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
+            }
+
+            if (packb & 0x04)
+            {
+                // PACK 0x04: Read volume
+                JSGETBYTE(tmp);
+                pnote->volume = (tmp >= 0x10) ? tmp - 0x10 : jsetNotSet;
+            }
+
+            if (packb & 0x08)
+            {
+                // PACK 0x08: Read effect
+                JSGETBYTE(pnote->effect);
+                pnote->param = 0;
+            }
+
+            if (packb & 0x10)
+            {
+                // PACK 0x10: Read effect parameter
+                JSGETBYTE(pnote->param);
+                if (pnote->effect == jsetNotSet && pnote->param != 0)
+                    pnote->effect = 0;
+            }
+        }
+        else
+        {
+            // All data available
+            pnote->note = jssXMConvertNote(packb & 0x7f);
+
+            // Get instrument
+            JSGETBYTE(tmp);
+            pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
+
+            // Get volume
+            JSGETBYTE(tmp);
+            pnote->volume = (tmp >= 0x10) ? tmp - 0x10 : jsetNotSet;
+
+            // Get effect
+            JSGETBYTE(pnote->effect);
+
+            // Get parameter
+            JSGETBYTE(pnote->param);
+            if (pnote->effect == 0 && pnote->param == 0)
+                pnote->effect = pnote->param = jsetNotSet;
+        }
+        pnote++;
+    }
+
+    // Check the state
+    if (size > 0)
+    {
+        // Some data left unparsed
+        JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA,
+        "Unparsed data after pattern (%i bytes), possibly broken file.\n", size);
+    }
+
+    return DMERR_OK;
+}
+
+
+/* Convert XM envelope structure to JSS envelope structure
+ */
+static int jssXMConvertEnvelope(JSSEnvelope * d, XMEnvelope * s, char * e, int instr)
+{
+    int i;
+    (void) e; (void) instr;
+
+    // Convert envelope points
+    for (i = 0; i < XM_MaxEnvPoints; i++)
+    {
+        d->points[i].frame = s->points[i].frame;
+        d->points[i].value = s->points[i].value;
+    }
+    
+    // Convert other values
+    d->npoints = s->npoints;
+    d->sustain = s->sustain;
+    d->loopS = s->loopS;
+    d->loopE = s->loopE;
+    
+    // Check if the envelope is used
+    if (s->flags & 0x01)
+    {
+        // Convert envelope flags
+        d->flags = jenvfUsed;
+        if (s->flags & 0x02)
+            d->flags |= jenvfSustain;
+
+        if (s->flags & 0x04)
+            d->flags |= jenvfLooped;
+
+        // Check other values
+        if (s->npoints > XM_MaxEnvPoints)
+        {
+            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+            "Inst#%i/%s-env: nPoints > MAX, possibly broken file.\n", instr, e);
+            s->npoints = XM_MaxEnvPoints;
+        }
+
+        if ((d->flags & jenvfSustain) && s->sustain > s->npoints)
+        {
+            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+            "Inst#%i/%s-env: iSustain > nPoints (%i > %i), possibly broken file.\n",
+            instr, e, s->sustain, s->npoints);
+            s->sustain = s->npoints;
+        }
+
+        if ((d->flags & jenvfLooped) && s->loopE > s->npoints)
+        {
+            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+            "Inst#%i/%s-env: loopE > nPoints (%i > %i), possibly broken file.\n",
+            instr, e, s->loopE, s->npoints);
+            s->loopE = s->npoints;
+        }
+
+        if ((d->flags & jenvfLooped) && s->loopS > s->loopE)
+        {
+            JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+            "Inst#%i/%s-env: loopS > loopE (%i > %i), possibly broken file.\n",
+            instr, e, s->loopS, s->loopE);
+            s->loopS = 0;
+        }
+    }
+
+    return DMERR_OK;
+}
+
+
+/* Load XM-format extended instrument from file-stream into JSS module's given inst
+ */
+static int jssXMLoadExtInstrument(DMResource *inFile, int ninst, JSSModule *module)
+{
+    XMInstrument1 xmI1;
+    off_t pos, remainder;
+
+    // Get instrument header #1
+    pos = dmftell(inFile);
+    dmf_read_le32(inFile, &xmI1.headSize);
+    dmf_read_str(inFile, &xmI1.instName, sizeof(xmI1.instName));
+    xmI1.instType = dmfgetc(inFile);
+    dmf_read_le16(inFile, &xmI1.nsamples);
+
+    // If there are samples, there is header #2
+    if (xmI1.nsamples > 0)
+    {
+        int i, nsample, tmp;
+        int xmConvTable[XM_MaxInstruments + 1];
+        JSSExtInstrument *pEInst;
+        JSSInstrument *pInst;
+        XMInstrument2 xmI2;
+
+        // Allocate instrument
+        if ((pEInst = jssAllocateExtInstrument()) == NULL)
+        {
+            JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+            "Could not allocate extended instrument structure #%i\n", ninst);
+        }
+
+        module->extInstruments[ninst] = pEInst;
+
+        // Get instrument header #2
+        dmf_read_le32(inFile, &xmI2.headSize);
+        dmf_read_str(inFile, &xmI2.sNumForNotes, sizeof(xmI2.sNumForNotes));
+
+        for (i = 0; i < XM_MaxEnvPoints; i++)
+        {
+            dmf_read_le16(inFile, &xmI2.volumeEnv.points[i].frame);
+            dmf_read_le16(inFile, &xmI2.volumeEnv.points[i].value);
+        }
+
+        for (i = 0; i < XM_MaxEnvPoints; i++)
+        {
+            dmf_read_le16(inFile, &xmI2.panningEnv.points[i].frame);
+            dmf_read_le16(inFile, &xmI2.panningEnv.points[i].value);
+        }
+
+        xmI2.volumeEnv.npoints = dmfgetc(inFile);
+        xmI2.panningEnv.npoints = dmfgetc(inFile);
+
+        xmI2.volumeEnv.sustain = dmfgetc(inFile);
+        xmI2.volumeEnv.loopS = dmfgetc(inFile);
+        xmI2.volumeEnv.loopE = dmfgetc(inFile);
+
+        xmI2.panningEnv.sustain = dmfgetc(inFile);
+        xmI2.panningEnv.loopS = dmfgetc(inFile);
+        xmI2.panningEnv.loopE = dmfgetc(inFile);
+
+        xmI2.volumeEnv.flags = dmfgetc(inFile);
+        xmI2.panningEnv.flags = dmfgetc(inFile);
+
+        xmI2.vibratoType = dmfgetc(inFile);
+        xmI2.vibratoSweep = dmfgetc(inFile);
+        xmI2.vibratoDepth = dmfgetc(inFile);
+        xmI2.vibratoRate = dmfgetc(inFile);
+
+        dmf_read_le16(inFile, &xmI2.fadeOut);
+        dmf_read_le16(inFile, &xmI2.ARESERVED);
+
+        // Skip the extra data after header #2
+        remainder = xmI1.headSize - (dmftell(inFile) - pos);
+        if (remainder > 0)
+        {
+            JSSDEBUG("xmI1#1 Skipping: %li\n", remainder);
+            dmfseek(inFile, remainder, SEEK_CUR);
+        }
+
+        // Check and convert all ext instrument information
+#ifndef JSS_LIGHT
+        pEInst->desc = jssASCIItoStr(xmI1.instName, 0, sizeof(xmI1.instName));
+#endif
+        jssXMConvertEnvelope(&pEInst->volumeEnv, &xmI2.volumeEnv, "vol", ninst);
+        jssXMConvertEnvelope(&pEInst->panningEnv, &xmI2.panningEnv, "pan", ninst);
+
+        switch (xmI2.vibratoType)
+        {
+            case 0: pEInst->vibratoType = jvibSine; break;
+            case 1: pEInst->vibratoType = jvibRamp; break;
+            case 2: pEInst->vibratoType = jvibSquare; break;
+            case 3: pEInst->vibratoType = jvibRandom; break;
+            default:
+                pEInst->vibratoType = jvibSine;
+                JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                "Invalid extinstrument vibrato type %d for inst #%d\n", ninst);
+                break;
+        }
+        pEInst->vibratoSweep = xmI2.vibratoSweep;
+        pEInst->vibratoDepth = xmI2.vibratoDepth;
+        pEInst->vibratoRate = xmI2.vibratoRate;
+        pEInst->fadeOut = xmI2.fadeOut;
+        pEInst->nsamples = xmI1.nsamples;
+
+        // Initialize the SNumForNotes conversion table
+        for (i = 0; i < XM_MaxInstruments; i++)
+            xmConvTable[i] = jsetNotSet;
+
+        // Read sample headers
+        for (nsample = 0; nsample < xmI1.nsamples; nsample++)
+        {
+            XMSample xmS;
+
+            // Read header data
+            dmf_read_le32(inFile, &xmS.size);
+            dmf_read_le32(inFile, &xmS.loopS);
+            dmf_read_le32(inFile, &xmS.loopL);
+            xmS.volume = dmfgetc(inFile);
+            xmS.fineTune = (signed char) dmfgetc(inFile);
+            xmS.type = dmfgetc(inFile);
+            xmS.panning = dmfgetc(inFile);
+            xmS.relNote = (signed char) dmfgetc(inFile);
+            xmS.ARESERVED = dmfgetc(inFile);
+            dmf_read_str(inFile, &xmS.sampleName, sizeof(xmS.sampleName));
+
+            if (xmS.size > 0)
+            {
+                // Allocate sample instrument
+                JSSDEBUG("Allocating sample #%i/%i [%i]\n",
+                ninst, nsample, module->ninstruments);
+
+                xmConvTable[nsample] = module->ninstruments;
+                pInst = module->instruments[module->ninstruments] = jssAllocateInstrument();
+                if (pInst == NULL)
+                {
+                    JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+                    "Could not allocate sample #%i/%i [%i]\n",
+                    ninst, nsample, module->ninstruments);
+                }
+                module->ninstruments++;
+            } else
+                pInst = NULL;
+
+            // Check and convert sample information
+            if (pInst != NULL)
+            {
+                // Copy values
+                if (xmS.volume > XM_MaxSampleVolume)
+                {
+                    JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                           "Samp #%i/%i: volume > MAX\n", ninst, nsample);
+                    xmS.volume = XM_MaxSampleVolume;
+                }
+
+                pInst->volume = xmS.volume;
+                pInst->ERelNote = xmS.relNote;
+                pInst->EFineTune = xmS.fineTune;
+                pInst->EPanning = xmS.panning;
+#ifndef JSS_LIGHT
+                pInst->desc = jssASCIItoStr(xmS.sampleName, 0, sizeof(xmS.sampleName));
+#endif
+
+                // Convert flags
+                switch (xmS.type & 0x03)
+                {
+                    case 0: pInst->flags = 0; break;
+                    case 1: pInst->flags = jsfLooped; break;
+                    case 2: pInst->flags = jsfLooped | jsfBiDi; break;
+                    default:
+                        pInst->flags = 0;
+                        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                        "Samp #%i/%i: Invalid sample type 0x%x\n",
+                        ninst, nsample, xmS.type);
+                        break;
+                }
+
+                if (xmS.type & 0x10)
+                {
+                    // 16-bit sample
+                    JSFSET(pInst->flags, jsf16bit);
+
+                    pInst->size = xmS.size / sizeof(Uint16);
+                    pInst->loopS = xmS.loopS / sizeof(Uint16);
+                    pInst->loopE = ((xmS.loopS + xmS.loopL) / sizeof(Uint16));
+                }
+                else
+                {
+                    // 8-bit sample
+                    pInst->size = xmS.size;
+                    pInst->loopS = xmS.loopS;
+                    pInst->loopE = (xmS.loopS + xmS.loopL);
+                }
+                
+                if (xmS.loopL == 0)
+                {
+                    // Always unset loop, if loop length is zero
+                    JSFUNSET(pInst->flags, jsfLooped);
+                }
+
+                if (pInst->flags & jsfLooped)
+                {
+                    if (pInst->loopS >= pInst->size)
+                    {
+                        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                        "Samp #%i/%i: loopS >= size (%d >= %d)\n",
+                        ninst, nsample, pInst->loopS, pInst->size);
+                        JSFUNSET(pInst->flags, jsfLooped);
+                    }
+
+                    if (pInst->loopE > pInst->size)
+                    {
+                        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                        "Samp #%i/%i: loopE > size (%d > %d)\n",
+                        ninst, nsample, pInst->loopE, pInst->size);
+                        JSFUNSET(pInst->flags, jsfLooped);
+                    }
+                }
+
+
+                // Allocate memory for sample data
+                if (pInst->flags & jsf16bit)
+                    pInst->data = dmCalloc(pInst->size, sizeof(Uint16));
+                else
+                    pInst->data = dmCalloc(pInst->size, sizeof(Uint8));
+
+                if (pInst->data == NULL)
+                    JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+                    "Could not allocate sample data #%i/%i.\n", ninst, nsample);
+            }
+        }
+
+        // Read sample data
+        for (nsample = 0; nsample < xmI1.nsamples; nsample++)
+        if (xmConvTable[nsample] != jsetNotSet)
+        {
+            pInst = module->instruments[xmConvTable[nsample]];
+            if (pInst)
+            {
+                JSSDEBUG("desc....: '%s'\n"
+                     "size....: %i\n"
+                     "loopS...: %i\n"
+                     "loopE...: %i\n"
+                     "volume..: %i\n"
+                     "flags...: %x\n",
+                     pInst->desc,
+                     pInst->size, pInst->loopS, pInst->loopE,
+                     pInst->volume, pInst->flags);
+
+                if (pInst->flags & jsf16bit)
+                {
+                    // Read sampledata
+                    if (dmfread(pInst->data, sizeof(Uint16), pInst->size, inFile) != (size_t) pInst->size)
+                        JSSERROR(DMERR_FREAD, DMERR_FREAD,
+                        "Error reading sampledata for instrument #%i/%i, %i words.\n",
+                        ninst, nsample, pInst->size);
+
+                    // Convert data
+                    jssDecodeSample16((Uint16 *) pInst->data, pInst->size,
+#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+                               (jsampDelta | jsampSwapEndianess)
+#else
+                               (jsampDelta)
+#endif
+                    );
+                }
+                else
+                {
+                    // Read sampledata
+                    if (dmfread(pInst->data, sizeof(Uint8), pInst->size, inFile) != (size_t) pInst->size)
+                        JSSERROR(DMERR_FREAD, DMERR_FREAD,
+                        "Error reading sampledata for instrument #%i/%i, %i bytes.\n",
+                        ninst, nsample, pInst->size);
+
+                    // Convert data
+                    jssDecodeSample8((Uint8 *) pInst->data, pInst->size,
+                        (jsampDelta | jsampFlipSign));
+                }
+            }
+        }
+
+        // Apply new values to sNumForNotes values
+        for (i = 0; i < XM_MaxNotes; i++)
+        {
+            tmp = xmI2.sNumForNotes[i];
+            if (tmp >= 0 && tmp < xmI1.nsamples)
+                pEInst->sNumForNotes[i] = xmConvTable[tmp];
+            else
+            {
+                pEInst->sNumForNotes[i] = jsetNotSet;
+                JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                "Ext.instrument #%d sNumForNotes[%d] out of range (%d).\n",
+                ninst, i, tmp);
+            }
+        }
+    }
+    else
+    {
+        // We may STILL need to skip extra data after 1st instr. header
+        remainder = xmI1.headSize - (dmftell(inFile) - pos);
+        if (remainder > 0)
+        {
+            JSSDEBUG("xmI1#2 Skipping: %li\n", remainder);
+            dmfseek(inFile, remainder, SEEK_CUR);
+        }
+    }
+
+    return 0;
+}
+
+
+/* Load XM-format module from given file-stream
+ */
+int jssLoadXM(DMResource *inFile, JSSModule **ppModule)
+{
+    JSSModule *module;
+    XMHeader xmH;
+    int result, index, tmp;
+
+    assert(ppModule != NULL);
+    assert(inFile != NULL);
+    *ppModule = NULL;
+
+    /* Get XM-header and check it
+     */
+    dmf_read_str(inFile, &xmH.idMagic, sizeof(xmH.idMagic));
+    dmf_read_str(inFile, &xmH.songName, sizeof(xmH.songName));
+    xmH.unUsed1A = dmfgetc(inFile);
+    dmf_read_str(inFile, &xmH.trackerName, sizeof(xmH.trackerName));
+    dmf_read_le16(inFile, &xmH.version);
+    dmf_read_le32(inFile, &xmH.headSize);
+    dmf_read_le16(inFile, &xmH.norders);
+    dmf_read_le16(inFile, &xmH.defRestartPos);
+    dmf_read_le16(inFile, &xmH.nchannels);
+    dmf_read_le16(inFile, &xmH.npatterns);
+    dmf_read_le16(inFile, &xmH.ninstruments);
+    dmf_read_le16(inFile, &xmH.flags);
+    dmf_read_le16(inFile, &xmH.defSpeed);
+    dmf_read_le16(inFile, &xmH.defTempo);
+    dmf_read_str(inFile, &xmH.orderList, sizeof(xmH.orderList));
+
+
+    // Check the fields, none of these are considered fatal
+    if (strncmp(xmH.idMagic, "Extended Module: ", 17) != 0)
+    {
+        JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
+        "Not a FT2 Extended Module (XM), ident mismatch!\n");
+    }
+
+    if (xmH.version != 0x0104)
+    {
+        JSSWARNING(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
+        "Unsupported version of XM format 0x%04x instead of expected 0x0104.\n",
+        xmH.version);
+    }
+
+    if (xmH.unUsed1A != 0x1a)
+    {
+        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Possibly modified or corrupted XM [%x]\n", xmH.unUsed1A);
+    }
+
+    if (xmH.norders > XM_MaxOrders)
+    {
+        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Number of orders %i > %i, possibly broken module.\n",
+        xmH.norders, XM_MaxOrders);
+        xmH.norders = XM_MaxOrders;
+    }
+
+    if (xmH.norders == 0)
+    {
+        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Number of orders was zero.\n");
+    }
+
+    if (xmH.npatterns > XM_MaxPatterns)
+    {
+        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Number of patterns %i > %i, possibly broken module.\n",
+        xmH.npatterns, XM_MaxPatterns);
+        xmH.npatterns = XM_MaxPatterns;
+    }
+
+    if (xmH.npatterns == 0)
+    {
+        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Number of patterns was zero.\n");
+    }
+
+    if (xmH.nchannels <= 0 || xmH.nchannels > XM_MaxChannels)
+    {
+        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Number of channels was invalid, %i (should be 1 - %i).\n",
+        xmH.nchannels, XM_MaxChannels);
+    }
+
+    if (xmH.ninstruments <= 0 || xmH.ninstruments > XM_MaxInstruments)
+    {
+        JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+        "Number of instruments was invalid, %i (should be 1 - %i).\n",
+        xmH.ninstruments, XM_MaxInstruments);
+    }
+
+    /* Okay, allocate a module structure
+     */
+    module = jssAllocateModule();
+    if (module == NULL)
+    {
+        JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+        "Could not allocate memory for module structure.\n");
+    }
+    *ppModule = module;
+
+
+    // Convert and check the header data
+    module->moduleType      = jmdtXM;
+    module->intVersion      = xmH.version;
+#ifndef JSS_LIGHT
+    module->moduleName      = jssASCIItoStr(xmH.songName, 0, sizeof(xmH.songName));
+    module->trackerName     = jssASCIItoStr(xmH.trackerName, 0, sizeof(xmH.trackerName));
+#endif
+    module->defSpeed        = xmH.defSpeed;
+    module->defTempo        = xmH.defTempo;
+    module->nextInstruments = xmH.ninstruments;
+    module->ninstruments    = 0;
+    module->npatterns       = xmH.npatterns;
+    module->norders         = xmH.norders;
+    module->nchannels       = xmH.nchannels;
+    module->defFlags        = jmdfStereo | jmdfFT2Replay;
+    module->defRestartPos   = xmH.defRestartPos;
+
+    if ((xmH.flags & 1) == 0)
+        module->defFlags |= jmdfAmigaPeriods;
+
+    // Setup the default pannings
+    for (index = 0; index < jsetNChannels; index++)
+        module->defPanning[index] = jchPanMiddle;
+
+    /* Read patterns
+     */
+    for (index = 0; index < module->npatterns; index++)
+    {
+        off_t pos, remainder;
+        XMPattern xmP;
+
+        // Get the pattern header
+        pos = dmftell(inFile);
+        dmf_read_le32(inFile, &xmP.headSize);
+        xmP.packing = dmfgetc(inFile);
+        dmf_read_le16(inFile, &xmP.nrows);
+        dmf_read_le16(inFile, &xmP.size);
+
+        // Check the header
+        if (xmP.packing != 0)
+            JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+            "Pattern #%i packing type unsupported (%i)\n",
+            index, xmP.packing);
+
+        if (xmP.nrows == 0)
+            JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+            "Pattern #%i has %i rows, invalid data.\n",
+            index, xmP.nrows);
+
+        if (xmP.size > 0)
+        {
+            // Allocate and unpack pattern
+            module->patterns[index] = jssAllocatePattern(xmP.nrows, module->nchannels);
+            if (module->patterns[index] == NULL)
+                JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
+                "Could not allocate memory for pattern #%i\n", index);
+
+            result = jssXMUnpackPattern(inFile, xmP.size, module->patterns[index]);
+            if (result != 0)
+                JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index);
+        }
+
+        // Skip extra data (if the file is damaged)
+        remainder = xmP.headSize - (dmftell(inFile) - pos);
+        if (remainder > 0)
+        {
+            JSSDEBUG("xmP Skipping: %li\n", remainder);
+            dmfseek(inFile, remainder, SEEK_CUR);
+        }
+    }
+
+    // Allocate the empty pattern
+    module->patterns[jsetMaxPatterns] = jssAllocatePattern(64, module->nchannels);
+
+    /* Convert song orders list by replacing nonexisting patterns
+     * with pattern number jsetMaxPatterns.
+     */
+    for (index = 0; index < module->norders; index++)
+    {
+        tmp = xmH.orderList[index];
+        if (tmp >= module->npatterns || module->patterns[tmp] == NULL)
+            tmp = jsetMaxPatterns;
+
+        module->orderList[index] = tmp;
+    }
+
+    /* Read instruments
+     */
+    for (index = 0; index < module->nextInstruments; index++)
+    {
+        result = jssXMLoadExtInstrument(inFile, index, module);
+        if (result != 0)
+            JSSERROR(result, result, "Errors while reading instrument #%i\n", index);
+    }
+
+    return DMERR_OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jmix_c_in.c	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,93 @@
+/*
+ * miniJSS - Mixing routines in C
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
+ */
+
+//#define JMIXER_DEBUG fprintf(stderr, "[%.8x:%.8x / %.8x]: %d\n", FP_GETH(tmpPos), FP_GETL(tmpPos), endPos, sp[FP_GETH(tmpPos)]);
+#define JMIXER_DEBUG
+#define JMIXER_ADDBUF_TYPE Sint32
+
+
+/* Mono / Linear Interpolation
+ */
+#define JMIXER_SAMPLE_TYPE Sint16
+#define JMIXER_INIT        
+#define JMIXER_FUNC \
+    const Sint32 vol = (FP_GETH(tmpVolume) * mixer->globalVol) / 256; \
+    memmove(&tl[0], &tl[1], 4 * sizeof(tl[0])); \
+    tl[4] = (((Sint32) sp[FP_GETH(tmpPos)]) * vol * 3 + tl[3] * 2 + tl[2] + tl[1] + tl[0]) / (256 * 8); *(ap++) += tl[4]; \
+
+#define JMIXER_NAME        jvmMix_Mono_C_FW
+#define JMIXER_NEXT        FP_ADD(tmpPos, tmpDelta);
+#define JMIXER_ENDCOND     (tmpPos.dw < endPos.dw)
+#include "jmixtmpl_c.h"
+
+#define JMIXER_NAME        jvmMix_Mono_C_BW
+#define JMIXER_NEXT        FP_SUB(tmpPos, tmpDelta);
+#define JMIXER_ENDCOND     (tmpPos.dw > endPos.dw)
+#include "jmixtmpl_c.h"
+
+
+#undef JMIXER_SAMPLE_TYPE
+#undef JMIXER_INIT
+#undef JMIXER_FUNC
+
+
+/* Stereo / Linear Interpolation
+ */
+#define JMIXER_SAMPLE_TYPE Sint16
+#define JMIXER_INIT
+
+#define JMIXER_FUNC \
+    const Sint32 vol_l = (FP_GETH(tmpVolume) * mixer->globalVol) / 256, \
+                 vol_r = (FP_GETH(tmpVolume) * mixer->globalVol) / 256; \
+    memmove(&tl[0], &tl[1], 4 * sizeof(tl[0])); \
+    memmove(&tr[0], &tr[1], 4 * sizeof(tr[0])); \
+    tl[4] = (((Sint32) sp[FP_GETH(tmpPos)]) * vol_l * 3 + tl[3] * 2 + tl[2] + tl[1] + tl[0]) / (256 * 8); *(ap++) += tl[4]; \
+    tr[4] = (((Sint32) sp[FP_GETH(tmpPos)]) * vol_r * 3 + tr[3] * 2 + tr[2] + tr[1] + tr[0]) / (256 * 8); *(ap++) += tr[4];
+
+#define JMIXER_NAME        jvmMix_Stereo_C_FW
+#define JMIXER_NEXT        FP_ADD(tmpPos, tmpDelta);
+#define JMIXER_ENDCOND     (tmpPos.dw < endPos.dw)
+#include "jmixtmpl_c.h"
+
+#define JMIXER_NAME        jvmMix_Stereo_C_BW
+#define JMIXER_NEXT        FP_SUB(tmpPos, tmpDelta);
+#define JMIXER_ENDCOND     (tmpPos.dw > endPos.dw)
+#include "jmixtmpl_c.h"
+
+
+#undef JMIXER_SAMPLE_TYPE
+#undef JMIXER_INIT
+#undef JMIXER_FUNC
+
+
+/* Post processing functions
+ */
+#define JMIXER_CLAMP \
+    if (t < JVM_LIMIT_16_NEG) t = JVM_LIMIT_16_NEG; else    \
+    if (t > JVM_LIMIT_16_POS) t = JVM_LIMIT_16_POS;
+
+#define JMIXER_NAME jvmPostProcess_U8_C
+#define JMIXER_TYPE Uint8
+#define JMIXER_FUNCTION *(sp++) = (t + JVM_ADD_16) >> 8;
+#include "jmix_post_c.h"
+
+
+#define JMIXER_NAME jvmPostProcess_S8_C
+#define JMIXER_TYPE Sint8
+#define JMIXER_FUNCTION *(sp++) = t >> 8;
+#include "jmix_post_c.h"
+
+
+#define JMIXER_NAME jvmPostProcess_U16_C
+#define JMIXER_TYPE Uint16
+#define JMIXER_FUNCTION *(sp++) = t + JVM_ADD_16;
+#include "jmix_post_c.h"
+
+
+#define JMIXER_NAME jvmPostProcess_S16_C
+#define JMIXER_TYPE Sint16
+#define JMIXER_FUNCTION *(sp++) = t;
+#include "jmix_post_c.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jmix_post_c.h	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,27 @@
+/*
+ * miniJSS - Post-processing routine template
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2005-2007 Tecnic Software productions (TNSP)
+ */
+
+#ifdef JMIXER_HEADER
+void JMIXER_NAME (JMIXER_ADDBUF_TYPE *, void *, const int);
+#else
+void JMIXER_NAME (JMIXER_ADDBUF_TYPE *addBuffer, void *mixBuffer, const int mixLength)
+{
+    int strideLength = mixLength;
+    JMIXER_ADDBUF_TYPE *ap = addBuffer, t;
+    JMIXER_TYPE *sp = (JMIXER_TYPE *) mixBuffer;
+
+    while (strideLength--)
+    {
+        t = *(ap++);
+        JMIXER_CLAMP
+        JMIXER_FUNCTION
+    }
+}
+#endif
+
+#undef JMIXER_NAME
+#undef JMIXER_TYPE
+#undef JMIXER_FUNCTION
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jmixtmpl_c.h	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,57 @@
+/*
+ * miniJSS - Mixing routine in C, innerloop template
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
+ */
+
+int JMIXER_NAME (JSSMixer *mixer, JSSChannel *chn, JMIXER_ADDBUF_TYPE *addBuffer, const int mixLength, const DMFixedPoint endPos)
+#ifdef JMIXER_HEADER
+;
+#else
+{
+    const DMFixedPoint tmpDelta = chn->chDeltaO, tmpDeltaV = chn->chDeltaV;
+    DMFixedPoint tmpPos = chn->chPos,
+                 tmpVolume = chn->chVolume;
+    JMIXER_ADDBUF_TYPE
+        *ap = addBuffer,
+        *tr = chn->chPrevR,
+        *tl = chn->chPrevL;
+    int strideLength = 0;
+
+    JMIXER_SAMPLE_TYPE *sp = (JMIXER_SAMPLE_TYPE *) chn->chData;
+
+    JMIXER_INIT
+
+    if (chn->chVolumeD > 0)
+    {
+        const int tmpLength = chn->chVolumeD < mixLength ? chn->chVolumeD : mixLength;
+        chn->chVolumeD -= tmpLength;
+
+        while (strideLength < tmpLength && JMIXER_ENDCOND)
+        {
+            JMIXER_FUNC
+            JMIXER_DEBUG
+            JMIXER_NEXT
+            FP_ADD(tmpVolume, tmpDeltaV);
+            strideLength++;
+        }
+    }
+    
+    while (strideLength < mixLength && JMIXER_ENDCOND)
+    {
+        JMIXER_FUNC
+        JMIXER_DEBUG
+        JMIXER_NEXT
+        strideLength++;
+    }
+
+    chn->chPos = tmpPos;
+    chn->chVolume = tmpVolume;
+    return strideLength;
+}
+#endif
+
+#undef JMIXER_NAME
+#undef JMIXER_NEXT
+#undef JMIXER_ENDCOND
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jss.c	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,79 @@
+/*
+ * miniJSS - General functions
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
+ */
+#include "jss.h"
+#include <stdarg.h>
+
+
+/* Memory and error handling functions
+ */
+
+BOOL jssWarningIsFatal, jssErrorIsFatal;
+
+#ifndef JSS_LIGHT
+void (*jssError) (int code, const char *filename, int linen, const char *fmt, ...);
+void (*jssWarning) (int code, const char *filename, int linen, const char *fmt, ...);
+#endif
+
+
+void jssDefaultPrint(int code, const char *filename, int linen, const char *fmt)
+{
+    fprintf(stderr, "JSS");
+    if (filename)
+        fprintf(stderr, "[%s:%i]", filename, linen);
+    fprintf(stderr, "%s", fmt);
+    if (code > 0)
+        fprintf(stderr, "(%i)", code);
+    fprintf(stderr, ": ");
+}
+
+
+void jssDefaultError(int code, const char *filename, int linen, const char *fmt, ...)
+{
+    va_list ap;
+    jssDefaultPrint(code, filename, linen, "E");
+
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+}
+
+
+void jssDefaultWarning(int code, const char *filename, int linen, const char *fmt, ...)
+{
+    va_list ap;
+    jssDefaultPrint(code, filename, linen, "W");
+
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+}
+
+
+/* System initialization
+ */
+int jssInit(void)
+{
+    // Error handling
+    jssWarningIsFatal = FALSE;
+    jssErrorIsFatal = TRUE;
+
+#ifndef JSS_LIGHT
+    jssError = jssDefaultError;
+    jssWarning = jssDefaultWarning;
+#endif
+
+    // Allocate global tables
+
+    return DMERR_OK;
+}
+
+
+/* System shutdown
+ */
+int jssClose(void)
+{
+    return DMERR_OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jss.h	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,105 @@
+/*
+ * miniJSS - Main header file
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
+ */
+#ifndef JSS_H
+#define JSS_H
+
+#include "dmlib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Locking
+ */
+#ifdef JSS_SUP_THREADS
+#    define JSS_LOCK(Q) dmMutexLock((Q)->mutex)
+#    define JSS_UNLOCK(Q) dmMutexUnlock((Q)->mutex)
+#else
+#    warning DANGER! JSS threads support not included!
+#    define JSS_LOCK(Q)
+#    define JSS_UNLOCK(Q)
+#endif
+
+
+// Global settings
+#define jsetNChannels   (64)        // Max number of channels
+#define jsetNotSet      (-1)        // A global "not set" constant
+#define jsetMinVol      (0)
+#define jsetMaxVol      (255)
+
+// Sample instrument flags
+#define jsfLooped       (0x01)      // Is sample looped
+#define jsfBiDi         (0x02)      // Bi-directional loop?
+#define jsf16bit        (0x04)      // 16-bit?
+
+#define JSFSET(a, b)    do { a |= b; } while (0)
+#define JSFUNSET(a, b)  do { a &= (0xff ^ b); } while (0)
+
+// Panning position
+#define jchPanLeft      (0x00)      // Leftmost pan
+#define jchPanMiddle    (0x80)      // Center pan
+#define jchPanRight     (0xff)      // Rightmost pan
+
+// Audio formats
+enum
+{
+    JSS_AUDIO_U8,           // 8-bit formats
+    JSS_AUDIO_S8,
+    JSS_AUDIO_U16,          // 16-bit formats
+    JSS_AUDIO_S16,
+    JSS_AUDIO_U32,          // 32-bit (24+padding)
+    JSS_AUDIO_S32
+} JSS_AUDIO_FMT;
+
+
+enum
+{
+    JSS_AUDIO_MONO = 1,
+    JSS_AUDIO_STEREO = 2
+} JSS_AUDIO_CHANNELS;
+
+
+/* System initialization and shutdown
+ */
+int    jssInit(void);       // Initialization. Call before anything else!
+int    jssClose(void);      // Shutdown. Do not call ANY JSS routines after this!
+
+
+/* Error handling routines and related variables
+ */
+extern BOOL     jssWarningIsFatal,  // if TRUE, warnings are considered fatal -> function returns
+                jssErrorIsFatal;    // if FALSE, error is considered non-fatal. this may cause strange problems.
+
+#ifndef JSS_LIGHT
+extern void     (*jssError)(int code, const char *filename, int linen, const char *fmt, ...);
+extern void     (*jssWarning)(int code, const char *filename, int linen, const char *fmt, ...);
+#endif
+
+
+/* If JSS_LIGHT is NOT defined, we add code for verbose error-, warning-
+ * and debug-messages. Otherwise use a macro stub.
+ */
+#ifndef JSS_LIGHT
+#  define JSSERROR(MEVAL, MRET, ...) do { jssError(MEVAL, __FILE__, (int) __LINE__, __VA_ARGS__); if (jssErrorIsFatal) return MRET; } while (0)
+#  define JSSWARNING(MEVAL, MRET, ...) do { jssWarning(MEVAL, __FILE__, (int) __LINE__, __VA_ARGS__); if (jssWarningIsFatal) return MRET; } while (0)
+#  ifdef JSS_DEBUG
+#    define JSSDEBUG(...) do { fprintf(stderr, "[%s:%d]: ", __FILE__, (int) __LINE__); fprintf(stderr, __VA_ARGS__); } while (0)
+#  else
+#    define JSSDEBUG(...) do { } while (0)
+#  endif // NDEBUG
+#else
+#  define JSSERROR(MEVAL, MRET, ...) do { return MRET; } while (0)
+#  define JSSWARNING(MEVAL, MRET, ...) do { } while (0)
+#  define JSSDEBUG(...) do { } while (0)
+#endif // JSS_LIGHT
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // JSS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jssmix.c	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,712 @@
+/*
+ * miniJSS - Mixing device and channel handling
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
+ */
+#include "jssmix.h"
+#include <string.h>
+
+
+#ifdef DM_USE_C
+#define JMIXER_HEADER
+#include "jmix_c_in.c"
+#undef JMIXER_HEADER
+#endif
+
+#undef DM_USE_SIMD
+
+#ifdef DM_USE_SIMD
+#define JMIXER_HEADER
+#include "jmix_mmx_in.c"
+#undef JMIXER_HEADER
+#endif
+
+
+typedef struct
+{
+    int    mixerID;
+    int    outFormat;
+    int    outChannels;
+
+    int    (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
+    int    (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
+    void   (*jvmPostProcess)(JMIXER_ADDBUF_TYPE *, void *, const int);
+} JSSMixingRoutine;
+
+
+/* This table should be sorted from fastest to slowest, e.g. MMX/x86
+ * optimized routines first, pure C versions last.
+ */
+static JSSMixingRoutine jvmMixRoutines[] =
+{
+#ifdef DM_USE_SIMD
+{ JMIX_MMX, JSS_AUDIO_U8,  JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U8_MMX },
+{ JMIX_MMX, JSS_AUDIO_S8,  JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S8_MMX },
+{ JMIX_MMX, JSS_AUDIO_U8,  JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U8_MMX },
+{ JMIX_MMX, JSS_AUDIO_S8,  JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S8_MMX },
+
+{ JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U16_MMX },
+{ JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_MONO,   jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S16_MMX },
+{ JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U16_MMX },
+{ JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S16_MMX },
+#endif
+
+#ifdef DM_USE_C
+{ JMIX_C,   JSS_AUDIO_U8,  JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U8_C },
+{ JMIX_C,   JSS_AUDIO_S8,  JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S8_C },
+{ JMIX_C,   JSS_AUDIO_U8,  JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U8_C },
+{ JMIX_C,   JSS_AUDIO_S8,  JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S8_C },
+
+{ JMIX_C,   JSS_AUDIO_U16, JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U16_C },
+{ JMIX_C,   JSS_AUDIO_S16, JSS_AUDIO_MONO,   jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S16_C },
+{ JMIX_C,   JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U16_C },
+{ JMIX_C,   JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S16_C },
+#endif
+};
+
+static const int jvmNMixRoutines = sizeof(jvmMixRoutines) / sizeof(jvmMixRoutines[0]);
+
+
+static int jvmFindMixRoutine(int outFormat, int outChannels, int mixerID)
+{
+    int i;
+    
+    for (i = 0; i < jvmNMixRoutines; i++)
+    {
+        if (jvmMixRoutines[i].outFormat == outFormat &&
+            jvmMixRoutines[i].outChannels == outChannels &&
+            (mixerID == JMIX_AUTO || jvmMixRoutines[i].mixerID == mixerID))
+            return i;
+    }
+    
+    return -1;
+}
+
+
+JSSMixer *jvmInit(const int outFormat, const int outChannels, const int outFreq, const int mixerID)
+{
+    JSSMixer *mixer;
+    int mixerIdx;
+
+    // Check settings
+    if (outChannels < 1)
+    {
+        JSSERROR(DMERR_INVALID_ARGS, NULL,
+        "Invalid number of channels %d\n", outChannels);
+    }
+    
+    if (outFreq < 4000)
+    {
+        JSSERROR(DMERR_INVALID_ARGS, NULL,
+        "Invalid mixing frequency %d\n", outFreq);
+    }
+    
+    /* Select mixing routines:
+     * Here we try to choose the most fitting mixing routines
+     * from the compiled in routines, unless caller is forcing
+     * us to select specific ones.
+     */
+    if (mixerID == JMIX_AUTO)
+    {
+        mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_SSE);
+        if (mixerIdx < 0)
+            mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_MMX);
+        if (mixerIdx < 0)
+            mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_AUTO);
+    }
+    else
+    {
+        mixerIdx = jvmFindMixRoutine(outFormat, outChannels, mixerID);
+    }
+
+    if (mixerIdx < 0)
+    {
+        JSSERROR(DMERR_INVALID_ARGS, NULL,
+        "Could not find mixing routine for outFormat=%d, outChannels=%d, outFreq=%d.\n",
+        outFormat, outChannels, outFreq);
+        return NULL;
+    }
+    
+    // Allocate a mixer device structure
+    mixer = dmMalloc0(sizeof(JSSMixer));
+    if (mixer == NULL)
+    {
+        JSSERROR(DMERR_MALLOC, NULL,
+        "Could not allocate mixing device structure.\n");
+    }
+
+    // Initialize variables
+#ifdef JSS_SUP_THREADS
+    mixer->mutex = dmCreateMutex();
+#endif
+    mixer->outFormat = outFormat;
+    mixer->outFreq = outFreq;
+    mixer->outChannels = outChannels;
+    
+    mixer->jvmMixChannel_FW = jvmMixRoutines[mixerIdx].jvmMixChannel_FW;
+    mixer->jvmMixChannel_BW = jvmMixRoutines[mixerIdx].jvmMixChannel_BW;
+    mixer->jvmPostProcess = jvmMixRoutines[mixerIdx].jvmPostProcess;
+    
+    // Allocate addBuffer
+    mixer->addBufSize = outChannels * outFreq * 2;
+    mixer->addBuffer = dmMalloc(mixer->addBufSize * sizeof(JMIXER_ADDBUF_TYPE));
+    if (mixer->addBuffer == NULL)
+    {
+        JSSERROR(DMERR_MALLOC, NULL,
+        "Could not allocate mixing addition buffer.\n");
+    }
+    
+    return mixer;
+}
+
+
+int jvmClose(JSSMixer * mixer)
+{
+    if (mixer == NULL)
+        return DMERR_NULLPTR;
+
+    // Deallocate resources
+#ifdef JSS_SUP_THREADS
+    dmDestroyMutex(mixer->mutex);
+#endif
+    dmFree(mixer->addBuffer);
+
+    memset(mixer, 0, sizeof(JSSMixer));
+    dmFree(mixer);
+
+    return DMERR_OK;
+}
+
+
+int jvmGetSampleSize(JSSMixer *mixer)
+{
+    int sampSize = 1;
+    assert(mixer);
+    
+    switch (mixer->outChannels)
+    {
+        case JSS_AUDIO_STEREO:
+        case JSS_AUDIO_MONO:
+            sampSize = mixer->outChannels;
+            break;
+        default:
+            JSSERROR(DMERR_INVALID_ARGS, -1,
+            "outChannels=%d not stereo or mono!\n", mixer->outChannels);
+            break;
+    }
+    
+    switch (mixer->outFormat)
+    {
+        case JSS_AUDIO_U16: sampSize *= sizeof(Uint16); break;
+        case JSS_AUDIO_S16: sampSize *= sizeof(Sint16); break;
+        case JSS_AUDIO_U8:  sampSize *= sizeof(Uint8); break;
+        case JSS_AUDIO_S8:  sampSize *= sizeof(Sint8); break;
+        default:
+            JSSERROR(DMERR_INVALID_ARGS, -1,
+            "outFormat=%d is not supported!\n", mixer->outFormat);
+    }
+    
+    return sampSize;
+}
+
+
+int jvmGetSampleRes(JSSMixer *mixer)
+{
+    int sampRes = 0;
+    
+    assert(mixer);
+    
+    switch (mixer->outFormat)
+    {
+        case JSS_AUDIO_U16: case JSS_AUDIO_S16: sampRes = 16; break;
+        case JSS_AUDIO_U8:  case JSS_AUDIO_S8:  sampRes = 8; break;
+        default:
+            JSSERROR(DMERR_INVALID_ARGS, -1,
+            "outFormat=%d is not supported!\n", mixer->outFormat);
+    }
+    
+    return sampRes;
+}
+
+
+static void jvmMixChannel(JSSMixer *mixer, JSSChannel *chn, JMIXER_ADDBUF_TYPE *addBuffer, const int mixLength)
+{
+    int mixDone = mixLength, mixResult;
+    JMIXER_ADDBUF_TYPE *ab = addBuffer;
+    
+    if (!chn->chPlaying || chn->chMute)
+        return;
+
+    DBG("%s(%p, %d)\n", __FUNCTION__, chn, mixLength);
+    
+    while (mixDone > 0)
+    {
+        if (chn->chDirection)
+        {
+            // Channel is playing FORWARDS
+            if (chn->chFlags & jsfLooped)
+            {
+                // Sample is looped
+                if (chn->chFlags & jsfBiDi)
+                {
+                    // Bi-directional loop
+                    if (chn->chPos.dw >= chn->chLoopE.dw)
+                    {
+                        DMFixedPoint end;
+                        FP_ADD_R(end, chn->chLoopE, chn->chLoopE);
+                        FP_SUB_R(chn->chPos, end, chn->chPos);
+                        chn->chDirection = FALSE;
+                    }
+                }
+                else
+                {
+                    // Normal forward loop
+                    if (chn->chPos.dw >= chn->chLoopE.dw)
+                    {
+                        DMFixedPoint diff;
+                        FP_SUB_R(diff, chn->chPos, chn->chLoopE);
+                        FP_ADD_R(chn->chPos, chn->chLoopS, diff);
+                    }
+                }
+            }
+            else
+            {
+                // Normal (non-looped) sample
+                if (chn->chPos.dw >= chn->chSize.dw)
+                {
+                    chn->chPlaying = FALSE;
+                    return;
+                }
+            }
+        }
+        else
+        {
+            // Channel is playing BACKWARDS
+            if (chn->chFlags & jsfLooped)
+            {
+                // Sample is looped
+                if (chn->chFlags & jsfBiDi)
+                {
+                    // Bi-directional loop
+                    if (chn->chPos.dw <= chn->chLoopS.dw)
+                    {
+                        DMFixedPoint start;
+                        FP_ADD_R(start, chn->chLoopS, chn->chLoopS);
+                        FP_SUB_R(chn->chPos, start, chn->chPos);
+                        chn->chDirection = TRUE;
+                    }
+                }
+                else
+                {
+                    // Normal forward loop
+                    if (chn->chPos.dw <= chn->chLoopS.dw)
+                    {
+                        DMFixedPoint diff;
+                        FP_SUB_R(diff, chn->chLoopE, chn->chLoopS);
+                        FP_ADD(chn->chPos, diff);
+                    }
+                }
+            }
+            else
+            {
+                // Normal (non-looped) sample
+                if (chn->chPos.dw <= 0)
+                {
+                    chn->chPlaying = FALSE;
+                    return;
+                }
+            }
+        }
+        
+        // Call the mixing innerloop functions
+        if (chn->chDirection)
+        {
+            DBG("MIX_FW[%p : %d : ", ab, mixDone);
+            if (chn->chFlags & jsfLooped)
+            {
+                DBG("%d (%x)] {loop}\n", FP_GETH(chn->chLoopE), FP_GETH(chn->chLoopE));
+                mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn,
+                    ab, mixDone, chn->chLoopE);
+            }
+            else
+            {
+                DBG("%d (%x)]\n", FP_GETH(chn->chSize), FP_GETH(chn->chSize));
+                mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn,
+                    ab, mixDone, chn->chSize);
+            }
+        }
+        else
+        {
+            DBG("MIX_BW[%p : %d : ", ab, mixDone);
+            if (chn->chFlags & jsfLooped)
+            {
+                DBG("%d (%x)] {loop}\n", chn->chLoopS, chn->chLoopS);
+                mixResult = mixer->jvmMixChannel_BW(mixer, chn,
+                    ab, mixDone, chn->chLoopS);
+            }
+            else
+            {
+                static const DMFixedPoint zero = { 0 };
+                DBG("%d (%x)]\n", 0, 0);
+                mixResult = mixer->jvmMixChannel_BW(mixer, chn,
+                    ab, mixDone, zero);
+            }
+        }
+        
+        mixDone -= mixResult;
+        ab += mixResult * mixer->outChannels;
+    }
+
+#ifdef JSS_DEBUG
+    if (mixDone < 0)
+        JSSWARNING(DMERR_BOUNDS,, "mixDone < 0 in mixing logic loop.\n");
+#endif
+}
+
+
+void jvmRenderAudio(JSSMixer *mixer, void *mixBuffer, const int mixLength)
+{
+    int i, blockLength, mixLeft;
+    JMIXER_ADDBUF_TYPE *ab;
+
+    JSS_LOCK(mixer);
+
+    assert(mixer != NULL);
+    assert(mixBuffer != NULL);
+    assert(mixLength > 0);
+    assert(mixLength * mixer->outChannels <= mixer->addBufSize);
+
+    // Clear mixer->addBuffer
+    memset(mixer->addBuffer, 0, mixLength * mixer->outChannels * sizeof(JMIXER_ADDBUF_TYPE));
+    
+    ab = mixer->addBuffer;
+    mixLeft = mixLength;
+    while (mixLeft > 0)
+    {
+        // Check for callbacks
+        blockLength = mixLeft;
+
+        if (mixer->cbFunction)
+        {
+            if (mixer->cbCounter <= 0)
+            {
+                mixer->cbFunction(mixer, mixer->cbData);
+                mixer->cbCounter = mixer->cbFreq;
+            }
+
+            if (mixer->cbCounter < blockLength)
+                blockLength = mixer->cbCounter;
+        }
+
+        // Do mixing
+        for (i = 0; i < jsetNChannels; i++)
+        {
+            JSSChannel *chn = &(mixer->channels[i]);
+            if (chn->chPlaying && !chn->chMute)
+                jvmMixChannel(mixer, chn, ab, blockLength);
+        }
+
+/*
+            if (chn->chPlaying)
+            {
+                if (!chn->chMute)
+                    jvmMixChannel(mixer, chn, ab, blockLength);
+                else
+                    jvmAdvanceChannel(mixer, chn, blockLength);
+            }
+*/
+        
+        ab += blockLength * mixer->outChannels;
+        mixLeft -= blockLength;
+        mixer->cbCounter -= blockLength;
+    }
+
+    // Post-process
+    mixer->jvmPostProcess(mixer->addBuffer, mixBuffer, mixLength * mixer->outChannels);
+
+    JSS_UNLOCK(mixer);
+}
+
+
+int jvmSetCallback(JSSMixer * mixer, void (*cbFunction) (void *, void *), void *cbData)
+{
+    assert(mixer);
+
+    if (cbFunction == NULL)
+        JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "NULL pointer given as cbFunction");
+
+    JSS_LOCK(mixer);
+
+    mixer->cbFunction = cbFunction;
+    mixer->cbData = cbData;
+
+    JSS_UNLOCK(mixer);
+
+    return DMERR_OK;
+}
+
+
+void jvmRemoveCallback(JSSMixer * mixer)
+{
+    assert(mixer);
+
+    JSS_LOCK(mixer);
+
+    mixer->cbFunction = NULL;
+    mixer->cbData = NULL;
+    mixer->cbFreq = mixer->cbCounter = 0;
+
+    JSS_UNLOCK(mixer);
+}
+
+
+int jvmSetCallbackFreq(JSSMixer * mixer, const int cbFreq)
+{
+    assert(mixer);
+
+    JSS_LOCK(mixer);
+    
+    mixer->cbFreq = cbFreq;
+    mixer->cbCounter = 0;
+
+//fprintf(stderr, "set(outFreq = %d, cbFreq = %d) = %d\n", mixer->outFreq, cbFreq, mixer->cbFreq);
+    
+    JSS_UNLOCK(mixer);
+    return DMERR_OK;
+}
+
+
+/* Channel manipulation routines
+ */
+void jvmPlay(JSSMixer * mixer, const int channel)
+{
+    JSS_LOCK(mixer);
+    mixer->channels[channel].chPlaying = TRUE;
+    JSS_UNLOCK(mixer);
+}
+
+
+void jvmStop(JSSMixer * mixer, const int channel)
+{
+    JSS_LOCK(mixer);
+    mixer->channels[channel].chPlaying = FALSE;
+    JSS_UNLOCK(mixer);
+}
+
+
+void jvmReset(JSSMixer * mixer, const int channel)
+{
+    JSSChannel *c;
+    
+    JSS_LOCK(mixer);
+    c = &mixer->channels[channel];
+
+    c->chDirection = TRUE;
+    c->chPos.dw    = c->chDeltaO.dw = 0;
+
+    JSS_UNLOCK(mixer);
+}
+
+
+void jvmSetSample(JSSMixer * mixer, const int channel,
+          void *data, const Sint32 size, const Sint32 loopS,
+          const Sint32 loopE, const int flags)
+{
+    JSSChannel *c;
+    
+    JSS_LOCK(mixer);
+    c = &mixer->channels[channel];
+    
+    FP_SETHL(c->chSize, size, 0);
+    FP_SETHL(c->chLoopS, loopS, 0);
+    FP_SETHL(c->chLoopE, loopE, 0);
+    c->chData      = data;
+    c->chFlags     = flags;
+    c->chDirection = TRUE;
+    c->chPos.dw    = c->chDeltaO.dw = 0;
+    
+    JSS_UNLOCK(mixer);
+}
+
+
+void jvmSetFreq(JSSMixer * mixer, const int channel, const int freq)
+{
+    JSS_LOCK(mixer);
+
+    mixer->channels[channel].chFreq = freq;
+    
+    if (mixer->outFreq > 0)
+    {
+        DMFixedPoint a, b;
+        FP_SETHL(a, freq, 0);
+        FP_CONV(b, mixer->outFreq);
+        FP_DIV_R(mixer->channels[channel].chDeltaO, a, b);
+    }
+    else
+    {
+        FP_SET(mixer->channels[channel].chDeltaO, 0);
+    }
+
+    JSS_UNLOCK(mixer);
+}
+
+
+int jvmGetFreq(JSSMixer * mixer, const int channel)
+{
+    int tmp;
+
+    JSS_LOCK(mixer);
+    tmp = mixer->channels[channel].chFreq;
+    JSS_UNLOCK(mixer);
+
+    return tmp;
+}
+
+
+void jvmSetVolume(JSSMixer * mixer, const int channel, const int volume)
+{
+    JSS_LOCK(mixer);
+    FP_SETHL(mixer->channels[channel].chVolume, volume, 0);
+    mixer->channels[channel].chVolumeD = 0;
+    mixer->channels[channel].chDeltaV.dw = 0;
+    JSS_UNLOCK(mixer);
+}
+
+
+void jvmSetVolumeRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len)
+{
+    int tmp;
+    DMFixedPoint a, b;
+    JSS_LOCK(mixer);
+    FP_SETHL(mixer->channels[channel].chVolume, start, 0);
+
+    tmp = mixer->channels[channel].chVolumeD = 
+        len > 0 ? ((mixer->outFreq * len) / 1000) : mixer->cbFreq;
+
+    FP_SETHL(a, (end - start), 0);
+    FP_CONV(b, tmp);
+    FP_DIV_R(mixer->channels[channel].chDeltaV, a, b);
+
+    JSS_UNLOCK(mixer);
+}
+
+
+int jvmGetVolume(JSSMixer * mixer, const int channel)
+{
+    int tmp;
+
+    JSS_LOCK(mixer);
+    tmp = FP_GETH(mixer->channels[channel].chVolume);
+    JSS_UNLOCK(mixer);
+
+    return tmp;
+}
+
+
+void jvmSetPos(JSSMixer * mixer, const int channel, const Sint32 pos)
+{
+    JSS_LOCK(mixer);
+    FP_SETHL(mixer->channels[channel].chPos, pos, 0);
+    JSS_UNLOCK(mixer);
+}
+
+
+Sint32 jvmGetPos(JSSMixer * mixer, const int channel)
+{
+    Sint32 tmp;
+
+    JSS_LOCK(mixer);
+    tmp = FP_GETH(mixer->channels[channel].chPos);
+    JSS_UNLOCK(mixer);
+
+    return tmp;
+}
+
+
+void jvmSetPan(JSSMixer * mixer, const int channel, const int panning)
+{
+    JSS_LOCK(mixer);
+    FP_SETHL(mixer->channels[channel].chPanning, panning, 0);
+    mixer->channels[channel].chPanningD = 0;
+    mixer->channels[channel].chDeltaP.dw = 0;
+    JSS_UNLOCK(mixer);
+}
+
+
+void jvmSetPanRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len)
+{
+    int tmp;
+    DMFixedPoint a, b;
+    JSS_LOCK(mixer);
+
+    FP_SETHL(mixer->channels[channel].chPanning, start, 0);
+
+    tmp = mixer->channels[channel].chPanningD = 
+        len > 0 ? ((mixer->outFreq * len) / 1000) : mixer->cbFreq;
+
+    FP_SETHL(a, (end - start), 0);
+    FP_CONV(b, tmp);
+    FP_DIV_R(mixer->channels[channel].chDeltaP, a, b);
+
+    JSS_UNLOCK(mixer);
+}
+
+
+int jvmGetPan(JSSMixer * mixer, const int channel)
+{
+    int tmp;
+
+    JSS_LOCK(mixer);
+    tmp = FP_GETH(mixer->channels[channel].chPanning);
+    JSS_UNLOCK(mixer);
+
+    return tmp;
+}
+
+
+void jvmMute(JSSMixer * mixer, const int channel, const BOOL mute)
+{
+    JSS_LOCK(mixer);
+    mixer->channels[channel].chMute = mute;
+    JSS_UNLOCK(mixer);
+}
+
+
+BOOL jvmGetMute(JSSMixer * mixer, const int channel)
+{
+    BOOL tmp;
+
+    JSS_LOCK(mixer);
+    tmp = mixer->channels[channel].chMute;
+    JSS_UNLOCK(mixer);
+
+    return tmp;
+}
+
+
+void jvmClear(JSSMixer * mixer, const int channel)
+{
+    JSS_LOCK(mixer);
+    memset(&mixer->channels[channel], 0, sizeof(JSSChannel));
+    JSS_UNLOCK(mixer);
+}
+
+
+void jvmSetGlobalVol(JSSMixer * mixer, const int volume)
+{
+    JSS_LOCK(mixer);
+    mixer->globalVol = volume;
+    JSS_UNLOCK(mixer);
+}
+
+
+int jvmGetGlobalVol(JSSMixer * mixer)
+{
+    int tmp;
+
+    JSS_LOCK(mixer);
+    tmp = mixer->globalVol;
+    JSS_UNLOCK(mixer);
+
+    return tmp;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jssmix.h	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,170 @@
+/*
+ * miniJSS - Mixing device and channel handling
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
+ */
+#ifndef JSSMIX_H
+#define JSSMIX_H
+#include "jss.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#define DBG(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
+#define DBG(...) do { /* stub */ } while (0)
+
+
+// Mixing limits
+#define JVM_LIMIT_16_POS    (32767)
+#define JVM_LIMIT_16_NEG    (-32767)
+#define JVM_ADD_16          (32768)
+
+
+#define JMIXER_ADDBUF_TYPE Sint32
+
+
+// A channel data structure
+typedef struct
+{
+    DMFixedPoint
+            chSize,         // Length of sample in UNITS
+            chLoopS,        // Loop start in UNITS
+            chLoopE,        // Loop end in UNITS
+
+            chPos,          // Current position in sample, 32:32 fixpoint
+            chDeltaO,       // Delta in 32:32 UNSIGNED! (chDirection)
+            chVolume,       // Volume
+            chDeltaV,
+            chPanning,      // Panning
+            chDeltaP;
+    int     chVolumeD,
+            chPanningD;
+
+    int     chFreq;         // Frequency of sampel in Hz
+
+    void    *chData;        // Pointer to data
+
+    BOOL    chPlaying,      // TRUE = playing
+            chMute,         // TRUE = muted
+            chDirection;    // TRUE = playing forwards, FALSE = backwards
+
+    int     chFlags;        // Flags
+
+    JMIXER_ADDBUF_TYPE chPrevR[5], chPrevL[5];
+} JSSChannel;
+
+
+// Channel table
+typedef int JSSChannelTable[jsetNChannels];
+
+
+// Virtual software mixer "device" structure
+typedef struct _JSSMixer JSSMixer;
+
+struct _JSSMixer
+{
+    int             outFormat,
+                    outChannels,
+                    outFreq,
+                    globalVol;
+    JSSChannel      channels[jsetNChannels];
+    
+    int             addBufSize;
+    JMIXER_ADDBUF_TYPE *addBuffer;
+
+    // Callback handling
+    int             cbFreq, cbCounter;
+    void            *cbData;
+    void            (*cbFunction)(void *, void *);
+
+    // Mixing routine pointers
+    int    (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
+    int    (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint);
+    void   (*jvmPostProcess)(JMIXER_ADDBUF_TYPE *, void *, const int);
+
+    // Device locking
+#ifdef JSS_SUP_THREADS
+    DMMutex *mutex;
+#endif
+};
+
+
+/* Enums
+ */
+enum {
+    JMIX_AUTO = 0,
+    JMIX_C,
+    JMIX_MMX,
+    JMIX_SSE
+};
+
+
+/* Main routines
+ */
+JSSMixer *  jvmInit(const int outFormat, const int outChannels, const int outFreq, const int mixerID);
+int         jvmClose(JSSMixer *mixer);
+
+int         jvmSetCallback(JSSMixer *mixer, void (*cbFunction)(void *, void *), void *cbData);
+void        jvmRemoveCallback(JSSMixer *mixer);
+int         jvmSetCallbackFreq(JSSMixer *mixer, const int cbFreq);
+Sint32      jvmGetLastCBBufPos(JSSMixer *mixer, const int cbHandle);
+Sint32      jvmGetNextCBBufPos(JSSMixer *mixer, const int cbHandle);
+
+int         jvmGetSampleSize(JSSMixer *mixer);
+int         jvmGetSampleRes(JSSMixer *mixer);
+
+void        jvmRenderAudio(JSSMixer *mixer, void *mixBuffer, const int mixLength);
+
+
+/*
+int         jvmAddCallback(JSSMixer *mixer, void (*cbFunction)(JSSMixer *, void *));
+int         jvmRemoveCallback(JSSMixer *mixer, int cbHandle);
+int         jvmSetCallbackFreq(JSSMixer *mixer, int cbHandle, int cbFreq);
+
+int         jvmAllocChannels(JSSMixer *mixer, int nChannels);
+int         jvmFreeChannels(JSSMixer *mixer, int iHandle);
+int         jvmGetChannelTable(JSSMixer *mixer, int iHandle, JSSChannelTable *pTable);
+*/
+
+
+/* Channel manipulation routines
+ */
+void        jvmPlay(JSSMixer *mixer, const int channel);
+void        jvmStop(JSSMixer *mixer, const int channel);
+void        jvmReset(JSSMixer * mixer, const int channel);
+
+void        jvmSetSample(JSSMixer *mixer, const int channel,
+            void *data, const Sint32 size, const Sint32 loopS,
+            const Sint32 loopE, const int flags);
+
+void        jvmSetFreq(JSSMixer *mixer, const int channel, const int freq);
+int         jvmGetFreq(JSSMixer *mixer, const int channel);
+
+void        jvmSetVolume(JSSMixer *mixer, const int channel, const int volume);
+void        jvmSetVolumeRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len);
+int         jvmGetVolume(JSSMixer *mixer, const int channel);
+
+void        jvmSetPos(JSSMixer *mixer, const int channel, const Sint32 pos);
+Sint32      jvmGetPos(JSSMixer *mixer, const int channel);
+
+void        jvmSetPan(JSSMixer *mixer, const int channel, const int panning);
+void        jvmSetPanRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len);
+int         jvmGetPan(JSSMixer *mixer, const int channel);
+
+void        jvmMute(JSSMixer *mixer, const int channel, const BOOL mute);
+BOOL        jvmGetMute(JSSMixer *mixer, const int channel);
+
+void        jvmClear(JSSMixer *mixer, const int channel);
+void        jvmClearChannels(JSSMixer *mixer);
+
+void        jvmSetGlobalVol(JSSMixer *mixer, const int volume);
+int         jvmGetGlobalVol(JSSMixer *mixer);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // JSSMIX_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jssmod.c	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,447 @@
+/*
+ * miniJSS - Module structure and handling routines
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
+ */
+#include "jssmod.h"
+#include <string.h>
+
+
+#ifndef JSS_LIGHT
+/* Take given data until maxlen reached, make a string
+ */
+char *jssASCIItoStr(char * sdata, const char endByte, const size_t maxLen)
+{
+    size_t i, k;
+    char *res;
+
+    for (i = 0; sdata[i] && i < maxLen; i++);
+
+    res = (char *) dmMalloc(i + 1);
+    if (res == NULL)
+        return NULL;
+
+    for (k = 0; sdata[k] != endByte && k < i; k++)
+        res[k] = sdata[k];
+
+    res[k] = 0;
+
+    return res;
+}
+
+
+/* Encodes a given 8-bit sample
+ */
+BOOL jssEncodeSample8(Uint8 * data, const size_t len, const int ops)
+{
+    size_t count = len;
+    Sint8 t, value = 0;
+    
+    while (count--)
+    {
+        t = *data;
+        
+        if (ops & jsampFlipSign)
+            t ^= 0x80;
+
+        if (ops & jsampDelta)
+        {
+            int n = t - value;
+            value = t;
+            t = n;
+        }
+        
+        *(data++) = t;
+    }
+
+    return TRUE;
+}
+
+
+/* Decodes a given 16-bit sample
+ */
+BOOL jssEncodeSample16(Uint16 * data, const size_t len, const int ops)
+{
+    // "Split" the 16-bit samples into 8-bit halves
+    if (ops & jsampSplit)
+    {
+        // Allocate temporary processing buffer
+        size_t count, bufSize = len * sizeof(Sint16);
+        Uint8 *bp1, *bp2;
+        Sint16 *sdata, *tmpBuf = dmMalloc(bufSize);
+        if (tmpBuf == NULL) return FALSE;
+
+        sdata = tmpBuf;
+        bp1 = (Uint8 *) data;
+        bp2 = bp1 + len;
+        count = len;
+
+        while (count--)
+        {
+            Sint16 t = (*sdata++);
+            *bp1++ = t >> 8;
+            *bp2++ = t & 0xff;
+        }
+        
+        memcpy(data, tmpBuf, bufSize);
+        dmFree(tmpBuf);
+        
+        return jssEncodeSample8((Uint8 *) data, bufSize, ops);
+    }
+    else
+    {
+        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
+        size_t count = len;
+        
+        while (count--)
+        {
+            if (ops & jsampSwapEndianess)
+            {
+                p = *sdata;
+                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
+            } else
+                t = *sdata;
+            
+            if (ops & jsampDelta)
+            {
+                int n = t - value;
+                value = t;
+                t = n;
+            }
+            
+            if (ops & jsampFlipSign)
+                t ^= 0x8000;
+            
+            *(sdata++) = t;
+        }
+    }
+    return TRUE;
+}
+
+#endif
+
+
+/* Decodes a given 8-bit sample
+ */
+BOOL jssDecodeSample8(Uint8 * data, const size_t len, const int ops)
+{
+    size_t count = len;
+    Sint8 t, value = 0;
+    
+    while (count--)
+    {
+        t = *data;
+        
+        if (ops & jsampDelta)
+            t = value = t + value;
+        
+        if (ops & jsampFlipSign)
+            t ^= 0x80;
+        
+        *(data++) = t;
+    }
+    return TRUE;
+}
+
+
+/* Decodes a given 16-bit sample
+ */
+BOOL jssDecodeSample16(Uint16 * data, const size_t len, const int ops)
+{
+    if (ops & jsampSplit)
+    {
+        size_t count, bufSize = len * sizeof(Uint16);
+        Uint8 *bp1, *bp2;
+        Sint16 *tmpBuf, *sdata;
+        
+        if (!jssDecodeSample8((Uint8 *) data, bufSize, ops))
+            return FALSE;
+        
+        tmpBuf = dmMalloc(bufSize);
+        if (tmpBuf == NULL) return FALSE;
+        memcpy(tmpBuf, data, bufSize);
+        
+        sdata = (Sint16 *) data;
+        bp1 = (Uint8 *) tmpBuf;
+        bp2 = bp1 + len;
+        count = len;
+        while (count--)
+        {
+            *sdata++ = (*bp1++ << 8) | (*bp2++ & 0xff);
+        }
+        
+        dmFree(tmpBuf);
+    }
+    else
+    {
+        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
+        size_t count = len;
+        while (count--)
+        {
+            if (ops & jsampSwapEndianess)
+            {
+                p = *sdata;
+                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
+            } else
+                t = *sdata;
+            
+            if (ops & jsampDelta)
+                t = value = t + value;
+            
+            if (ops & jsampFlipSign)
+                t ^= 0x8000;
+            
+            *(sdata++) = t;
+        }
+    }
+    return TRUE;
+}
+
+
+/* Convert sample data from U8 to S16
+ */
+int jssConvertSampleTo16(void **dst, void * src, const size_t len)
+{
+    size_t count = len;
+    Uint8 *in = (Uint8 *) src;
+    Sint16 *out;
+
+    *dst = out = dmMalloc(sizeof(Sint16) * len);
+    if (out == NULL)
+        return DMERR_MALLOC;
+    
+    while (count--)
+    {
+        *(out++) = (*(in++) * 256) - 32768;
+    }
+    
+    return DMERR_OK;
+}
+
+/* Converts the given module in preparation for playing it.
+ * This involves sample format conversion (8 to 16 bit, etc.)
+ *
+ * NOTICE! The converted module can only be saved in JSSMOD
+ * format, but this is not recommended.
+ */
+int jssConvertModuleForPlaying(JSSModule *module)
+{
+    int i;
+    if (module == NULL)
+        return DMERR_NULLPTR;
+
+    // Convert instruments
+    for (i = 0; i < module->ninstruments; i++)
+    {
+        JSSInstrument *inst = module->instruments[i];
+        if (inst != NULL && inst->data != NULL)
+        {
+            int res;
+            void *data = NULL;
+
+            if (inst->flags & jsf16bit)
+                continue;
+
+            if ((res = jssConvertSampleTo16(&data, inst->data, inst->size)) != DMERR_OK)
+                return res;
+
+            inst->flags |= jsf16bit;
+            dmFree(inst->data);
+            inst->data = data;
+        }
+    }
+    
+    return DMERR_OK;
+}
+
+
+/* Allocates a new module structure or returns errorvalue if failed.
+ * Memory is allocated only for the basic structure. Sample- and pattern
+ * areas must be allocated separately with appropriate routines.
+ */
+JSSModule *jssAllocateModule(void)
+{
+    int i;
+    JSSModule *module;
+
+    // Allocate module structure
+    module = dmMalloc0(sizeof(JSSModule));
+    if (module == NULL)
+        return NULL;
+
+    // Initialize structure
+    for (i = 0; i < jsetNChannels; i++)
+        module->defPanning[i] = jchPanMiddle;
+
+    for (i = 0; i < jsetMaxOrders; i++)
+        module->orderList[i] = jsetOrderEnd;
+
+    // Allocate mutex
+#ifdef JSS_SUP_THREADS
+    module->mutex = dmCreateMutex();
+#endif
+
+    return module;
+}
+
+
+/* Frees a given module structure, freeing all memory areas
+ * that were allocated for it (including patterns, samples, etc.)
+ */
+int jssFreeModule(JSSModule * module)
+{
+    int i;
+
+    if (module == NULL)
+        return DMERR_NULLPTR;
+    
+    // Free strings
+#ifndef JSS_LIGHT
+    dmFree(module->moduleName);
+    dmFree(module->trackerName);
+#endif
+
+    // Free patterns
+    for (i = 0; i < module->npatterns; i++)
+    {
+        if (module->patterns[i])
+        {
+            JSSPattern *pat = module->patterns[i];
+            dmFree(pat->data);
+            dmFree(pat);
+            module->patterns[i] = NULL;
+        }
+    }
+
+    // Free the "empty" pattern
+    JSSPattern *pat = module->patterns[jsetMaxPatterns];
+    if (pat != NULL)
+    {
+        dmFree(pat->data);
+        dmFree(pat);
+        module->patterns[i] = NULL;
+    }
+
+    // Free instruments
+    for (i = 0; i < module->ninstruments; i++)
+    {
+        if (module->instruments[i])
+        {
+            JSSInstrument *inst = module->instruments[i];
+#ifndef JSS_LIGHT
+            dmFree(inst->desc);
+#endif
+            dmFree(inst->data);
+            dmFree(inst);
+            module->instruments[i] = NULL;
+        }
+    }
+
+    // Free extended instruments
+    for (i = 0; i < module->nextInstruments; i++)
+    {
+        if (module->extInstruments[i])
+        {
+            JSSExtInstrument *ext = module->extInstruments[i];
+#ifndef JSS_LIGHT
+            dmFree(ext->desc);
+#endif
+            dmFree(ext);
+            module->extInstruments[i] = NULL;
+        }
+    }
+
+    // Free mutex
+#ifdef JSS_SUP_THREADS
+    dmDestroyMutex(module->mutex);
+#endif
+    
+    // Free the module structure
+    memset(module, 0, sizeof(JSSModule));
+    dmFree(module);
+
+    return DMERR_OK;
+}
+
+
+/* Allocates and initializes a internal pattern structure.
+ */
+JSSPattern *jssAllocatePattern(int nrows, int nchannels)
+{
+    int i, j;
+    JSSPattern *res;
+    JSSNote *pnote;
+
+    // Check arguments
+    if (nrows <= 0 || nchannels <= 0)
+        JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid nrows=%i or nchannels=%i.\n", nrows, nchannels);
+
+    // Allocate a pattern structure
+    res = dmMalloc0(sizeof(JSSPattern));
+    if (res == NULL)
+        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern structure.\n");
+
+    // Allocate notedata
+    res->data = dmCalloc(nrows * nchannels, sizeof(JSSNote));
+    if (res->data == NULL)
+    {
+        dmFree(res);
+        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern data (nrows=%i, nchannels=%i).\n", nrows,
+             nchannels);
+    }
+
+    // Initialize
+    res->nrows = nrows;
+    res->nchannels = nchannels;
+
+    pnote = res->data;
+    for (j = 0; j < nrows; j++)
+    {
+        for (i = 0; i < nchannels; i++)
+        {
+            pnote->note = pnote->instrument = pnote->volume =
+                pnote->effect = pnote->param = jsetNotSet;
+
+            pnote++;
+        }
+    }
+
+    // OK, return pointer to struct
+    return res;
+}
+
+
+/* Allocates and initializes internal "normal" instrument structure.
+ */
+JSSInstrument *jssAllocateInstrument(void)
+{
+    JSSInstrument *res;
+
+    // Allocate a instrument structure
+    res = dmMalloc0(sizeof(JSSInstrument));
+    if (res == NULL)
+        return NULL;
+
+    return res;
+}
+
+
+/* Allocates and initializes "extended" instrument structure.
+ */
+JSSExtInstrument *jssAllocateExtInstrument(void)
+{
+    int i;
+    JSSExtInstrument *res;
+
+    // Allocate a instrument structure
+    res = dmMalloc0(sizeof(JSSExtInstrument));
+    if (res == NULL)
+        return NULL;
+
+    for (i = 0; i < jsetNNotes; i++)
+    {
+        res->sNumForNotes[i] = jsetNotSet;
+    }
+    
+    return res;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jssmod.h	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,315 @@
+/*
+ * miniJSS - Module structure and handling routines
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2009 Tecnic Software productions (TNSP)
+ */
+#ifndef JSSMOD_H
+#define JSSMOD_H
+
+#include "jss.h"
+#include "dmres.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Max data size definitions
+#define jsetMaxRows         (256)   // Max number of rows
+#define jsetMaxPatterns     (256)   // Max patterns
+#define jsetMaxTracks       (jsetMaxPatterns * jsetMaxPatterns) // Max tracks
+#define jsetMaxInstruments  (512)
+#define jsetMaxOrders       (260)
+#define jsetNNotes          (11 * 12)
+#define jsetMinNote         (0)     // Smallest note number
+#define jsetMaxNote         (jsetNNotes - 1)
+#define jsetMaxEnvPoints    (32)    // Max number of envelope points
+
+
+// Instrument envelope flags
+#define jenvfUsed           (0x01)  // Envelope is used
+#define jenvfSustain        (0x02)  // Envelope has a sustain point (XM) or has sustain loop (IT)
+#define jenvfLooped         (0x04)  // Envelope is looped
+
+// Instrument vibrato waveform types
+#define jvibSine            (0)
+#define jvibRamp            (1)
+#define jvibSquare          (2)
+#define jvibRandom          (3)
+
+// General stuff
+#define jsetNoteOff         (-2)
+#define jsetOrderEnd        (-1)
+#define jsetOrderSkip       (-2)
+
+
+// General module flags
+#define jmdfAmigaPeriods    (0x0001)    // Use non-linear periods (Amiga)
+#define jmdfAmigaLimits     (0x0002)    // Use Amiga-styled valuelimits
+#define jmdfStereo          (0x0008)    // Use stereo output, if possible
+
+#define jmdfFT2Replay       (0x0010)    // Use FT2 replaying bugs/features
+#define jmdfST300Slides     (0x0020)    // Use Scream Tracker 3.00 slides
+#define jmdfByteLStart      (0x0040)    // LStart is in BYTES instead of WORDS (MOD only)
+
+
+// Module format types
+enum JMDT
+{
+    jmdtMOD = 1,
+    jmdtS3M,
+    jmdtXM,
+    jmdtIT
+};
+
+
+// Bits for sample conversion routines
+#define jsampDelta          (0x01)
+#define jsampFlipSign       (0x02)
+#define jsampSwapEndianess  (0x04)
+#define jsampSplit          (0x08)
+
+
+// Internal instrument structure
+typedef struct
+{
+    int size,               // Length in units
+        loopS,              // Loop start position in units
+        loopE;              // Loop end position in units
+    int volume,             // Volume [jsetMinVol...jsetMaxVol]
+        flags,              // Flags - see jss.h jsfXXXX
+        C4BaseSpeed,        // C4BaseSpeed
+        ERelNote,           // Extended: Relative note value
+        EFineTune,          // Extended: Fine-tune value
+        EPanning;           // Extended: Panning
+#ifndef JSS_LIGHT
+    char *desc;
+#endif
+    void *data;             // Sample data
+
+    BOOL hasData;
+    int convFlags;
+} JSSInstrument;
+
+
+// Envelope point structure
+typedef struct
+{
+    int frame, value;
+} JSSEnvelopePoint;
+
+
+// Envelope structure
+typedef struct
+{
+    int flags,
+        npoints,
+        sustain,
+        loopS,
+        loopE;
+    JSSEnvelopePoint points[jsetMaxEnvPoints];
+} JSSEnvelope;
+
+
+// Extended instrument
+typedef struct
+{
+#ifndef JSS_LIGHT
+    char *desc;
+#endif
+    int nsamples, sNumForNotes[jsetNNotes];
+    JSSEnvelope volumeEnv, panningEnv;
+    int vibratoType,
+        vibratoSweep,
+        vibratoDepth,
+        vibratoRate,
+        fadeOut;
+} JSSExtInstrument;
+
+
+// Internal pattern structures
+typedef struct
+{
+    int note,
+        instrument,
+        volume,
+        effect,
+        param;
+} JSSNote;
+
+
+typedef struct
+{
+    int nrows, nchannels;
+    JSSNote *data;
+} JSSPattern;
+
+
+// Module structure
+typedef struct
+{
+    int     moduleType;     // Type of the module
+    char    *moduleName;    // Title/name
+    char    *trackerName;   // Tracker software name
+    int     defSpeed,       // Initial values
+            defTempo,
+            defFlags,
+            defRestartPos,
+            intVersion,     // Format's internal version
+            nchannels,
+            ninstruments,
+            nextInstruments,
+            npatterns,
+            norders;
+
+    int     defPanning[jsetNChannels];
+    int     orderList[jsetMaxOrders];
+    JSSPattern       *patterns[jsetMaxPatterns + 1];
+    JSSInstrument    *instruments[jsetMaxInstruments];
+    JSSExtInstrument *extInstruments[jsetMaxInstruments];
+
+#ifdef JSS_SUP_THREADS
+    DMMutex *mutex;
+#endif
+
+} JSSModule;
+
+
+#ifdef JSS_SUP_JSSMOD
+
+#define JSSMOD_VERSION    (0x10)
+
+enum
+{
+    PATMODE_RAW_HORIZ = 1,
+    PATMODE_COMP_HORIZ,
+    PATMODE_RAW_VERT,
+    PATMODE_COMP_VERT,
+    PATMODE_RAW_ELEM,
+    PATMODE_LAST    
+};
+
+/* JSSMOD typedefs
+ */
+typedef struct
+{
+    char  idMagic[2];     // "JM"
+    Uint8 idVersion;      // 0x10 for 1.0, etc.
+    Sint16
+        norders,            // Number of orders in orderlist
+        npatterns,          // Number of patterns
+        nchannels,          // Number of channels
+        nextInstruments,    // Number of extended instruments
+        ninstruments,       // Number of sample-instruments
+        defFlags,           // Flags field: see jmdf* flags
+        intVersion,         // Internal version, format dependant
+        defRestartPos,      // Default restart position in orderlist
+        defSpeed,           // Default speed (ticks/row)
+        defTempo,           // Default tempo (BPM)
+        patMode;            // Pattern data format mode
+
+    /*
+    - After this, norders long orders table will follow, of type:
+      Sint16 orderList[norders];
+    
+    - Pattern data, format depends on patMode.
+    
+    - Extended instruments (*)
+    
+    - Sample instrument headers (*)
+    
+    - Sample data (format depends) (*)
+    
+    (*) Items are optional and may have been omitted. Fields in the 
+        module and other headers are used to indicate if these items exist.
+    */
+} JSSMODHeader;
+
+
+typedef struct
+{
+    Uint16 frame, value;
+} JSSMODEnvelopePoint;
+
+
+typedef struct
+{
+    int flags,
+        npoints,
+        sustain,
+        loopS,
+        loopE;
+    JSSMODEnvelopePoint points[jsetMaxEnvPoints];
+} JSSMODEnvelope;
+
+
+typedef struct
+{
+    Sint16 nsamples;
+    Uint8 sNumForNotes[jsetNNotes];
+    JSSMODEnvelope volumeEnv, panningEnv;
+    Sint16 vibratoType,
+            vibratoSweep,
+            vibratoDepth,
+            vibratoRate;
+    int     fadeOut;
+} JSSMODExtInstrument;
+
+
+typedef struct
+{
+    int     size,           // Length in units
+            loopS,          // Loop start position in units
+            loopE;          // Loop end position in units
+    Sint16 volume,         // Volume [jsetMinVol...jsetMaxVol]
+            flags,          // Flags - see jss.h jsfXXXX
+            C4BaseSpeed,    // C4BaseSpeed
+            ERelNote,       // Extended: Relative note value
+            EFineTune,      // Extended: Fine-tune value
+            EPanning;       // Extended: Panning
+    Uint8 convFlags,      // Conversion flags .. jsampXXXX
+            hasData;        // != 0 if there is sample data
+} JSSMODInstrument;
+
+
+#define COMP_NOTE           (0x01)
+#define COMP_INSTRUMENT     (0x02)
+#define COMP_VOLUME         (0x04)
+#define COMP_EFFECT         (0x08)
+#define COMP_PARAM          (0x10)
+#define COMP_ALL (COMP_NOTE | COMP_INSTRUMENT | COMP_VOLUME | COMP_EFFECT | COMP_PARAM)
+
+
+typedef struct
+{
+    size_t nrows, size;
+} JSSMODPattern;
+
+#endif
+
+#ifndef JSS_LIGHT
+char*               jssASCIItoStr(char *, const char, const size_t);
+BOOL                jssEncodeSample8(Uint8 *, const size_t, const int);
+BOOL                jssEncodeSample16(Uint16 *, const size_t, const int);
+#endif
+BOOL                jssDecodeSample8(Uint8 *, const size_t, const int);
+BOOL                jssDecodeSample16(Uint16 *, const size_t, const int);
+int                 jssConvertModuleForPlaying(JSSModule *module);
+JSSModule *         jssAllocateModule(void);
+int                 jssFreeModule(JSSModule *);
+JSSPattern *        jssAllocatePattern(int, int);
+JSSInstrument *     jssAllocateInstrument(void);
+JSSExtInstrument *  jssAllocateExtInstrument(void);
+
+#ifdef JSS_SUP_XM
+int        jssLoadXM(DMResource *, JSSModule **);
+#endif
+
+#ifdef JSS_SUP_JSSMOD
+int        jssLoadJSSMOD(Uint8 *, const size_t, JSSModule **);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // JSSMOD_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jssplr.c	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,1579 @@
+/*
+ * miniJSS - Module playing routines
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
+ */
+#include "jssplr.h"
+
+// FIXME!! FIX ME!
+#include <math.h>
+
+/* Miscellaneous tables
+ */
+static const Uint8 jmpSineTab[32] =
+{
+      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
+};
+
+
+static const Sint16 jmpXMAmigaPeriodTab[13 * 8] = {
+    907, 900, 894, 887, 881, 875, 868, 862, 856, 850, 844, 838,
+    832, 826, 820, 814, 808, 802, 796, 791, 785, 779, 774, 768,
+    762, 757, 752, 746, 741, 736, 730, 725, 720, 715, 709, 704,
+    699, 694, 689, 684, 678, 675, 670, 665, 660, 655, 651, 646,
+    640, 636, 632, 628, 623, 619, 614, 610, 604, 601, 597, 592,
+    588, 584, 580, 575, 570, 567, 563, 559, 555, 551, 547, 543,
+    538, 535, 532, 528, 524, 520, 516, 513, 508, 505, 502, 498,
+    494, 491, 487, 484, 480, 477, 474, 470, 467, 463, 460, 457,
+
+    453, 450, 447, 443, 440, 437, 434, 431
+};
+
+
+#define jmpNMODEffectTable (36)
+static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+
+/* Helper functions
+ */
+static int jmpNoteToAmigaPeriod(int note, int finetune)
+{
+    int tmp = dmClamp(note + finetune + 8, 0, 103);
+    return jmpXMAmigaPeriodTab[tmp];
+}
+
+
+static int jmpGetPeriodFromNote(JSSPlayer *mp, int note, int finetune)
+{
+    int res;
+
+    if (JMPGETMODFLAGS(mp, jmdfAmigaPeriods))
+    {
+        int mfinetune = finetune / 16,
+            mnote = (note % 12) * 8,
+            moctave = note / 12,
+            period1, period2;
+        
+        period1 = jmpNoteToAmigaPeriod(mnote, mfinetune);
+        
+        if (finetune < 0)
+        {
+            mfinetune--;
+            finetune = -finetune;
+        } else
+            mfinetune++;
+        
+        period2 = jmpNoteToAmigaPeriod(mnote, mfinetune);
+        
+        mfinetune = finetune & 15;
+        period1 *= (16 - mfinetune);
+        period2 *= mfinetune;
+        
+        res = ((period1 + period2) * 2) >> moctave;
+        
+//fprintf(stderr, "jmpGetAmigaPeriod(%d, %d) = %d\n", note, finetune, res);
+    }
+    else
+    {
+//fprintf(stderr, "jmpGetLinearPeriod(%d, %d) = %d\n", note, finetune, res);
+        //res = ((120 - note) << 6) - (finetune / 2);
+        res = 7680 - (note * 64) - (finetune / 2);
+        if (res < 1) res = 1;
+    }
+
+    return res;
+}
+
+
+static void jmpCSetPitch(JSSPlayer *mp, JSSPlayerChannel *chn, int value)
+{
+    if (value > 0)
+    {
+        if (JMPGETMODFLAGS(mp, jmdfAmigaPeriods))
+        {
+            // Frequency = 8363*1712 / Period
+            chn->cfreq = chn->freq = 14317456 / value;
+        }
+        else
+        {
+            // Frequency = Frequency = 8363*2^((6*12*16*4 - Period) / (12*16*4))
+            chn->cfreq = chn->freq = 8363.0f * pow(2.0f, (4608.0f - (double) value) / 768.0f);
+        }
+
+        JMPSETNDFLAGS(cdfNewFreq);
+    }
+}
+
+
+static void jmpCSetVolume(JSSPlayerChannel *chn, int channel, int volume)
+{
+    (void) channel;
+
+    chn->volume = dmClamp(volume, mpMinVol, mpMaxVol);
+    JMPSETNDFLAGS(cdfNewVolume);
+}
+
+
+static BOOL jmpExecEnvelope(JSSEnvelope *env, JSSPlayerEnvelope *pe, BOOL keyOff)
+{
+    int point;
+
+    if (!pe->exec)
+        return FALSE;
+    
+    // Find current point, if not last point
+    for (point = 0; point < env->npoints - 1; point++)
+    {
+        if (pe->frame < env->points[point + 1].frame)
+            break;
+    }
+
+    if (env->flags & jenvfLooped && pe->frame >= env->points[env->loopE].frame)
+    {
+        point = env->loopS;
+        pe->frame = env->points[env->loopS].frame;
+        pe->value = env->points[point].value;
+    }
+
+    // Check for last point
+    if (pe->frame >= env->points[env->npoints - 1].frame)
+    {
+        point = env->npoints - 1;
+        pe->exec = FALSE;
+        pe->value = env->points[point].value;
+    }
+    else
+    {
+        // Linearly interpolate the value between current and next point
+        JSSEnvelopePoint
+            *ep1 = &env->points[point],
+            *ep2 = &env->points[point + 1];
+
+        int delta = ep2->frame - ep1->frame;
+        if (delta > 0)
+            pe->value = ep1->value + ((ep2->value - ep1->value) * (pe->frame - ep1->frame)) / delta;
+        else
+            pe->value = ep1->value;
+    }
+
+    if (pe->exec)
+    {
+        // The frame counter IS processed even if the envelope is not!
+        if ((env->flags & jenvfSustain) && point == env->sustain &&
+            env->points[point].frame == env->points[env->sustain].frame)
+        {
+            if (keyOff)
+                pe->frame++;
+        } else
+            pe->frame++;
+    }
+        
+    return TRUE;
+}
+
+
+static void jmpProcessExtInstrument(JSSPlayerChannel *chn, int channel)
+{
+    JSSExtInstrument *inst = chn->extInstrument;
+    (void) channel;
+
+    // Get the instrument for envelope data
+    if (!inst) return;
+
+    // Process the autovibrato
+    /*
+       FIXME fix me FIX me!!! todo.
+     */
+
+    if (inst->volumeEnv.flags & jenvfUsed)
+    {
+        // Process the instrument volume fadeout
+        if (chn->keyOff && chn->fadeOutVol > 0 && inst->fadeOut > 0)
+        {
+            int tmp = chn->fadeOutVol - inst->fadeOut;
+            if (tmp < 0) tmp = 0;
+            chn->fadeOutVol = tmp;
+
+            JMPSETNDFLAGS(cdfNewVolume);
+        }
+
+        // Execute the volume envelope
+        if (jmpExecEnvelope(&inst->volumeEnv, &chn->volumeEnv, chn->keyOff))
+            JMPSETNDFLAGS(cdfNewVolume);
+    }
+    else
+    {
+        // If the envelope is not used, set max volume
+        chn->volumeEnv.value = mpMaxVol;
+        chn->fadeOutVol = chn->keyOff ? 0 : mpMaxFadeoutVol;
+        JMPSETNDFLAGS(cdfNewVolume);
+    }
+
+    if (inst->panningEnv.flags & jenvfUsed)
+    {
+        // Process the panning envelope
+        if (jmpExecEnvelope(&inst->panningEnv, &chn->panningEnv, chn->keyOff))
+            JMPSETNDFLAGS(cdfNewPanPos);
+    }
+    else
+    {
+        // If the envelope is not used, set center panning
+        if (chn->panningEnv.value != mpPanCenter)
+        {
+            chn->panningEnv.value = mpPanCenter;
+            JMPSETNDFLAGS(cdfNewPanPos);
+        }
+    }
+}
+
+
+/*
+ * The player
+ */
+JSSPlayer *jmpInit(JSSMixer *pDevice)
+{
+    JSSPlayer *mp;
+    
+    // Allocate a player structure
+    mp = dmMalloc0(sizeof(JSSPlayer));
+    if (mp == NULL)
+        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate memory for player structure.\n");
+
+    // Set variables
+    mp->device = pDevice;
+
+#ifdef JSS_SUP_THREADS
+    mp->mutex = dmCreateMutex();
+#endif
+    
+    return mp;
+}
+
+
+int jmpClose(JSSPlayer * mp)
+{
+    if (mp == NULL)
+        return DMERR_NULLPTR;
+
+    // Stop player
+    jmpStop(mp);
+
+    // Deallocate resources
+#ifdef JSS_SUP_THREADS
+    dmDestroyMutex(mp->mutex);
+#endif
+
+    // Clear structure
+    memset(mp, 0, sizeof(JSSPlayer));
+    dmFree(mp);
+
+    return DMERR_OK;
+}
+
+
+/* Reset the envelopes for given channel.
+ */
+static void jmpResetEnvelope(JSSPlayerEnvelope *env)
+{
+    env->frame = env->value = 0;
+    env->exec = TRUE;
+}
+
+
+/* Clear module player structure
+ */
+void jmpClearChannel(JSSPlayerChannel *chn)
+{
+    memset(chn, 0, sizeof(JSSPlayerChannel));
+
+    chn->note            = jsetNotSet;
+    chn->ninstrument     = jsetNotSet;
+    chn->nextInstrument  = jsetNotSet;
+    chn->panning         = mpPanCenter;
+    chn->panningEnv.value          = mpPanCenter;
+}
+
+
+void jmpClearPlayer(JSSPlayer * mp)
+{
+    int i;
+    assert(mp != NULL);
+    JSS_LOCK(mp);
+
+    // Initialize general variables
+    mp->patternDelay      = 0;
+    mp->newRowSet         = FALSE;
+    mp->newOrderSet       = FALSE;
+    mp->tick              = jsetNotSet;
+    mp->row               = 0;
+    mp->lastPatLoopRow    = 0;
+
+    // Initialize channel data
+    for (i = 0; i < jsetNChannels; i++)
+        jmpClearChannel(&mp->channels[i]);
+    
+    JSS_UNLOCK(mp);
+}
+
+
+/* Set module
+ */
+void jmpSetModule(JSSPlayer * mp, JSSModule * module)
+{
+    assert(mp != NULL);
+    JSS_LOCK(mp);
+
+    jmpStop(mp);
+    jmpClearPlayer(mp);
+
+    mp->module = module;
+
+    JSS_UNLOCK(mp);
+}
+
+
+/* Stop playing
+ */
+void jmpStop(JSSPlayer * mp)
+{
+    assert(mp != NULL);
+    JSS_LOCK(mp);
+
+    if (mp->isPlaying)
+    {
+        jvmRemoveCallback(mp->device);
+        mp->isPlaying = FALSE;
+    }
+    
+    JSS_UNLOCK(mp);
+}
+
+
+/* Resume playing
+ */
+void jmpResume(JSSPlayer * mp)
+{
+    assert(mp != NULL);
+    JSS_LOCK(mp);
+
+    if (!mp->isPlaying)
+    {
+        int result = jvmSetCallback(mp->device, jmpExec, (void *) mp);
+        if (result != DMERR_OK)
+            JSSERROR(result,, "Could not initialize callback for player.\n");
+
+        mp->isPlaying = TRUE;
+    }
+
+    JSS_UNLOCK(mp);
+}
+
+
+/* Sets new order using given value as reference.
+ * Jumps over skip-points and invalid values, loops
+ * to first order if enabled.
+ */
+static void jmpSetNewOrder(JSSPlayer * mp, int order)
+{
+    BOOL orderOK;
+    int pattern;
+    
+    pattern = jsetOrderEnd;
+    mp->order = jsetNotSet;
+    orderOK = FALSE;
+
+    while (!orderOK)
+    {
+        if (order < 0 || order >= mp->module->norders)
+        {
+            jmpStop(mp);
+            orderOK = TRUE;
+        }
+        else
+        {
+            pattern = mp->module->orderList[order];
+            if (pattern == jsetOrderSkip)
+            {
+                order++;
+            }
+            else
+            if (pattern >= mp->module->npatterns || pattern == jsetOrderEnd)
+            {
+                jmpStop(mp);
+                orderOK = TRUE;
+            }
+            else
+            {
+                // All OK
+                orderOK = TRUE;
+                mp->pattern = mp->module->patterns[pattern];
+                mp->npattern = pattern;
+                mp->order = order;
+            }
+        }
+    }
+}
+
+
+/* Set new tempo-value of the player.
+ */
+static void jmpSetTempo(JSSPlayer * mp, int tempo)
+{
+    assert(mp != NULL);
+    JSS_LOCK(mp);
+    assert(mp->device != NULL);
+
+    mp->tempo = tempo;
+    jvmSetCallbackFreq(mp->device, (mp->device->outFreq * 5) / (tempo * 2));
+    JSS_UNLOCK(mp);
+}
+
+
+static void jmpClearChannels(JSSPlayer * mp)
+{
+    int i;
+    assert(mp != NULL);
+    JSS_LOCK(mp);
+    assert(mp->device != NULL);
+    assert(mp->module != NULL);
+
+    for (i = 0; i < mp->module->nchannels; i++)
+        jvmStop(mp->device, i);
+
+    // Initialize channel data
+    for (i = 0; i < jsetNChannels; i++)
+        jmpClearChannel(&mp->channels[i]);
+
+    JSS_UNLOCK(mp);
+}
+
+
+/* Starts playing module from a given ORDER.
+ */
+static int jmpPlayStart(JSSPlayer *mp)
+{
+    int result;
+
+    mp->speed = mp->module->defSpeed;
+    jmpSetTempo(mp, mp->module->defTempo);
+
+    result = jvmSetCallback(mp->device, jmpExec, (void *) mp);
+    if (result != DMERR_OK)
+    {
+        JSSERROR(result, result, "Could not initialize callback for player.\n");
+    }
+
+    mp->isPlaying = TRUE;
+    return DMERR_OK;
+}
+
+
+int jmpChangeOrder(JSSPlayer *mp, int order)
+{
+    assert(mp != NULL);
+
+    JSS_LOCK(mp);
+    assert(mp->module != NULL);
+
+    jmpClearChannels(mp);
+    jmpClearPlayer(mp);
+
+    jmpSetNewOrder(mp, order);
+    if (mp->order == jsetNotSet)
+    {
+        JSS_UNLOCK(mp);
+        JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
+             "Could not start playing from given order #%i\n", order);
+    }
+
+    JSS_UNLOCK(mp);
+    return DMERR_OK;
+}
+
+
+int jmpPlayOrder(JSSPlayer * mp, int order)
+{
+    int result;
+    assert(mp != NULL);
+
+    JSS_LOCK(mp);
+    assert(mp->module != NULL);
+
+    // Stop if already playing
+    jmpStop(mp);
+    jmpClearChannels(mp);
+    jmpClearPlayer(mp);
+
+    // Check starting order
+    jmpSetNewOrder(mp, order);
+    if (mp->order == jsetNotSet)
+    {
+        JSS_UNLOCK(mp);
+        JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED,
+             "Could not start playing from given order #%i\n", order);
+    }
+
+    if ((result = jmpPlayStart(mp)) != DMERR_OK)
+    {
+        JSS_UNLOCK(mp);
+        return result;
+    }
+
+    JSS_UNLOCK(mp);
+    return DMERR_OK;
+}
+
+
+/* Play given pattern
+ */
+int jmpPlayPattern(JSSPlayer * mp, int pattern)
+{
+    int result;
+    assert(mp != NULL);
+    JSS_LOCK(mp);
+    assert(mp->module != NULL);
+
+    // Stop if already playing
+    jmpStop(mp);
+    jmpClearPlayer(mp);
+
+    mp->npattern = pattern;
+
+    if ((result = jmpPlayStart(mp)) != DMERR_OK)
+    {
+        JSS_UNLOCK(mp);
+        return result;
+    }
+
+    JSS_UNLOCK(mp);
+    return DMERR_OK;
+}
+
+
+/* Set volume for given module channel.
+ */
+static void jmpSetVolume(JSSPlayerChannel * chn, int channel, int volume)
+{
+    (void) channel;
+
+    chn->volume = chn->cvolume = dmClamp(volume, mpMinVol, mpMaxVol);
+    JMPSETNDFLAGS(cdfNewVolume);
+}
+
+#define jmpChangeVolume(Q, Z, X) jmpSetVolume(Q, Z, chn->volume + (X))
+
+
+/* Change the pitch of given channel by ADelta.
+ */
+static void jmpChangePitch(JSSPlayerChannel *chn, int channel, int delta)
+{
+    int value;
+    (void) channel;
+
+    // Calculate new pitch and check it
+    value = chn->pitch + delta;
+    if (value < 0)
+        value = 0;
+
+    chn->pitch = value;
+    JMPSETNDFLAGS(cdfNewPitch);
+}
+
+
+/* Do a note portamento (pitch slide) effect for given module channel.
+ */
+static void jmpDoPortamento(JSSPlayerChannel * chn, int channel)
+{
+    (void) channel;
+
+    // Check for zero parameter
+    if (chn->iLastPortaToNoteParam == 0)
+    {
+        JMPSETNDFLAGS(cdfNewPitch);
+        return;
+    }
+
+    /* Slide the pitch of channel to the destination value
+     * with speed of iLastPortaToNoteParam[] * 4 and stop when it equals.
+     */
+    if (chn->pitch < chn->iLastPortaToNotePitch)
+    {
+        // Increase pitch UP
+        jmpChangePitch(chn, channel, chn->iLastPortaToNoteParam * 4);
+        if (chn->pitch > chn->iLastPortaToNotePitch)
+            chn->pitch = chn->iLastPortaToNotePitch;
+    }
+    else
+    if (chn->pitch > chn->iLastPortaToNotePitch)
+    {
+        // Decrease pitch DOWN
+        jmpChangePitch(chn, channel, -(chn->iLastPortaToNoteParam * 4));
+        if (chn->pitch < chn->iLastPortaToNotePitch)
+            chn->pitch = chn->iLastPortaToNotePitch;
+    }
+}
+
+
+/* Do a tremolo effect for given module channel.
+ */
+static void jmpDoTremolo(JSSPlayerChannel *chn, int channel)
+{
+    (void) channel;
+    
+    if (chn->tremolo.depth != 0 && chn->tremolo.speed != 0)
+    {
+        int delta, tmp = chn->tremolo.pos & 31;
+
+        switch (chn->tremolo.wc & 3)
+        {
+            case 0:
+                delta = jmpSineTab[tmp];
+                break;
+            case 1:
+                tmp <<= 3;
+                delta = (chn->tremolo.pos < 0) ? 255 - tmp : tmp;
+                break;
+            case 2:
+                delta = 255;
+                break;
+            case 3:
+                delta = jmpSineTab[tmp];
+                break;
+        }
+
+        delta = (delta * chn->tremolo.depth) >> 6;
+        jmpCSetVolume(chn, channel, chn->cvolume + (chn->tremolo.pos >= 0 ? delta : -delta));
+
+        chn->tremolo.pos += chn->tremolo.speed;
+        if (chn->tremolo.pos > 31)
+            chn->tremolo.pos -= 64;
+    }
+}
+
+
+/* Do a vibrato effect for given module channel.
+ */
+static void jmpDoVibrato(JSSPlayerChannel *chn, int channel)
+{
+    (void) channel;
+
+    if (chn->vibrato.depth != 0 && chn->vibrato.speed != 0)
+    {
+        int delta, tmp = chn->vibrato.pos & 31;
+
+        switch (chn->vibrato.wc & 3)
+        {
+            case 0:
+                delta = jmpSineTab[tmp];
+                break;
+            case 1:
+                tmp <<= 3;
+                delta = (chn->vibrato.pos < 0) ? 255 - tmp : tmp;
+                break;
+            case 2:
+                delta = 255;
+                break;
+            case 3:
+                delta = jmpSineTab[tmp];
+                break;
+        }
+
+        delta = ((delta * chn->vibrato.depth) >> 7) << 2;
+        chn->freq = chn->cfreq + (chn->vibrato.pos >= 0 ? delta : -delta);
+        JMPSETNDFLAGS(cdfNewFreq);
+
+        chn->vibrato.pos += chn->vibrato.speed;
+        if (chn->vibrato.pos > 31)
+            chn->vibrato.pos -= 64;
+    }
+}
+
+
+/* Do a volume slide effect for given module channel.
+ */
+static void jmpDoVolumeSlide(JSSPlayerChannel * chn, int channel, int param)
+{
+    int paramX, paramY;
+
+    JMPMAKEPARAM(param, paramX, paramY)
+
+    if (paramY == 0)
+        jmpChangeVolume(chn, channel, paramX);
+    if (paramX == 0)
+        jmpChangeVolume(chn, channel, -paramY);
+}
+
+static void jmpTriggerNote(JSSPlayer * mp, JSSPlayerChannel *chn, BOOL newExtInstrument);
+
+
+static void jmpDoMultiRetrigNote(JSSPlayer *mp, JSSPlayerChannel *chn, int channel)
+{
+    if (chn->lastMultiRetrigParamY != 0 &&
+       (mp->tick % chn->lastMultiRetrigParamY) == 0)
+    {
+        BOOL change = TRUE;
+        int volume = chn->volume;
+        switch (chn->lastMultiRetrigParamX)
+        {
+            case 0x1: volume -= 1; break;
+            case 0x2: volume -= 2; break;
+            case 0x3: volume -= 4; break;
+            case 0x4: volume -= 8; break;
+            case 0x5: volume -= 16; break;
+            case 0x6: volume  = (volume * 2) / 3; break;
+            case 0x7: volume /= 2; break;
+
+            case 0x9: volume += 1; break;
+            case 0xA: volume += 2; break;
+            case 0xB: volume += 4; break;
+            case 0xC: volume += 8; break;
+            case 0xD: volume += 16; break;
+            case 0xE: volume  = (volume * 3) / 2; break;
+            case 0xF: volume *= 2; break;
+            default: change = FALSE;
+        }
+        jmpTriggerNote(mp, chn, FALSE);
+        if (change)
+            jmpSetVolume(chn, channel, volume);
+    }
+}
+
+
+/* Execute a pattern loop effect/command for given module channel.
+ *
+ * This routine works for most of the supported formats, as they
+ * use the 'standard' implementation ascending from MOD. However,
+ * here is included also a slightly kludgy implementation of the
+ * FT2 patloop bug.
+ */
+static void jmpDoPatternLoop(JSSPlayer * mp, JSSPlayerChannel *chn, int channel, int paramY)
+{
+    (void) channel;
+
+    // Check what we need to do
+    if (paramY > 0)
+    {
+        // SBx/E6x loops 'x' times
+        if (chn->iPatLoopCount == 1)
+            chn->iPatLoopCount = 0;
+        else
+        {
+            // Check if we need to set the count
+            if (chn->iPatLoopCount == 0)
+                chn->iPatLoopCount = paramY + 1;
+
+            // Loop to specified row
+            chn->iPatLoopCount--;
+            mp->newRow = chn->iPatLoopRow;
+            mp->newRowSet = TRUE;
+        }
+    }
+    else
+    {
+        // SB0/E60 sets the loop start point
+        chn->iPatLoopRow = mp->row;
+
+        // This is here because of the infamous FT2 patloop bug
+        mp->lastPatLoopRow = mp->row;
+    }
+}
+
+
+/* Do arpeggio effect
+ */
+static void jmpDoArpeggio(JSSPlayer * mp, JSSPlayerChannel *chn, int channel, int paramY, int paramX)
+{
+    JSSInstrument *inst = chn->instrument;
+    (void) channel;
+
+    if (inst != NULL)
+    {
+        int tmp = chn->note;
+        if (tmp == jsetNotSet || tmp == jsetNoteOff)
+            return;
+
+        switch (mp->tick & 3)
+        {
+            case 1:
+                tmp += paramX;
+                break;
+            case 2:
+                tmp += paramY;
+                break;
+        }
+
+        tmp = dmClamp(tmp + inst->ERelNote, 0, 119);
+        jmpCSetPitch(mp, chn, jmpGetPeriodFromNote(mp, tmp, inst->EFineTune));
+    }
+}
+
+
+/* Trigger a new note on the given channel.
+ * Separate function used from various places where note
+ * triggering is needed (retrig, multi-retrig, etc.)
+ */
+static void jmpTriggerNote(JSSPlayer * mp, JSSPlayerChannel *chn, BOOL newExtInstrument)
+{
+    if (chn->nextInstrument >= 0 &&
+        chn->nextInstrument < mp->module->nextInstruments &&
+        mp->module->extInstruments[chn->nextInstrument] != NULL)
+    {
+        chn->extInstrument = mp->module->extInstruments[chn->nextInstrument];
+    }
+    else
+    {
+        chn->extInstrument = NULL;
+        chn->instrument    = NULL;
+        chn->ninstrument   = jsetNotSet;
+    }
+
+    if (chn->extInstrument != NULL)
+    {
+        int tmp = chn->extInstrument->sNumForNotes[chn->note];
+
+        if (tmp >= 0 && tmp < mp->module->ninstruments &&
+            mp->module->instruments[tmp] != NULL)
+        {
+            if (chn->ninstrument != tmp)
+                JMPSETNDFLAGS(cdfNewInstr);
+            else
+                JMPSETNDFLAGS(cdfPlay);
+
+            chn->ninstrument = tmp;
+            chn->instrument  = mp->module->instruments[tmp];
+
+            if (newExtInstrument)
+            {
+                chn->volume  = chn->instrument->volume;
+                chn->panning = chn->instrument->EPanning;
+                JMPSETNDFLAGS(cdfNewPanPos | cdfNewVolume);
+            }
+        }
+    }
+
+    if (chn->instrument != NULL)
+    {
+        int tmp;
+        JSSInstrument *inst = chn->instrument;
+
+        // Save old pitch for later use
+        chn->oldPitch = chn->pitch;
+
+        chn->position = 0;
+
+        // Compute new pitch
+        tmp = dmClamp(chn->note + inst->ERelNote, 0, 119);
+        chn->pitch = jmpGetPeriodFromNote(mp, tmp, inst->EFineTune);
+        JMPSETNDFLAGS(cdfNewPitch | cdfPlay | cdfNewPos);
+    }
+    else
+    {
+        chn->volume = 0;
+        chn->panning = jchPanMiddle;
+        JMPSETNDFLAGS(cdfStop | cdfNewPanPos | cdfNewVolume);
+    }
+}
+
+
+/*
+ * Process a new pattern row
+ */
+static void jmpProcessNewRow(JSSPlayer * mp, int channel)
+{
+    JSSNote *currNote;
+    BOOL newNote = FALSE, newExtInstrument = FALSE, volumePortaSet = FALSE;
+    char effect;
+    int param, paramX, paramY;
+    JSSPlayerChannel *chn = &(mp->channels[channel]);
+
+    JMPGETNOTE(currNote, mp->row, channel);
+
+    // Check for a new note/keyoff here
+    if (currNote->note == jsetNoteOff)
+        chn->keyOff = TRUE;
+    else
+    if (currNote->note >= 0 && currNote->note <= 96)
+    {
+        newNote = TRUE;
+        chn->oldNote = chn->note;
+        chn->note = currNote->note;
+        chn->keyOff = FALSE;
+    }
+
+    // Check for new instrument
+    if (currNote->instrument != jsetNotSet)
+    {
+        /* Envelopes and ext.instrument fadeout are initialized always if
+         * new instrument is set, even if the instrument does not exist.
+         */
+        jmpResetEnvelope(&chn->volumeEnv);
+        jmpResetEnvelope(&chn->panningEnv);
+        chn->keyOff = FALSE;
+        chn->fadeOutVol = mpMaxFadeoutVol;
+
+        JMPSETNDFLAGS(cdfNewPanPos | cdfPlay | cdfNewVolume);
+
+        // We save the instrument number here for later use
+        chn->nextInstrument = currNote->instrument;
+        newExtInstrument = TRUE;
+    }
+
+    if (newNote)
+    {
+        jmpTriggerNote(mp, chn, newExtInstrument);
+    }
+
+    // Process the volume column
+    JMPMAKEPARAM(currNote->volume, paramX, paramY);
+
+    switch (paramX)
+    {
+        case 0x0:
+        case 0x1:
+        case 0x2:
+        case 0x3:
+        case 0x4:
+            jmpSetVolume(chn, channel, currNote->volume);
+            break;
+
+        case 0x7:        // Dx = Fine Volumeslide Down : IMPL.VERIFIED
+            jmpChangeVolume(chn, channel, -paramY);
+            break;
+
+        case 0x8:        // Ux = Fine Volumeslide Up : IMPL.VERIFIED
+            jmpChangeVolume(chn, channel, paramY);
+            break;
+
+        case 0x9:        // Sx = Set vibrato speed : IMPL.VERIFIED
+            chn->vibrato.speed = paramY;
+            break;
+
+        case 0xa:        // Vx = Vibrato : IMPL.VERIFIED
+            if (paramY)
+                chn->vibrato.depth = paramY;
+            break;
+
+        case 0xe:        // Mx = Porta To Note : IMPL.VERIFIED
+            if (paramY)
+                chn->iLastPortaToNoteParam = paramY * 16;
+
+            if (currNote->note != jsetNotSet && currNote->note != jsetNoteOff)
+            {
+                chn->lastPortaToNoteNote = chn->note;
+                chn->iLastPortaToNotePitch = chn->pitch;
+                chn->pitch = chn->oldPitch;
+                chn->note = chn->oldNote;
+                JMPUNSETNDFLAGS(cdfNewPitch | cdfPlay);
+                volumePortaSet = TRUE;
+            }
+            break;
+    }
+
+    // ...And finally process the Normal effects
+    if (currNote->effect == jsetNotSet)
+        return;
+
+    param = currNote->param;
+    JMPMAKEPARAM(param, paramX, paramY);
+    JMPGETEFFECT(effect, currNote->effect);
+
+    switch (effect)
+    {
+        case '0':        // 0xy = Arpeggio
+            jmpDoArpeggio(mp, chn, channel, paramX, paramY);
+            break;
+
+        case 'W':        // Used widely in demo-music as MIDAS Sound System sync-command
+        case 'Q':        // SoundTracker/OpenCP: Qxx = Set LP filter resonance
+        case 'Z':        // SoundTracker/OpenCP: Zxx = Set LP filter cutoff freq
+            break;
+
+        case '1':
+        case '2':        // 1xy = Portamento Up, 2xy = Portamento Down : IMPL.VERIFIED
+            if (param)
+                chn->iLastPortaParam = param;
+            break;
+
+        case '3':        // 3xy = Porta To Note
+            if (volumePortaSet)
+                break;
+
+            if (param)
+                chn->iLastPortaToNoteParam = param;
+
+            if (currNote->note != jsetNotSet && currNote->note != jsetNoteOff)
+            {
+                chn->lastPortaToNoteNote = chn->note;
+                chn->iLastPortaToNotePitch = chn->pitch;
+                chn->pitch = chn->oldPitch;
+                chn->note = chn->oldNote;
+                JMPUNSETNDFLAGS(cdfNewPitch | cdfPlay);
+            }
+            break;
+
+        case '4':        // 4xy = Vibrato : IMPL.VERIFIED
+            if (paramX)
+                chn->vibrato.speed = paramX;
+
+            if (paramY)
+                chn->vibrato.depth = paramY;
+
+            if ((chn->vibrato.wc & 4) == 0)
+                chn->vibrato.pos = 0;
+            break;
+
+        case '5':        // 5xy = Portamento + Volume Slide
+        case '6':        // 6xy = Vibrato + Volume slide
+            if (param)
+                chn->iLastVolSlideParam = param;
+            break;
+
+        case '7':        // 7xy = Tremolo
+            if (paramX)
+                chn->tremolo.speed = paramX;
+
+            if (paramY)
+                chn->tremolo.depth = paramY;
+
+            if ((chn->tremolo.wc & 4) == 0)
+                chn->tremolo.pos = 0;
+            break;
+
+        case '8':        // 8xx = Set Panning
+            chn->panning = param;
+            JMPSETNDFLAGS(cdfNewPanPos);
+            break;
+
+        case '9':        // 9xx = Set Sample Offset : IMPL.VERIFIED
+            if (param != 0)
+                chn->lastSampleOffsetParam = param;
+            if (chn->newDataFlags & cdfNewPitch && chn->instrument != NULL)
+            {
+                int pos = chn->lastSampleOffsetParam * 0x100,
+                    end = (chn->instrument->flags & jsfLooped) ?
+                          chn->instrument->loopE : chn->instrument->size;
+                if (pos <= end)
+                {
+                    chn->position = pos;
+                    JMPSETNDFLAGS(cdfNewPos);
+                }
+                else
+                {
+                    JMPSETNDFLAGS(cdfStop);
+                }
+            }
+            break;
+
+        case 'A':        // Axy = Volume Slide : IMPL.VERIFIED
+            if (param)
+                chn->iLastVolSlideParam = param;
+            break;
+
+        case 'B':        // Bxx = Pattern Jump : IMPL.VERIFIED
+            mp->newOrder = param;
+            mp->newOrderSet = TRUE;
+            mp->jumpFlag = TRUE;
+            mp->lastPatLoopRow = 0;
+            break;
+
+        case 'C':        // Cxx = Set Volume : IMPL.VERIFIED
+            jmpSetVolume(chn, channel, param);
+            break;
+
+        case 'D':        // Dxx = Pattern Break : IMPL.VERIFIED
+            // Compute the new row
+            mp->newRow = (paramX * 10) + paramY;
+            if (mp->newRow >= mp->pattern->nrows)
+                mp->newRow = 0;
+
+            mp->newRowSet = TRUE;
+
+            // Now we do some tricky tests
+            if (!mp->breakFlag && !mp->jumpFlag)
+            {
+                mp->newOrder = mp->order + 1;
+                mp->newOrderSet = TRUE;
+            }
+
+            mp->breakFlag = TRUE;
+            break;
+
+        case 'E':         // Exy = Special Effects
+            switch (paramX)
+            {
+                case 0x00:    // E0x - Set filter (NOT SUPPORTED)
+                    JMPDEBUG("Set Filter used, UNSUPPORTED");
+                    break;
+
+                case 0x01:    // E1x - Fine Portamento Up
+                    if (paramY)
+                        chn->iCLastFinePortamentoUpParam = paramY;
+
+                    jmpChangePitch(chn, channel, -(chn->iCLastFinePortamentoUpParam * 4));
+                    break;
+
+                case 0x02:    // E2x - Fine Portamento Down
+                    if (paramY)
+                        chn->iCLastFinePortamentoDownParam = paramY;
+
+                    jmpChangePitch(chn, channel, (chn->iCLastFinePortamentoDownParam * 4));
+                    break;
+
+                case 0x03:    // E3x - Glissando Control (NOT SUPPORTED)
+                    break;
+
+                case 0x04:    // E4x - Set Vibrato waveform
+                    chn->vibrato.wc = paramY;
+                    break;
+
+                case 0x05:    // E5x - Set Finetune
+                    JMPDEBUG("Set Finetune used, UNIMPLEMENTED");
+                    break;
+
+                case 0x06:    // E6x - Set Pattern Loop
+                    jmpDoPatternLoop(mp, chn, channel, paramY);
+                    break;
+
+                case 0x07:    // E7x - Set Tremolo waveform
+                    chn->tremolo.wc = paramY;
+                    break;
+
+                case 0x08:    // E8x - Set Pan Position
+                    chn->panning = (paramY * 16);
+                    JMPSETNDFLAGS(cdfNewPanPos);
+                    break;
+
+                case 0x09:    // E9x - Retrig note
+                    if (mp->tick == paramY)
+                        jmpTriggerNote(mp, chn, FALSE);
+                    break;
+
+                case 0x0a:    // EAx - Fine Volumeslide Up
+                    if (paramY)
+                        chn->iCLastFineVolumeslideUpParam = paramY;
+
+                    jmpChangeVolume(chn, channel, chn->iCLastFineVolumeslideUpParam);
+                    break;
+
+                case 0x0b:    // EBx - Fine Volumeslide Down
+                    if (paramY)
+                        chn->iCLastFineVolumeslideDownParam = paramY;
+                    jmpChangeVolume(chn, channel, -(chn->iCLastFineVolumeslideDownParam));
+                    break;
+
+                case 0x0c:    // ECx - Set Note Cut (NOT PROCESSED IN TICK0)
+                    break;
+
+                case 0x0d:    // EDx - Set Note Delay : IMPL.VERIFIED
+                    if (paramY > 0)
+                    {
+                        // Save the ND-flags, then clear
+                        chn->iSaveNDFlags = chn->newDataFlags;
+                        chn->newDataFlags = 0;
+                        // TODO .. does this only affect NOTE or also instrument?
+                    }
+                    break;
+
+                case 0x0e:    // EEx - Set Pattern Delay : IMPL.VERIFIED
+                    mp->patternDelay = paramY;
+                    break;
+
+                case 0x0f:    // EFx - Invert Loop (NOT SUPPORTED)
+                    JMPDEBUG("Invert Loop used, UNSUPPORTED");
+                    break;
+
+                default:
+                    JMPDEBUG("Unsupported special command used");
+            }
+            break;
+
+        case 'F':        // Fxy = Set Speed / Tempo : IMPL.VERIFIED
+            if (param > 0)
+            {
+                if (param < 0x20)
+                    mp->speed = param;
+                else
+                    jmpSetTempo(mp, param);
+            }
+            break;
+
+        case 'G':        // Gxx = Global Volume
+            mp->globalVol = param;
+            JMPSETNDFLAGS(cdfNewGlobalVol);
+            break;
+
+
+        case 'H':        // Hxx = Global Volume Slide
+            JMPDEBUG("Global Volume Slide used, UNIMPLEMENTED");
+            break;
+
+        case 'K':        // Kxx = Key-off (Same as key-off note)
+            chn->keyOff = TRUE;
+            break;
+
+        case 'L':        // Lxx = Set Envelope Position
+            JMPDEBUG("Set Envelope Position used, NOT verified with FT2");
+            chn->panningEnv.frame = param;
+            chn->volumeEnv.frame = param;
+            chn->panningEnv.exec = TRUE;
+            chn->volumeEnv.exec = TRUE;
+            break;
+
+        case 'R':        // Rxy = Multi Retrig note
+            if (paramX != 0)
+                chn->lastMultiRetrigParamX = paramX;
+            if (paramY != 0)
+                chn->lastMultiRetrigParamY = paramY;
+            break;
+
+        case 'T':        // Txy = Tremor
+            if (param)
+                chn->iLastTremorParam = param;
+            break;
+
+        case 'X':        // Xxy = Extra Fine Portamento
+            switch (paramX)
+            {
+                case 0x01:    // X1y - Extra Fine Portamento Up
+                    if (paramY)
+                        chn->iCLastExtraFinePortamentoUpParam = paramY;
+
+                    jmpChangePitch(chn, channel, - chn->iCLastExtraFinePortamentoUpParam);
+                    break;
+
+                case 0x02:    // X2y - Extra Fine Portamento Down
+                    if (paramY)
+                        chn->iCLastExtraFinePortamentoDownParam = paramY;
+
+                    jmpChangePitch(chn, channel, chn->iCLastExtraFinePortamentoUpParam);
+                    break;
+
+                default:
+                    JMPDEBUG("Unsupported value in Extra Fine Portamento command!");
+                    break;
+            }
+            break;
+
+        default:
+            JMPDEBUG("Unsupported effect");
+            break;
+    }
+}
+
+
+static void jmpProcessEffects(JSSPlayer * mp, int channel)
+{
+    JSSPlayerChannel *chn = &(mp->channels[channel]);
+    JSSNote *currNote;
+    int param, paramX, paramY, tmp;
+    char effect;
+
+    // Process the volume column effects
+    JMPGETNOTE(currNote, mp->row, channel);
+    JMPMAKEPARAM(currNote->volume, paramX, paramY);
+
+    switch (paramX)
+    {
+        case 0x05: // -x = Volumeslide Down : IMPL.VERIFIED
+            jmpChangeVolume(chn, channel, -paramY);
+            break;
+
+        case 0x06: // +x = Volumeslide Down : IMPL.VERIFIED
+            jmpChangeVolume(chn, channel, paramY);
+            break;
+
+        case 0x0a: // Vx = Vibrato : IMPL.VERIFIED
+            jmpDoVibrato(chn, channel);
+            break;
+
+        case 0x0e: // Mx = Porta To Note : IMPL.VERIFIED
+            jmpDoPortamento(chn, channel);
+            break;
+    }
+
+    // ...And finally process the Normal effects
+    if (currNote->effect == jsetNotSet)
+        return;
+    
+    param = currNote->param;
+    JMPMAKEPARAM(param, paramX, paramY);
+    JMPGETEFFECT(effect, currNote->effect);
+
+    switch (effect)
+    {
+        case '0': // 0xy = Arpeggio
+            jmpDoArpeggio(mp, chn, channel, paramX, paramY);
+            break;
+
+        case '1': // 1xy = Portamento Up
+            if (chn->iLastPortaParam > 0)
+                jmpChangePitch(chn, channel, -(chn->iLastPortaParam * 4));
+            break;
+
+        case '2': // 2xy = Portamento Down
+            if (chn->iLastPortaParam > 0)
+                jmpChangePitch(chn, channel, (chn->iLastPortaParam * 4));
+            break;
+
+        case '3': // 3xy = Porta To Note
+            jmpDoPortamento(chn, channel);
+            break;
+
+        case '4': // 4xy = Vibrato
+            jmpDoVibrato(chn, channel);
+            break;
+
+        case '5': // 5xy = Portamento + Volume Slide
+            jmpDoPortamento(chn, channel);
+            jmpDoVolumeSlide(chn, channel, chn->iLastVolSlideParam);
+            break;
+
+        case '6': // 6xy = Vibrato + Volume Slide
+            jmpDoVibrato(chn, channel);
+            jmpDoVolumeSlide(chn, channel, chn->iLastVolSlideParam);
+            break;
+
+        case '7': // 7xy = Tremolo
+            jmpDoTremolo(chn, channel);
+            break;
+
+        case 'A': // Axy = Volume slide
+            jmpDoVolumeSlide(chn, channel, chn->iLastVolSlideParam);
+            break;
+
+        case 'E': // Exy = Special Effects
+            switch (paramX)
+            {
+                case 0x09:    // E9x - Retrig note
+                    if (mp->tick == paramY)
+                        jmpTriggerNote(mp, chn, FALSE);
+                    break;
+
+                case 0x0c: // ECx - Set Note Cut
+                    if (mp->tick == paramY)
+                        jmpSetVolume(chn, channel, jsetMinVol);
+                    break;
+
+                case 0x0d: // EDx - Set Note Delay
+                    if (mp->tick == paramY)
+                        chn->newDataFlags = chn->iSaveNDFlags;
+                    break;
+            }
+            break;
+
+        case 'R':        // Rxy = Multi Retrig note
+            jmpDoMultiRetrigNote(mp, chn, channel);
+            break;
+
+        case 'T': // Txy = Tremor
+            JMPMAKEPARAM(chn->iLastTremorParam, paramX, paramY)
+            paramX++;
+            paramY++;
+            tmp = chn->iTremorCount % (paramX + paramY);
+
+            if (tmp < paramX)
+                jmpCSetVolume(chn, channel, chn->cvolume);
+            else
+                jmpCSetVolume(chn, channel, jsetMinVol);
+
+            chn->iTremorCount = tmp + 1;
+            break;
+    }
+}
+
+
+/* This is the main processing callback-loop of a module player.
+ * It processes the ticks, calling the needed jmpProcessNewRow()
+ * and jmpProcessEffects() methods for processing the module playing.
+ */
+void jmpExec(void *pDEV, void *pMP)
+{
+    JSSPlayer *mp;
+    JSSMixer *dev;
+    int channel;
+
+    // Check some things via assert()
+    mp = (JSSPlayer *) pMP;
+    JSS_LOCK(mp);
+
+    dev = (JSSMixer *) pDEV;
+
+    // Check if we are playing
+    if (!mp->isPlaying)
+        goto out;
+
+    // Clear channel new data flags
+    mp->jumpFlag = FALSE;
+    mp->breakFlag = FALSE;
+
+    for (channel = 0; channel < jsetNChannels; channel++)
+        mp->channels[channel].newDataFlags = 0;
+
+//fprintf(stderr, "1: tick=%d, order=%d, iPattern=%d, row=%d\n", mp->tick, mp->order, mp->npattern, mp->row);
+
+    // Check for init-tick
+    if (mp->tick == jsetNotSet)
+    {
+        // Initialize pattern
+        mp->newRow = 0;
+        mp->newRowSet = TRUE;
+        mp->tick = mp->speed;
+        mp->patternDelay = 0;
+    }
+
+//fprintf(stderr, "2: tick=%d, order=%d, iPattern=%d, row=%d\n", mp->tick, mp->order, mp->npattern, mp->row);
+
+    // Check if we are playing
+    if (!mp->isPlaying)
+        goto out;
+
+    assert(mp->pattern);
+
+    // Update the tick
+    mp->tick++;
+    if (mp->tick >= mp->speed)
+    {
+        // Re-init tick counter
+        mp->tick = 0;
+
+        // Check pattern delay
+        if (mp->patternDelay > 0)
+            mp->patternDelay--;
+        else
+        {
+            // New pattern row
+            if (mp->newRowSet)
+            {
+                mp->row = mp->newRow;
+                mp->newRowSet = FALSE;
+            } else
+                mp->row++;
+
+            // Check for end of pattern
+            if (mp->row >= mp->pattern->nrows)
+            {
+                // Go to next order
+                if (mp->order != jsetNotSet)
+                    jmpSetNewOrder(mp, mp->order + 1);
+                else
+                    mp->isPlaying = FALSE;
+                
+                // Check for FT2 quirks
+                if (JMPGETMODFLAGS(mp, jmdfFT2Replay))
+                    mp->row = mp->lastPatLoopRow;
+                else
+                    mp->row = 0;
+            }
+
+            if (!mp->isPlaying)
+                goto out;
+
+            // Check current order
+            if (mp->newOrderSet)
+            {
+                jmpSetNewOrder(mp, mp->newOrder);
+                mp->newOrderSet = FALSE;
+            }
+
+//fprintf(stderr, "3: tick=%d, order=%d, iPattern=%d, row=%d\n", mp->tick, mp->order, mp->npattern, mp->row);
+
+            if (!mp->isPlaying)
+                goto out;
+
+            // TICK #0: Process new row
+            for (channel = 0; channel < mp->module->nchannels; channel++)
+                jmpProcessNewRow(mp, channel);
+        } // patternDelay
+    } // tick
+    else
+    {
+        // Implement FT2's pattern delay-effect: don't update effects while on patdelay
+        if (!JMPGETMODFLAGS(mp, jmdfFT2Replay) ||
+            (JMPGETMODFLAGS(mp, jmdfFT2Replay) && mp->patternDelay <= 0))
+        {
+            // TICK n: Process the effects
+            for (channel = 0; channel < mp->module->nchannels; channel++)
+                jmpProcessEffects(mp, channel);
+        }
+    }
+
+    // Check if playing has stopped
+    if (!mp->isPlaying)
+        goto out;
+
+    // Update player data to audio device/mixer
+    for (channel = 0; channel < mp->module->nchannels; channel++)
+    {
+        JSSPlayerChannel *chn = &mp->channels[channel];
+
+        // Process extended instruments
+        jmpProcessExtInstrument(chn, channel);
+        
+        // Check NDFlags and update channel data
+        int flags = chn->newDataFlags;
+        if (!flags)
+            continue;
+
+        // Check if we stop?
+        if (flags & cdfStop)
+        {
+            jvmStop(mp->device, channel);
+        }
+        else
+        {
+            // No, handle other flags
+            if (flags & cdfNewInstr)
+            {
+                JSSInstrument *instr = chn->instrument;
+                if (instr != NULL)
+                {
+                    jvmSetSample(mp->device, channel,
+                        instr->data, instr->size,
+                        instr->loopS, instr->loopE,
+                        instr->flags);
+                }
+            }
+
+            if (flags & cdfPlay)
+            {
+                jvmReset(mp->device, channel);
+                jvmPlay(mp->device, channel);
+            }
+
+            if (flags & cdfNewPitch)
+                jmpCSetPitch(mp, chn, chn->pitch);
+                
+            if (flags & (cdfNewFreq | cdfNewPitch))
+                jvmSetFreq(mp->device, channel, chn->freq);
+
+            if (flags & cdfNewPos)
+                jvmSetPos(mp->device, channel, chn->position);
+
+            if (flags & cdfNewVolume)
+            {
+                BOOL init = flags & (cdfNewInstr | cdfPlay);
+                jvmSetVolumeRamp(mp->device, channel,
+                    init ? 0 : jvmGetVolume(mp->device, channel),
+                    (chn->fadeOutVol * chn->volumeEnv.value * chn->volume) / (16 * 65536),
+                    init ? 5 : 0);
+            }
+
+            if (flags & cdfNewPanPos)
+            {
+                jvmSetPanRamp(mp->device, channel,
+                    jvmGetPan(mp->device, channel),
+                    chn->panning + (((chn->panningEnv.value - 32) * (128 - abs(chn->panning - 128))) / 32),
+                    0);
+            }
+
+            if (flags & cdfNewGlobalVol)
+                jvmSetGlobalVol(mp->device, mp->globalVol);
+        }
+    }
+
+out:
+    JSS_UNLOCK(mp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/minijss/jssplr.h	Tue Apr 16 07:32:29 2013 +0300
@@ -0,0 +1,186 @@
+/*
+ * miniJSS - Module playing routines
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2006-2012 Tecnic Software productions (TNSP)
+ */
+#ifndef JSSPLR_H
+#define JSSPLR_H
+
+#include "jss.h"
+#include "jssmod.h"
+#include "jssmix.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Player general constants
+#define mpMinVol            (0)
+#define mpMaxVol            (64)
+#define mpPanCenter         (0)
+//#define mpMaxFadeoutVol     (65535)
+#define mpMaxFadeoutVol     (32767)
+
+
+// Channel New-Data flags
+#define cdfNONE             (0x000)    // Set nothing
+#define cdfNewInstr         (0x001)    // Set a new instrument
+#define cdfNewPitch         (0x002)    // Set a new pitch
+#define cdfNewPos           (0x004)    // Set a new position
+#define cdfNewVolume        (0x008)    // Set a new volume
+#define cdfNewPanPos        (0x010)    // Set a new panning position
+#define cdfNewGlobalVol     (0x020)    // Set a new global volume
+#define cdfNewFreq          (0x040)    // New frequency
+#define cdfPlay             (0x100)    // Start or restart playing sample
+#define cdfStop             (0x200)    // Stop channel playing
+
+
+// Player envelope data
+typedef struct
+{
+    int frame, value;
+    BOOL exec;
+} JSSPlayerEnvelope;
+
+
+typedef struct
+{
+    int pos, speed, depth, wc;
+} JSSPlayerVData;
+
+
+// Player channel structure
+typedef struct
+{
+    JSSPlayerEnvelope volumeEnv, panningEnv;
+
+    JSSPlayerVData vibrato, tremolo;
+        
+    BOOL    keyOff;
+
+    JSSInstrument  *instrument;           // Instrument
+    JSSExtInstrument *extInstrument;       // ExtInstrument
+    int     ninstrument,
+            nextInstrument,
+            note,                 // Current note
+            pitch,                // Pitch (NOT actual frequency!)
+            freq, cfreq,
+            oldPitch,
+            oldNote,
+            position,             // Sample position
+            volume, cvolume,      // Volume
+            panning,              // Panning position
+
+            newDataFlags,         // New data flags
+
+            fadeOutVol,
+
+            iCAutoVib_Frame,
+
+            iCLastFineVolumeslideUpParam,
+            iCLastFineVolumeslideDownParam,
+            iCLastExtraFinePortamentoUpParam,
+            iCLastExtraFinePortamentoDownParam,
+            iCLastFinePortamentoUpParam,
+            iCLastFinePortamentoDownParam,
+
+            iPatLoopRow,            // Pattern loop start row
+            iPatLoopCount,          // Pattern loop count
+        
+            iLastPortaParam,        // Last portamento effect parameter
+            iLastPortaToNoteParam,  // Last porta-to-note parameter
+            iLastPortaToNotePitch,  // Last porta-to-note pitch
+            lastPortaToNoteNote,
+
+            iLastTremorParam,
+            iTremorCount,
+            iLastSampleOffset,
+            iLastVolSlideParam,
+
+            lastMultiRetrigParamX,
+            lastMultiRetrigParamY,
+
+            lastSampleOffsetParam,
+            
+            iSaveNDFlags;           // For notedelay-effect
+} JSSPlayerChannel;
+
+
+// Struct holding all player related information
+typedef struct _JSSPlayer
+{
+    // General variables
+    int     tempo,              // Current values
+            speed,
+            tick,
+            order,
+            npattern,
+            row,
+            globalVol,
+            options;            // Playing option flags
+    BOOL    isPlaying;          // Are we playing?
+
+    int     newOrder,           // NEW order number
+            newRow;             // NEW row number
+    BOOL    newOrderSet,        // TRUE if new order has been set
+            newRowSet;          // TRUE if new row has been set
+
+    int     patternDelay,       // Pattern delay tick-counter
+            lastPatLoopRow;     // Latest set pattern loop row (any channel)
+
+    BOOL    jumpFlag,           // Pattern jump flag
+            breakFlag;          // Pattern break flag
+
+    // All channels for this player
+//    int     nchannels;
+    JSSPlayerChannel channels[jsetNChannels];
+
+
+    // Module and sounddevice specific
+    JSSModule *module;          // Current module in this player
+    JSSPattern *pattern;        // Current pattern
+    JSSMixer *device;           // Pointer to mixing device structure
+
+#ifdef JSS_SUP_THREADS
+    DMMutex *mutex;
+#endif
+
+} JSSPlayer;
+
+
+/* External functions for end users
+ */
+int             jmpPlayOrder(JSSPlayer *, int);
+int             jmpChangeOrder(JSSPlayer *, int);
+int             jmpPlayPattern(JSSPlayer *, int);
+void            jmpStop(JSSPlayer *);
+void            jmpResume(JSSPlayer *);
+void            jmpSetModule(JSSPlayer *, JSSModule *);
+JSSPlayer *     jmpInit(JSSMixer *);
+int             jmpClose(JSSPlayer *);
+void            jmpExec(void *, void *);
+
+
+/* Helper macros
+ */
+#define JMPMAKEPARAM(AIVAL, AVALX, AVALY) { AVALX = (((AIVAL) >> 4) & 0x0f); AVALY = ((AIVAL) & 0x0f); }
+#define JMPSETNDFLAGS(IVAL) chn->newDataFlags |= IVAL
+#define JMPUNSETNDFLAGS(IVAL) chn->newDataFlags &= (~(IVAL))
+#define JMPGETNOTE(MNOTE, MROW, MCHAN) assert(mp); assert(mp->pattern); assert((MROW) >= 0); assert((MROW) < mp->pattern->nrows); MNOTE = &mp->pattern->data[(mp->pattern->nchannels * MROW) + (MCHAN)]
+#define JMPGETEFFECT(MEFF, MIEFF) if ((MIEFF >= 0) && (MIEFF < jmpNMODEffectTable)) MEFF = jmpMODEffectTable[MIEFF]; else MEFF = 0
+#define JMPGETMODFLAGS(Q, Z) ((Q->module->defFlags & (Z)) == (Z))
+
+
+/* Debugging macros
+ */
+#if !defined(JSS_LIGHT) && defined(JSS_DEBUG)
+#  define JMPDEBUG(QQ) { fprintf(stderr, "[o=%03d:p=%03d:r=%03d] (%c/%x:%x) %s\n", mp->order, mp->npattern, mp->row, effect, currNote->effect, currNote->param, QQ ); }
+#else
+#  define JMPDEBUG(QQ) // stub
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // JSSPLR_H