Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 13568) +++ apps/metadata.c (working copy) @@ -1814,6 +1814,44 @@ return true; } +static bool get_mod_metadata(int fd, struct mp3entry* id3) +{ + /* Use the trackname part of the id3 structure as a temporary buffer */ + unsigned char buf[1084]; + int read_bytes; + char *p; + + + if ((lseek(fd, 0, SEEK_SET) < 0) + || ((read_bytes = read(fd, buf, sizeof(buf))) < 1084)) + { + return false; + } + + if ( (memcmp(&buf[1080], "M.K.", 4) != 0) && + (memcmp(&buf[1080], "6CHN", 4) != 0) && + (memcmp(&buf[1080], "8CHN", 4) != 0) && + (memcmp(&buf[1080], "FLT4", 4) != 0) ) + { + return false; + } + + p = id3->id3v2buf; + + /* Copy Title as artist */ + strcpy(p, &buf[0x00]); + id3->artist = p; + p += strlen(p)+1; + + id3->bitrate = filesize(fd)/1024; /* size in kb */ + id3->frequency = 44100; + id3->length = 120*1000; + id3->vbr = false; + id3->filesize = filesize(fd); + + return true; +} + /* PSID metadata info is available here: http://www.unusedino.de/ec64/technical/formats/sidplay.html */ static bool get_sid_metadata(int fd, struct mp3entry* id3) @@ -2362,6 +2400,14 @@ break; + case AFMT_MOD: + if (!get_mod_metadata(fd, &(track->id3))) + { + return false; + } + + break; + case AFMT_SHN: track->id3.vbr = true; track->id3.filesize = filesize(fd); Index: apps/filetypes.c =================================================================== --- apps/filetypes.c (revision 13568) +++ apps/filetypes.c (working copy) @@ -67,6 +67,7 @@ { "m4a", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "m4b", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mp4", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { "mod", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "shn", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "aif", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "aiff",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, Index: apps/codecs/Makefile =================================================================== --- apps/codecs/Makefile (revision 13568) +++ apps/codecs/Makefile (working copy) @@ -48,6 +48,7 @@ ifndef SIMVER $(BUILDDIR)/%.a : % $(CODECDEPS) +$(OBJDIR)/mod.elf : $(OBJDIR)/mod.o $(OBJDIR)/codec_crt0.o $(OBJDIR)/wav.elf : $(OBJDIR)/wav.o $(OBJDIR)/codec_crt0.o $(OBJDIR)/sid.elf : $(OBJDIR)/sid.o $(OBJDIR)/codec_crt0.o $(OBJDIR)/adx.elf : $(OBJDIR)/adx.o $(OBJDIR)/codec_crt0.o Index: apps/codecs/SOURCES =================================================================== --- apps/codecs/SOURCES (revision 13568) +++ apps/codecs/SOURCES (working copy) @@ -12,6 +12,7 @@ aac.c #endif ape.c +mod.c shorten.c aiff.c speex.c Index: apps/codecs/mod.c =================================================================== --- apps/codecs/mod.c (revision 0) +++ apps/codecs/mod.c (revision 0) @@ -0,0 +1,963 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * MOD Codec for rockbox + * + * Sequencer taken from Gravis Ultrasound open source mod player + * (forgot the name, if anyone finds out, please insert *g* ) + * + * Mixer written and ported to rockbox by Rainer Sinsch + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + + /************** + * This version is quite experimental and support modules up to 486kbyte, only! + * Source code is not cleaned and in a very untidy state + * Execution should however be efficient, works fine on h320 and ipod 5G + ******************************/ + +#include "debug.h" +#include "codeclib.h" +#include + +#include +#include +#include +#include + + +CODEC_HEADER + +#define CHUNK_SIZE (1024*2) + +/* This codec supports MOD Files: + * + */ + +static int32_t samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */ + +static unsigned char modfile[486*1024]; /* Static buffer for module */ +static signed char *sampleMem IDATA_ATTR; /* Pointer to the samples */ + +unsigned char mute[8] IDATA_ATTR; +signed char volume[8] IDATA_ATTR; // volume of channel + +int freq[8] IDATA_ATTR; // amiga frequency of each channel +signed char panval[8] IDATA_ATTR; +int midival[8] IDATA_ATTR; // midi value of channel +unsigned char lastins[8] IDATA_ATTR; // instrument # for each channel +int porto[8] IDATA_ATTR; // note to port to value +unsigned char portsp[8] IDATA_ATTR; // porta speed +unsigned char vibspe[8] IDATA_ATTR; // vibrato speed +unsigned char vibdep[8] IDATA_ATTR; // vibrato depth +unsigned char tremspe[8] IDATA_ATTR; // tremolo speed +unsigned char tremdep[8] IDATA_ATTR; // tremolo depth +unsigned char sinepos[8] IDATA_ATTR; // position in sine wave +unsigned char sineneg[8] IDATA_ATTR; // toggle to add or subtract sine value +unsigned char geffect[8] IDATA_ATTR; // effect played at the time (for interface) + +static int freqtab[296] ICONST_ATTR = { // The sorted amiga table. + 907,900,894,887,881,875,868,862, // Finetune -8 to -1 + 856,850,844,838,832,826,820,814, // C-1 to finetune +7 + 808,802,796,791,785,779,774,768, // C#1 to finetune +7 + 762,757,752,746,741,736,730,725, // D-1 to finetune +7 + 720,715,709,704,699,694,689,684, // D#1 to finetune +7 + 678,675,670,665,660,655,651,646, // E-1 to finetune +7 + 640,636,632,628,623,619,614,610, // F-1 to finetune +7 + 604,601,597,592,588,584,580,575, // F#1 to finetune +7 + 570,567,563,559,555,551,547,543, // G-1 to finetune +7 + 538,535,532,528,524,520,516,513, // G#1 to finetune +7 + 508,505,502,498,494,491,487,484, // A-1 to finetune +7 + 480,477,474,470,467,463,460,457, // A#1 to finetune +7 + 453,450,447,444,441,437,434,431, // B-1 to finetune +7 + 428,425,422,419,416,413,410,407, // C-2 to finetune +7 + 404,401,398,395,392,390,387,384, // C#2 to finetune +7 + 381,379,376,373,370,368,365,363, // D-2 to finetune +7 + 360,357,355,352,350,347,345,342, // D#2 to finetune +7 + 339,337,335,332,330,328,325,323, // E-2 to finetune +7 + 320,318,316,314,312,309,307,305, // F-2 to finetune +7 + 302,300,298,296,294,292,290,288, // F#2 to finetune +7 + 285,284,282,280,278,276,274,272, // G-2 to finetune +7 + 269,268,266,264,262,260,258,256, // G#2 to finetune +7 + 254,253,251,249,247,245,244,242, // A-2 to finetune +7 + 240,238,237,235,233,232,230,228, // A#2 to finetune +7 + 226,225,223,222,220,219,217,216, // B-2 to finetune +7 + 214,212,211,209,208,206,205,203, // C-3 to finetune +7 + 202,200,199,198,196,195,193,192, // C#3 to finetune +7 + 190,189,188,187,185,184,183,181, // D-3 to finetune +7 + 180,179,177,176,175,174,172,171, // D#3 to finetune +7 + 170,169,167,166,165,164,163,161, // E-3 to finetune +7 + 160,159,158,157,156,155,154,152, // F-3 to finetune +7 + 151,150,149,148,147,146,145,144, // F#3 to finetune +7 + 143,142,141,140,139,138,137,136, // G-3 to finetune +7 + 135,134,133,132,131,130,129,128, // G#3 to finetune +7 + 127,126,125,125,123,123,122,121, // A-3 to finetune +7 + 120,119,118,118,117,116,115,114, // A#3 to finetune +7 + 113,113,112,111,110,109,109,108, // B-3 to finetune +7 +}; + +// This table is for vibrato and contains half a sine wave. +static unsigned char sintab[32] ICONST_ATTR = { + 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 +}; + +int speed, bpm, channels IDATA_ATTR; // speed, bpm, channels, figure it out. +unsigned char patdelay IDATA_ATTR; // variable storing number of times to delay patn +unsigned char patlooprow, patloopno IDATA_ATTR; // pattern loop variables. +signed char row; +char mastervol IDATA_ATTR; // same here, current row, and master volume +int ord IDATA_ATTR; // current order being played +bool stop; // flag for stop condition +bool target[128]; // keep track positions have been targeted by loop Effect +static unsigned char long_buf[23*31+1]; // buffer for scroll text 714 +static unsigned char short_buf[5]; // buffer for CHANNEL magic bytes + +// SAMPLE STRUCT +typedef struct { + char name[23]; // instrument name + unsigned short length; // sample length + signed char finetune; // sample finetune value + unsigned char volume; // sample default volume + unsigned short loopstart; // sample loop start + unsigned short loopend; // sample loop length + unsigned int offset; // offset of sample in dram +} Sample; + +// SONG STRUCT +struct { + char name[20]; // song name + Sample inst[31]; // instrument headers + unsigned char songLength; // song length + unsigned char numpats; // number of physical patterns + unsigned char order[128]; // pattern playing orders +} MOD; + +typedef struct { + unsigned char number; // sample being played 5 bits + int period; // frequency being played at + 11 bits = 16. + unsigned char effect; // effect number + 8 + unsigned char eparm; // effect parameter + 8 = 4 bytes! +} Note; +Note *current IDATA_ATTR; + +static char *patbuff IDATA_ATTR; // buffer that holds our pattern data +static unsigned int offset IDATA_ATTR; // offset of note in pattern data buffer. + +static const int iMixingRate = 44100; + +static int iBalance[8] IDATA_ATTR; // Balance +static int iFreq[8] IDATA_ATTR; // Frequency +static int iSamplePos[8] IDATA_ATTR; // Position des Samplezeigers +static int iSampleFractPos[8] IDATA_ATTR; // Fractal position +static int iLoopStart[8] IDATA_ATTR; +static int iLoopEnd[8] IDATA_ATTR; +static int bPlayVoice[8] IDATA_ATTR; +static int bLoopVoice[8] IDATA_ATTR; + +static int tick IDATA_ATTR; +static int iSamplesPerTick IDATA_ATTR; // Samples Per Tick + + +static inline void SetVolume(unsigned char iChannel, unsigned char iVolume) +{ + volume[iChannel] = iVolume; +} +static inline void SetBalance(int iChannel, int iVal) +{ + iBalance[iChannel] = iVal; +} +static inline void SetFreq(int iChannel, int iVal) +{ + iFreq[iChannel] = 3579546.471f/iVal; +} +static inline void PlayVoice(unsigned char iChannel, unsigned char iMode , unsigned int iBegin, unsigned int iStart, unsigned int iEnd) +{ + bPlayVoice[iChannel] = 1; + iSamplePos[iChannel] = iBegin; + iSampleFractPos[iChannel] = 0; + iLoopEnd[iChannel] = iEnd; + // Loopen? + if (iMode) { + bLoopVoice[iChannel] = 1; + iLoopStart[iChannel] = iStart; + } + else bLoopVoice[iChannel] = false; +} + +static inline void StopVoice(unsigned char iChannel) +{ + bPlayVoice[iChannel] = false; +} + +STATICIRAM void UpdateNote(void) ICODE_ATTR; +STATICIRAM void UpdateNote(void) { + unsigned char track, sample, eparmx, eparmy; + unsigned short soff; + int period; + + // calculate where in the pattern buffer we should be according to + // the pattern number and row. + offset = channels*64*sizeof(Note)*MOD.order[ord]+(sizeof(Note)*row*channels); + + // new row? now we loop through each channel until we have finished + for (track=0; track period; // get period + sample = current -> number; // get instrument # + eparmx = current -> eparm >> 4; // get effect param x + eparmy = current -> eparm & 0xF; // get effect param y + + // for the interface, the next 3 lines just store the effect # to + // remember for later + geffect[track] = current -> effect; + if (geffect[track] == 0xE) geffect[track]+=2+eparmx; + if (current->effect == 0 && current -> eparm > 0) geffect[track] = 32; + + if (sample > 0) { // if instrument > 0 set volume + lastins[track] = sample - 1; // remember the sample # + volume[track] = MOD.inst[sample-1].volume; // set chan's vol + SetVolume(track, volume[track]*mastervol/64); + } + if (period >= 0) { // if period >= 0 set freq + midival[track] = period; // remember the note + // if not a porta effect, then set the channels frequency to the + // looked up amiga value + or - any finetune + if (current->effect != 0x3 && current->effect != 0x5) freq[track]= + freqtab[midival[track]+MOD.inst[lastins[track]].finetune]; + } + soff = 0; // sample offset = nothing now + + switch (current -> effect) { + case 0x0: break; // dont waste my time in here!!! + // 0x1: not processed on tick 0 + // 0x2: not processed on tick 0 + case 0x3: + case 0x5: porto[track] = freqtab[midival[track]+MOD.inst[lastins[track]].finetune]; + if (current -> eparm > 0 && current->effect == 0x3) + portsp[track] = current -> eparm; + period = -1; + break; + case 0x4: if (eparmx > 0) vibspe[track] = eparmx; + if (eparmy > 0) vibdep[track] = eparmy; + break; + // 0x6: not processed on tick 0 + case 0x7: if (eparmx > 0) tremspe[track] = eparmx; + if (eparmy > 0) tremdep[track] = eparmy; + break; + case 0x8: if (current -> eparm == 0xa4) panval[track]=7; + else panval[track] = (current -> eparm >> 3)-1; + if (panval[track] < 0) panval[track] = 0; + SetBalance(track, panval[track]); + break; + case 0x9: soff = current -> eparm << 8; + if (soff > MOD.inst[lastins[track]].length) soff = + MOD.inst[lastins[track]].length; + break; + // 0xA: processed in UpdateEffect() (tick based) + case 0xB: ord = current->eparm; + row = 0; + if (ord >= MOD.songLength) { ord=0; stop=true; } + if (target[ord]) stop=true; else target[ord]=true; + break; + case 0xC: volume[track] = current -> eparm; + if (volume[track] < 0) volume[track] = 0; + if (volume[track] > 64) volume[track] = 64; + SetVolume(track, volume[track]*mastervol/64); + break; + case 0xD: row = eparmx*10 + eparmy -1; + if (row > 63) row =0; + ord++; + if (ord >= MOD.songLength) { ord=0; stop=true; } + break; + case 0xF: if (current->eparm < 0x20) speed = current->eparm; + else bpm = current->eparm; + break; + case 0xE: switch (eparmx) { + case 0x1: freq[track] -= eparmy; + break; + case 0x2: freq[track] += eparmy; + break; + // 0x3: not supported (glissando) + // 0x4: not supported (set vibrato waveform) + case 0x5: MOD.inst[sample-1].finetune = eparmy; + if (MOD.inst[sample-1].finetune > 7) + MOD.inst[sample-1].finetune -= 16; + break; + case 0x6: if (eparmy == 0) patlooprow = row; + else { + if (patloopno == 0) patloopno=eparmy; + else patloopno--; + if (patloopno > 0) row = patlooprow-1; + } + break; + // 0x6: not supported (set tremolo waveform) + case 0x8: panval[track] = eparmy; + SetBalance(track, eparmy); + break; + // 0x9: not processed on tick 0 + case 0xA: volume[track] += eparmy; + if (volume[track] > 64) volume[track]=64; + SetVolume(track, volume[track]*mastervol/64); + break; + case 0xB: volume[track] -= eparmy; + if (volume[track] < 0) volume[track]=0; + SetVolume(track, volume[track]*mastervol/64); + break; + // 0xC: not processed on tick 0 + case 0xD: period = -1; + break; + case 0xE: patdelay = eparmy; + break; + // 0xF: not supported (Invert loop) + }; + break; + }; + + // set the frequency on the GUS for the voice, as long as its > 0 + if (freq[track] > 0) SetFreq(track, freq[track]); + // only play the note if there is a note or there is an effect 9xy + if (period >= 0 || soff > 0x00FF) { + sinepos[track]=0; // retrig waveform + sineneg[track]=0; + // if sample has a loop then loop it. + if (MOD.inst[lastins[track]].loopend > 2) PlayVoice(track, 8, + MOD.inst[lastins[track]].offset+soff, + MOD.inst[lastins[track]].offset+MOD.inst[lastins[track]].loopstart, + MOD.inst[lastins[track]].offset+MOD.inst[lastins[track]].loopend); + // else just play the sample straight and no loop. + else PlayVoice(track, 0, + MOD.inst[lastins[track]].offset+soff, + MOD.inst[lastins[track]].offset, + MOD.inst[lastins[track]].offset+MOD.inst[lastins[track]].length); + } + offset += sizeof(Note); // increment our note pointer. + } +} + + +/**************************************************************************** + * Name : doporta * + * Purpose : to carry out a tone portamento to a certain note * + * Passed : - * + * Returns : - * + * Locals : - * + ****************************************************************************/ +static inline void doporta(unsigned char track) { + if (freq[track] < porto[track]) { + freq[track] += portsp[track]; + if (freq[track] > porto[track]) freq[track]=porto[track]; + } + if (freq[track] > porto[track]) { + freq[track] -= portsp[track]; + if (freq[track] < 1) freq[track]=1; + if (freq[track] < porto[track]) freq[track]=porto[track]; + } + SetFreq(track, freq[track]); +} + + +/**************************************************************************** + * Name : dovibrato * + * Purpose : to carry out a vibrato at a certain depth and speed * + * Passed : - * + * Returns : - * + * Locals : int vib - size of delta to add or subtract from period * + ****************************************************************************/ +static inline void dovibrato(unsigned char track) { + int vib; + + vib = vibdep[track]*sintab[sinepos[track]] >> 7; // div 128 + if (sineneg[track] == 0) SetFreq(track, freq[track]+vib); + else SetFreq(track, freq[track]-vib); + + sinepos[track]+=vibspe[track]; + if (sinepos[track] > 31) { + sinepos[track] -=32; + sineneg[track] = ~sineneg[track]; // flip pos/neg flag + } +} + + +/**************************************************************************** + * Name : dotremolo * + * Purpose : to carry out a tremolo at a certain depth and speed * + * Passed : - * + * Returns : - * + * Locals : int vib - size of delta to add or subtract from volume * + ****************************************************************************/ +static inline void dotremolo(unsigned char track) { + int vib; + + vib = tremdep[track]*sintab[sinepos[track]] >> 6; // div64 + if (sineneg[track] == 0) { + if (volume[track]+vib > 64) vib = 64-volume[track]; + SetVolume(track, (volume[track]+vib)*mastervol/64); + } + else { + if (volume[track]-vib < 0) vib = volume[track]; + SetVolume(track, (volume[track]-vib)*mastervol/64); + } + + sinepos[track]+= tremspe[track]; + if (sinepos[track] > 31) { + sinepos[track] -=32; + sineneg[track] = ~sineneg[track]; // flip pos/neg flag + } +} + + +/**************************************************************************** + * Name : UpdateEffect * + * Purpose : To update any tick based effects after tick 0 * + * Passed : int tick - the actual tick number. * + * Returns : - * + * Locals : int track - the number of the column/track or channel we are in* + * int vib - the delta which to vibrate from the frequency with * + * byte effect - a temp variable to get the effect number wanted * + * byte eparmx - a term variable to get the effect parameter x * + * byte eparmy - a term variable to get the effect parameter y * + * static byte arpcount - a counter to tell us what to do with * + * arpeggio (we dont want to forget it too, so its static) * + * Note *current - a temporary note structure to hold the pointed * + * to note in the pattern buffer * + * Note : To see explanations of effects check out the howto document * + ****************************************************************************/ +STATICIRAM void UpdateEffect(int tick) ICODE_ATTR; +STATICIRAM void UpdateEffect(int tick) { + unsigned char track, effect, eparmx, eparmy; + + if (row < 1) return; // if at row 0 nothing to update + offset -= (sizeof(Note) * channels); // go back 4 bytes in buffer + // becuase update note did +4 + for (track=0; track effect; // grab the effect number + eparmx = current -> eparm >> 4; // grab the effect parameter x + eparmy = current -> eparm & 0xF; // grab the effect parameter y + + if (freq[track] == 0) goto skip; + + switch(effect) { + case 0x0: if (current -> eparm > 0) { + switch (tick%3) { + case 0: SetFreq(track, freq[track]); + break; + case 1: SetFreq(track, freqtab[midival[track]+(8*eparmx)+MOD.inst[lastins[track]].finetune]); + break; + case 2: SetFreq(track, freqtab[midival[track]+(8*eparmy)+MOD.inst[lastins[track]].finetune]); + break; + }; + } + break; + + case 0x3: doporta(track); + break; + + case 0x4: dovibrato(track); + break; + + case 0x1: freq[track]-= current -> eparm; // subtract freq + SetFreq(track, freq[track]); + if (freq[track] < 54) freq[track]=54; // stop at C-5 + break; + + case 0x2: freq[track]+= current -> eparm; + SetFreq(track, freq[track]); + break; + + + case 0x5: doporta(track); + volume[track] += eparmx - eparmy; + if (volume[track] < 0) volume[track] = 0; + if (volume[track] > 64) volume[track] = 64; + SetVolume(track, volume[track]*mastervol/64); + break; + + case 0x6: dovibrato(track); + volume[track] += eparmx - eparmy; + if (volume[track] < 0) volume[track] = 0; + if (volume[track] > 64) volume[track] = 64; + SetVolume(track, volume[track]*mastervol/64); + break; + + case 0x7: dotremolo(track); + break; + + case 0xA: volume[track] += eparmx - eparmy; + if (volume[track] < 0) volume[track] = 0; + if (volume[track] > 64) volume[track] = 64; + SetVolume(track, volume[track]*mastervol/64); + break; + case 0xE: switch(eparmx) { + case 0xC: if (eparmy == tick) { + volume[track] = 0; + SetVolume(track, volume[track]); + } + break; + + case 0x9: if (tick % eparmy == 0) { + if (MOD.inst[lastins[track]].loopend > 2) + PlayVoice(track, 8, MOD.inst[lastins[track]].offset, + MOD.inst[lastins[track]].offset+ + MOD.inst[lastins[track]].loopstart, + MOD.inst[lastins[track]].offset+ + MOD.inst[lastins[track]].loopend); + else PlayVoice(track, 0, MOD.inst[lastins[track]].offset, + MOD.inst[lastins[track]].offset, + MOD.inst[lastins[track]].offset+ + MOD.inst[lastins[track]].length); + } + break; + case 0xD: if (eparmy==tick) { + if (MOD.inst[lastins[track]].loopend > 2) + PlayVoice(track, 8, MOD.inst[lastins[track]].offset, + MOD.inst[lastins[track]].offset+ + MOD.inst[lastins[track]].loopstart, + MOD.inst[lastins[track]].offset+ + MOD.inst[lastins[track]].loopend); + else PlayVoice(track, 0, MOD.inst[lastins[track]].offset, + MOD.inst[lastins[track]].offset, + MOD.inst[lastins[track]].offset+ + MOD.inst[lastins[track]].length); + } + break; + }; + break; + }; +skip: + offset+=sizeof(Note); // increment our buffer to next note + } +} + +STATICIRAM int LoadMODFromMem(void *pMod) ICODE_ATTR; +STATICIRAM int LoadMODFromMem(void *pMod) +{ + unsigned char *part, *lbp, *ch; + unsigned char *pbMod = (unsigned char*)pMod; + int count=0, count2=0, pattcount, period, i; + unsigned int gusoff, size; + + + //********************** + //*** VERIFICATION *** + //********************** + part = (unsigned char*)(pbMod+1080); + + ci->memcpy(short_buf, part, 4); + short_buf[4] = '\0'; + // check what MOD format + if (ci->strcmp((char*)short_buf, "M.K.") == 0) channels = 4; + else if (ci->strcmp((char*)short_buf, "6CHN") == 0) channels = 6; + else if (ci->strcmp((char*)short_buf, "8CHN") == 0) channels = 8; + else if (ci->strcmp((char*)short_buf, "FLT4") == 0) channels = 4; + else return(false); // not a recognized format + + ci->id3->genre_string = short_buf; + + //fseek(handle, 20, SEEK_SET); + part = (unsigned char*)(pbMod+20); + + lbp = long_buf; + + + + /*********************************/ + /*** LOAD SAMPLE INFORMATION ***/ + /*********************************/ + for (count=0; count<31; count++) { // go through 31 instruments. + // Sample Name + //fread(MOD.inst[count].name, 22, 1, handle); + + // transfer instrument name to buffer + ci->memcpy(short_buf, part, 3); + short_buf[3] = '\0'; + if(ci->strcmp(short_buf,"st-")) { + ch=part; + for(i=0; (i<22) && (*ch!=0); i++,ch++) { + if((*ch==' ') && ((*(lbp-1) ==' ') || (lbp==long_buf))) continue; + if((*ch>0x1f) && (*ch<0x80) && (*ch!='#')) *lbp++ = *ch; + } + if(*(lbp-1)!=' ') *lbp++ = ' '; + *lbp = 0; + + } + + memcpy(MOD.inst[count].name, part, 22); + part+= 22; + + //part[0] = fgetc(handle); // get samples length + //part[1] = fgetc(handle); + MOD.inst[count].length = ((part[0] * 0x100) + part[1]) * 2; + part+=2; + + //MOD.inst[count].finetune = fgetc(handle);// get finetune + MOD.inst[count].finetune = *part++; // get finetune + + if (MOD.inst[count].finetune > 7) + MOD.inst[count].finetune -= 16; + + //MOD.inst[count].volume = fgetc(handle); // get sample default volume + MOD.inst[count].volume = *part++; // get sample default volume + + // get sample loop start + //MOD.inst[count].loopstart = ((fgetc(handle) << 8)+fgetc(handle))*2; + //MOD.inst[count].loopstart = ((*part++<<8) + *part++)*2; + MOD.inst[count].loopstart = *part++<<8; + MOD.inst[count].loopstart|= *part++; + MOD.inst[count].loopstart<<=1; + + // get sample loop end + //MOD.inst[count].loopend = ((fgetc(handle) << 8)+fgetc(handle))*2 + MOD.inst[count].loopstart; + //MOD.inst[count].loopend = ((*part++<<8)+ *part++)*2 + MOD.inst[count].loopstart; + MOD.inst[count].loopend = *part++<<8; + MOD.inst[count].loopend|= *part++; + MOD.inst[count].loopend<<=1; + MOD.inst[count].loopend+= MOD.inst[count].loopstart; + + + // incase the loopend is past the end of the sample fix it + if (MOD.inst[count].loopend > MOD.inst[count].length) + MOD.inst[count].loopend = MOD.inst[count].length; + } + + long_buf[MAX_PATH] = 0; // unfortunate limit of the scroll thread + + /**************************************/ + /*** LOAD ORDER AND SIGNATURE DATA ***/ + /**************************************/ + MOD.songLength = *part++; // get number of orders. + + if(ci->taginfo_ready) { + ci->id3->length = MOD.songLength*1000; + ci->id3->title = long_buf; + } + + part++; // unused byte, skip it + + MOD.numpats = 0; // highest pattern is now 0 + for (count=0; count<128; count++) { + MOD.order[count] = *part++; // get 128 orders. + if (MOD.order[count] > MOD.numpats) + MOD.numpats = MOD.order[count]; // get highest pattern. + } + size = channels*sizeof(Note)*64*(MOD.numpats+1); // calculate buffer size + // try and allocate memory + //if (patbuff) free(patbuff); + if ((patbuff = (char *)codec_malloc(size)) == NULL) return(false); + + // at 1080 again, skip it + part = (unsigned char*) (pbMod + 1084); + + offset =0; // set buffer offset to 0 + + /**************************/ + /*** LOAD PATTERN DATA ***/ + /**************************/ + for (pattcount=0; pattcount <= MOD.numpats; pattcount++) { + for (row=0; row<64; row++) { // loop down through 64 notes. + for (count=0; count number = ((part[0] & 0xF0) + (part[2] >> 4)); + + // get period + period = ((part[0] & 0xF) << 8) + part[1]; + + // do the look up in the table against what is read in. + // store note (in midi style format) + current -> period = -1; + for (count2=1;count2<37; count2++) { + if (period > freqtab[count2*8]-3 && + period < freqtab[count2*8]+3 ) + current -> period = count2*8; + } + + // store effects and arguments + current -> effect = part[2] & 0xF; // Effect + current -> eparm = part[3]; // parameter + + offset += sizeof(Note); // increment buffer pos + part += 4; + } + } + } + /*************************/ + /*** LOAD SAMPLE DATA ***/ + /*************************/ + gusoff =0; + sampleMem = (signed char*) part; + + for (count = 0; count <31; count++) { + MOD.inst[count].offset = gusoff; // get offsets in GUS memory + gusoff += MOD.inst[count].length; + } + + tick = speed-1; + + /* Panning values = { 4,12,12, 4, 4,12,12, 4 } */ + panval[0] = panval[3] = panval[4] = panval[7] = 4; + panval[1] = panval[2] = panval[5] = panval[6] = 12; + + for (count=0; count 32767) return(32767); + else if (i < -32767) return(-32767); + else return(i); +} + +STATICIRAM void SynthRender(void *pRenderBuffer, int iSampleCount) ICODE_ATTR; +STATICIRAM void SynthRender(void *pRenderBuffer, int iSampleCount) +{ + // 125bpm entspricht 50Hz (= 0.02s) + // => ein tick = MixingRate/50, bzw. + // samples die fr einen tick vergehen: MixingRate/(bpm/2.5) = 2.5*MixingRate/bpm + + //static int tick = speed-1; + signed int *pLeft = (signed int *) pRenderBuffer; + signed int *pRight = pLeft+1; + signed short s; + int qfDistance, qfDistance2; + + int i; + + int c, Left, Right; + + for (i=0;i= speed) { + tick = 0; // set the tick to nothing + if (row == 64) { // if end of pattn (64) + ord++; // next order + if (ord >= MOD.songLength) { ord=0; stop=true; } // if end goto 1st order + row = 0; // start at top of pattn + } + + if (patdelay == 0) { // if there is no pat delay + UpdateNote(); // Update and play the note + row++; // increment the row + + /* + for (int count=0;count<4;count++) + printf("I%02d V%02d N%3s ", + lastins[count]+1, volume[count], notetab[midival[count]/8]); + + printf("\n"); + */ + + } + else patdelay --; // else decrement pat delay + } + else + UpdateEffect(tick); // Else update the effects + + //iSamplesPerTick = (int) (2.5f*(float)iMixingRate/(float)bpm); + iSamplesPerTick = (20*iMixingRate/bpm)>>3; + } + // ab hier Puffer mischen + // Alle Kan�e durchlaufen + Left=0, Right=0; + static int iLastSample[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + for (c=0;c= iLoopEnd[c]) + { + if (bLoopVoice[c]) + iSamplePos[c] -= (iLoopEnd[c]-iLoopStart[c]); + else bPlayVoice[c] = false; + } + + if (bPlayVoice[c]) + { + s = (signed short)(sampleMem[iSamplePos[c]])*volume[c]; + + // Wenn die Samplefrequenz niedriger als die Mixingfrequenz ist: Interpolien + if (iFreq[c] < iMixingRate) + { + // Lineare Interpolation + //fDistance = (float)iSampleFractPos[c]/iMixingRate; + //fDistance2 = 1-fDistance; + + qfDistance = iSampleFractPos[c]<<16 / iMixingRate; + qfDistance2 = (1<<16)-qfDistance; + + //s = (int)(fDistance*s + fDistance2*iLastSample[c]); + s = (qfDistance*s + qfDistance2*iLastSample[c])>>16; + } + + // Sample bernehmen + Left += s*(16-iBalance[c])>>2; + Right += s*iBalance[c]>>2; + + // Sample weiter rcken + iSampleFractPos[c] += iFreq[c]; + while (iSampleFractPos[c] > iMixingRate) + { + iSampleFractPos[c] -= iMixingRate; + iSamplePos[c]++; + + // Zuletzt gespieltes Sample retten (zwecks Interpolation) + iLastSample[c] = s; + } + + } + } + } + *pLeft = Clip(Left)<<13; + *pRight = Clip(Right)<<13; + pLeft+=2; + pRight+=2; + } +} + +enum codec_status codec_main(void) +{ + size_t n, bytesfree; + unsigned char *p; + unsigned int filesize; + int old_ord; + + int bytesdone; + + ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512); + ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, 1024*256); + +next_track: + if (codec_init()) { + return CODEC_ERROR; + } + + while (!*ci->taginfo_ready) + ci->yield(); + + codec_set_replaygain(ci->id3); + + /* Load MOD file */ + p = modfile; + bytesfree=sizeof(modfile); + while ((n = ci->read_filebuf(p, bytesfree)) > 0) { + p += n; + bytesfree -= n; + } + filesize = p-modfile; + + if (filesize == 0) + return CODEC_ERROR; + + LoadMODFromMem(modfile); + + /* Make use of 44.1khz */ + ci->configure(DSP_SET_FREQUENCY, 44100); + /* Sample depth is 28 bit host endian */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 28); + /* Stereo output */ + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + + /* The main decoder loop */ + ci->set_elapsed(0); + bytesdone = 0; + old_ord = 0; + + while (1) { + ci->yield(); + if (ci->stop_codec || ci->new_track || stop) + break; + + if (ci->seek_time) { + /* New time is ready in ci->seek_time */ + ord = ci->seek_time/1000; + row = 0; + ci->seek_complete(); + } + + if(old_ord!=ord) { + ci->set_elapsed(ord*1000+500); + old_ord=ord; + } + + SynthRender(samples, CHUNK_SIZE/2); + + bytesdone += CHUNK_SIZE; + + ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE/2); + + } + + if (ci->request_next_track()) + goto next_track; + + return CODEC_OK; +} Index: firmware/export/id3.h =================================================================== --- firmware/export/id3.h (revision 13568) +++ firmware/export/id3.h (working copy) @@ -47,6 +47,7 @@ AFMT_WAVPACK, /* WavPack */ AFMT_ALAC, /* Apple Lossless Audio Codec */ AFMT_AAC, /* Advanced Audio Coding (AAC) in M4A container */ + AFMT_MOD, /* MOD File Format */ AFMT_SHN, /* Shorten */ AFMT_SID, /* SID File Format */ AFMT_ADX, /* ADX File Format */ Index: firmware/id3.c =================================================================== --- firmware/id3.c (revision 13568) +++ firmware/id3.c (working copy) @@ -90,6 +90,9 @@ [AFMT_AAC] = AFMT_ENTRY("AAC", "aac", NULL, "mp4\0" ), /* Shorten */ + [AFMT_MOD] = + AFMT_ENTRY("MOD", "mod", NULL, "mod\0" ), + /* Shorten */ [AFMT_SHN] = AFMT_ENTRY("SHN", "shorten", NULL, "shn\0" ), /* SID File Format */