/* Name: MUNITRK.C Description: All routines dealing with the manipulation of UNITRK(tm) streams Portability: All systems - all compilers */ package audio.jmikmod.MikMod.MUniTrk; import audio.jmikmod.MikMod.*; public class clMUniTrk extends Object { public audio.jmikmod.MikMod.clMainBase m_; /* all known effects: */ public static final short UNI_NOTE=1; public static final short UNI_INSTRUMENT=2; public static final short UNI_PTEFFECT0=3; public static final short UNI_PTEFFECT1=4; public static final short UNI_PTEFFECT2=5; public static final short UNI_PTEFFECT3=6; public static final short UNI_PTEFFECT4=7; public static final short UNI_PTEFFECT5=8; public static final short UNI_PTEFFECT6=9; public static final short UNI_PTEFFECT7=10; public static final short UNI_PTEFFECT8=11; public static final short UNI_PTEFFECT9=12; public static final short UNI_PTEFFECTA=13; public static final short UNI_PTEFFECTB=14; public static final short UNI_PTEFFECTC=15; public static final short UNI_PTEFFECTD=16; public static final short UNI_PTEFFECTE=17; public static final short UNI_PTEFFECTF=18; public static final short UNI_S3MEFFECTA=19; public static final short UNI_S3MEFFECTD=20; public static final short UNI_S3MEFFECTE=21; public static final short UNI_S3MEFFECTF=22; public static final short UNI_S3MEFFECTI=23; public static final short UNI_S3MEFFECTQ=24; public static final short UNI_S3MEFFECTT=25; public static final short UNI_XMEFFECTA=26; public static final short UNI_XMEFFECTG=27; public static final short UNI_XMEFFECTH=28; public static final short UNI_XMEFFECTP=29; /* Unimod flags */ public static final int UF_XMPERIODS = 1; /* if set use XM periods/finetuning */ public static final int UF_LINEAR = 2; /* if set use LINEAR periods */ public short [] rowstart; /* startadress of a row */ public int rowend; //short *rowend; /* endaddress of a row (exclusive) */ public int rowpc; //short *rowpc; /* current unimod(tm) programcounter */ public short [] unibuf; /* pointer to the temporary unitrk buffer */ public int unimax; /* maximum number of bytes to be written to this buffer */ public int unipc; /* index in the buffer where next opcode will be written */ public int unitt; /* holds index of the rep/len byte of a row */ public int lastp; /* holds index to the previous row (needed for compressing) */ protected static final int BUFPAGE = 128; /* smallest unibuffer size */ protected static final int TRESHOLD = 16; /* unibuffer is increased by BUFPAGE bytes when unipc reaches unimax-TRESHOLD */ /* Ok.. I'll try to explain the new internal module format.. so here it goes: The UNITRK(tm) Format: ====================== A UNITRK stream is an array of bytes representing a single track of a pattern. It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly language): rrrlllll [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND].. ^ ^ ^ |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track... The rep/len byte contains the number of bytes in the current row, _including_ the length byte itself (So the LENGTH byte of row 0 in the previous example would have a value of 5). This makes it easy to search through a stream for a particular row. A track is concluded by a 0-value length byte. The upper 3 bits of the rep/len byte contain the number of times -1 this row is repeated for this track. (so a value of 7 means this row is repeated 8 times) Opcodes can range from 1 to 255 but currently only opcodes 1 to 19 are being used. Each opcode can have a different number of operands. You can find the number of operands to a particular opcode by using the opcode as an index into the 'unioperands' table. */ //protected short unioperands[256]={ protected short unioperands[]={ 0, /* not used */ 1, /* UNI_NOTE */ 1, /* UNI_INSTRUMENT */ 1, /* UNI_PTEFFECT0 */ 1, /* UNI_PTEFFECT1 */ 1, /* UNI_PTEFFECT2 */ 1, /* UNI_PTEFFECT3 */ 1, /* UNI_PTEFFECT4 */ 1, /* UNI_PTEFFECT5 */ 1, /* UNI_PTEFFECT6 */ 1, /* UNI_PTEFFECT7 */ 1, /* UNI_PTEFFECT8 */ 1, /* UNI_PTEFFECT9 */ 1, /* UNI_PTEFFECTA */ 1, /* UNI_PTEFFECTB */ 1, /* UNI_PTEFFECTC */ 1, /* UNI_PTEFFECTD */ 1, /* UNI_PTEFFECTE */ 1, /* UNI_PTEFFECTF */ 1, /* UNI_S3MEFFECTA */ 1, /* UNI_S3MEFFECTD */ 1, /* UNI_S3MEFFECTE */ 1, /* UNI_S3MEFFECTF */ 1, /* UNI_S3MEFFECTI */ 1, /* UNI_S3MEFFECTQ */ 1, /* UNI_S3MEFFECTT */ 1, /* UNI_XMEFFECTA */ 1, /* UNI_XMEFFECTG */ 1, /* UNI_XMEFFECTH */ 1 /* UNI_XMEFFECTP */ }; /*************************************************************************** >>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<< ***************************************************************************/ public clMUniTrk(clMainBase theMain) { m_ = theMain; rowstart = null; rowend = 0; rowpc = 0; unibuf = null; unimax = 0; unipc = 0; unitt = 0; lastp = 0; } public void UniSetRow(short [] t, int start_at) { rowstart=t; rowpc = start_at; //rowpc=rowstart; rowend = rowpc+(rowstart[rowpc++]&0x1f); //rowend=rowstart+(*(rowpc++)&0x1f); } public short UniGetByte() { //return (rowpc<rowend) ? *(rowpc++) : 0; return (rowpc<rowend) ? rowstart[rowpc++] : 0; } public void UniSkipOpcode(short op) { short t=unioperands[op]; while((t--)!=0) UniGetByte(); } public int UniFindRow(short [] t,int row) /* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns NULL if the row can't be found. */ { short c,l; int tp=0; while(true){ c=t[tp]; /* get rep/len byte */ if(c == 0) return -1; /* zero ? -> end of track.. */ l=(short)((c>>5)+1); /* extract repeat value */ if(l>row) break; /* reached wanted row? -> return pointer */ row-=l; /* havn't reached row yet.. update row */ tp+=c&0x1f; /* point t to the next row */ } return tp; } /*************************************************************************** >>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<< ***************************************************************************/ public void UniReset() /* Resets index-pointers to create a new track. */ { unitt=0; /* reset index to rep/len byte */ unipc=1; /* first opcode will be written to index 1 */ lastp=0; /* no previous row yet */ unibuf[0]=0; /* clear rep/len byte */ } public void UniWrite(short data) /* Appends one byte of data to the current row of a track. */ { /* write byte to current position and update */ data &= 0xFF; unibuf[unipc++]=data; /* Check if we've reached the end of the buffer */ if(unipc>(unimax-TRESHOLD)){ short newbuf[]; /* We've reached the end of the buffer, so expand the buffer by BUFPAGE bytes */ // newbuf=(short *)realloc(unibuf,(unimax+BUFPAGE)*2); newbuf = new short[unimax+BUFPAGE]; /* Check if realloc succeeded */ if(newbuf!=null){ int i; for (i=0; i<unimax; i++) newbuf[i] = unibuf[i]; //delete [] unibuf; unibuf = null; unibuf=newbuf; unimax+=BUFPAGE; } else{ /* realloc failed, so decrease unipc so we won't write beyond the end of the buffer.. I don't report the out-of-memory here; the UniDup() will fail anyway so that's where the loader sees that something went wrong */ unipc--; } } } public void UniInstrument(short ins) /* Appends UNI_INSTRUMENT opcode to the unitrk stream. */ { UniWrite(UNI_INSTRUMENT); UniWrite(ins); } public void UniNote(short note) /* Appends UNI_NOTE opcode to the unitrk stream. */ { UniWrite(UNI_NOTE); UniWrite(note); } public void UniPTEffect(short eff,short dat) /* Appends UNI_PTEFFECTX opcode to the unitrk stream. */ { eff &= 0xFF; dat &= 0xFF; if(eff!=0 || dat!=0){ /* don't write empty effect */ UniWrite((short)(UNI_PTEFFECT0+eff)); UniWrite(dat); } } public boolean MyCmp(short a[], int a_offset, short b[], int b_offset, int l) { int t; for(t=0;t<l;t++){ if (a[t+a_offset] != b[t+b_offset]) return false; } return true; } public void UniNewline() /* Closes the current row of a unitrk stream (updates the rep/len byte) and sets pointers to start a new row. */ { int n,l,len; n=(unibuf[lastp]>>5)+1; /* repeat of previous row */ l=(unibuf[lastp]&0x1f); /* length of previous row */ len=unipc-unitt; /* length of current row */ /* Now, check if the previous and the current row are identical.. when they are, just increase the repeat field of the previous row */ if(n<8 && len==l && MyCmp(unibuf, lastp+1, unibuf, unitt+1,(len-1))){ unibuf[lastp]+=(short)0x20; unipc=unitt+1; } else{ /* current and previous row aren't equal.. so just update the pointers */ unibuf[unitt]=(short)len; lastp=unitt; unitt=unipc; unipc++; } } public short [] UniDup() /* Terminates the current unitrk stream and returns a pointer to a copy of the stream. */ { int i; short d[]; unibuf[unitt]=0; /*if((d=(short *)malloc(unipc*2))==NULL){ m_->mmIO->myerr=m_->ERROR_ALLOC_STRUCT; return NULL; }*/ d = new short [unipc]; //memcpy(d,unibuf,unipc*2); for(i=0; i<unipc; i++) d[i] = unibuf[i]; return d; } public int TrkLen(short t[]) /* Determines the length (in rows) of a unitrk stream 't' */ { int len=0; short c; int tp=0; while((c=(short)(t[tp]&0x1f)) != 0) { len+=c; tp+=c; } len++; return len; } public boolean UniInit() { unimax=BUFPAGE; /*if(!(unibuf=(short *)malloc(unimax*2))){ m_->mmIO->myerr=m_->ERROR_ALLOC_STRUCT; return 0; }*/ unibuf = new short [unimax]; return true; } public void UniCleanup() { //delete [] unibuf; unibuf=null; } }