# HG changeset patch # User Matti Hamalainen # Date 1366086749 -10800 # Node ID c430112449a7881661f5fd8988808f56e95d9d8f # Parent 7e25ec0bbf59b3ade7a6f0b22fee7a6ef17a04e6 Move miniJSS into a subdirectory. diff -r 7e25ec0bbf59 -r c430112449a7 Makefile.gen --- 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) diff -r 7e25ec0bbf59 -r c430112449a7 jloadjss.c --- 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 - - -#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; -} diff -r 7e25ec0bbf59 -r c430112449a7 jloadxm.c --- 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 - - -/* 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; -} diff -r 7e25ec0bbf59 -r c430112449a7 jmix_c_in.c --- 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" diff -r 7e25ec0bbf59 -r c430112449a7 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 diff -r 7e25ec0bbf59 -r c430112449a7 jmixtmpl_c.h --- 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 - diff -r 7e25ec0bbf59 -r c430112449a7 jss.c --- 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 - - -/* 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; -} diff -r 7e25ec0bbf59 -r c430112449a7 jss.h --- 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 diff -r 7e25ec0bbf59 -r c430112449a7 jssmix.c --- 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 - - -#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; -} diff -r 7e25ec0bbf59 -r c430112449a7 jssmix.h --- 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 diff -r 7e25ec0bbf59 -r c430112449a7 jssmod.c --- 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 - - -#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; -} diff -r 7e25ec0bbf59 -r c430112449a7 jssmod.h --- 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 diff -r 7e25ec0bbf59 -r c430112449a7 jssplr.c --- 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 - -/* 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); -} diff -r 7e25ec0bbf59 -r c430112449a7 jssplr.h --- 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 diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jloadjss.c --- /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 + + +#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; +} diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jloadxm.c --- /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 + + +/* 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; +} diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jmix_c_in.c --- /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" diff -r 7e25ec0bbf59 -r c430112449a7 minijss/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 diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jmixtmpl_c.h --- /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 + diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jss.c --- /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 + + +/* 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; +} diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jss.h --- /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 diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jssmix.c --- /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 + + +#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; +} diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jssmix.h --- /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 diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jssmod.c --- /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 + + +#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; +} diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jssmod.h --- /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 diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jssplr.c --- /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 + +/* 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); +} diff -r 7e25ec0bbf59 -r c430112449a7 minijss/jssplr.h --- /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