304 lines
7.7 KiB
C
304 lines
7.7 KiB
C
|
/* MikMod sound library
|
||
|
(c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
|
||
|
for complete list.
|
||
|
|
||
|
This library is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU Library General Public License as
|
||
|
published by the Free Software Foundation; either version 2 of
|
||
|
the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU Library General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public
|
||
|
License along with this library; if not, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||
|
02111-1307, USA.
|
||
|
*/
|
||
|
|
||
|
/*==============================================================================
|
||
|
|
||
|
$Id: munitrk.c,v 1.2 2005/03/30 19:11:46 realtech Exp $
|
||
|
|
||
|
All routines dealing with the manipulation of UNITRK streams
|
||
|
|
||
|
==============================================================================*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include "mikmod_internals.h"
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
/* Unibuffer chunk size */
|
||
|
#define BUFPAGE 128
|
||
|
|
||
|
UWORD unioperands[UNI_LAST]={
|
||
|
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_S3MEFFECTR */
|
||
|
1, /* UNI_S3MEFFECTT */
|
||
|
1, /* UNI_S3MEFFECTU */
|
||
|
0, /* UNI_KEYOFF */
|
||
|
1, /* UNI_KEYFADE */
|
||
|
2, /* UNI_VOLEFFECTS */
|
||
|
1, /* UNI_XMEFFECT4 */
|
||
|
1, /* UNI_XMEFFECT6 */
|
||
|
1, /* UNI_XMEFFECTA */
|
||
|
1, /* UNI_XMEFFECTE1 */
|
||
|
1, /* UNI_XMEFFECTE2 */
|
||
|
1, /* UNI_XMEFFECTEA */
|
||
|
1, /* UNI_XMEFFECTEB */
|
||
|
1, /* UNI_XMEFFECTG */
|
||
|
1, /* UNI_XMEFFECTH */
|
||
|
1, /* UNI_XMEFFECTL */
|
||
|
1, /* UNI_XMEFFECTP */
|
||
|
1, /* UNI_XMEFFECTX1 */
|
||
|
1, /* UNI_XMEFFECTX2 */
|
||
|
1, /* UNI_ITEFFECTG */
|
||
|
1, /* UNI_ITEFFECTH */
|
||
|
1, /* UNI_ITEFFECTI */
|
||
|
1, /* UNI_ITEFFECTM */
|
||
|
1, /* UNI_ITEFFECTN */
|
||
|
1, /* UNI_ITEFFECTP */
|
||
|
1, /* UNI_ITEFFECTT */
|
||
|
1, /* UNI_ITEFFECTU */
|
||
|
1, /* UNI_ITEFFECTW */
|
||
|
1, /* UNI_ITEFFECTY */
|
||
|
2, /* UNI_ITEFFECTZ */
|
||
|
1, /* UNI_ITEFFECTS0 */
|
||
|
2, /* UNI_ULTEFFECT9 */
|
||
|
2, /* UNI_MEDSPEED */
|
||
|
0, /* UNI_MEDEFFECTF1 */
|
||
|
0, /* UNI_MEDEFFECTF2 */
|
||
|
0, /* UNI_MEDEFFECTF3 */
|
||
|
2, /* UNI_OKTARP */
|
||
|
};
|
||
|
|
||
|
/* Sparse description of the internal module 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 62 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.
|
||
|
|
||
|
*/
|
||
|
|
||
|
/*========== Reading routines */
|
||
|
|
||
|
static UBYTE *rowstart; /* startadress of a row */
|
||
|
static UBYTE *rowend; /* endaddress of a row (exclusive) */
|
||
|
static UBYTE *rowpc; /* current unimod(tm) programcounter */
|
||
|
|
||
|
static UBYTE lastbyte; /* for UniSkipOpcode() */
|
||
|
|
||
|
void UniSetRow(UBYTE* t)
|
||
|
{
|
||
|
rowstart = t;
|
||
|
rowpc = rowstart;
|
||
|
rowend = t?rowstart+(*(rowpc++)&0x1f):t;
|
||
|
}
|
||
|
|
||
|
UBYTE UniGetByte(void)
|
||
|
{
|
||
|
return lastbyte = (rowpc<rowend)?*(rowpc++):0;
|
||
|
}
|
||
|
|
||
|
UWORD UniGetWord(void)
|
||
|
{
|
||
|
return ((UWORD)UniGetByte()<<8)|UniGetByte();
|
||
|
}
|
||
|
|
||
|
void UniSkipOpcode(void)
|
||
|
{
|
||
|
if (lastbyte < UNI_LAST) {
|
||
|
UWORD t = unioperands[lastbyte];
|
||
|
|
||
|
while (t--)
|
||
|
UniGetByte();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns
|
||
|
NULL if the row can't be found. */
|
||
|
UBYTE *UniFindRow(UBYTE* t,UWORD row)
|
||
|
{
|
||
|
UBYTE c,l;
|
||
|
|
||
|
if(t)
|
||
|
while(1) {
|
||
|
c = *t; /* get rep/len byte */
|
||
|
if(!c) return NULL; /* zero ? -> end of track.. */
|
||
|
l = (c>>5)+1; /* extract repeat value */
|
||
|
if(l>row) break; /* reached wanted row? -> return pointer */
|
||
|
row -= l; /* haven't reached row yet.. update row */
|
||
|
t += c&0x1f; /* point t to the next row */
|
||
|
}
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/*========== Writing routines */
|
||
|
|
||
|
static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */
|
||
|
static UWORD unimax; /* buffer size */
|
||
|
|
||
|
static UWORD unipc; /* buffer cursor */
|
||
|
static UWORD unitt; /* current row index */
|
||
|
static UWORD lastp; /* previous row index */
|
||
|
|
||
|
/* Resets index-pointers to create a new track. */
|
||
|
void UniReset(void)
|
||
|
{
|
||
|
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 */
|
||
|
}
|
||
|
|
||
|
/* Expands the buffer */
|
||
|
static int UniExpand(int wanted)
|
||
|
{
|
||
|
if ((unipc+wanted)>=unimax) {
|
||
|
UBYTE *newbuf;
|
||
|
|
||
|
/* Expand the buffer by BUFPAGE bytes */
|
||
|
newbuf=(UBYTE*)MikMod_realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE));
|
||
|
|
||
|
/* Check if MikMod_realloc succeeded */
|
||
|
if(newbuf) {
|
||
|
unibuf = newbuf;
|
||
|
unimax+=BUFPAGE;
|
||
|
return 1;
|
||
|
} else
|
||
|
return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Appends one byte of data to the current row of a track. */
|
||
|
void UniWriteByte(UBYTE data)
|
||
|
{
|
||
|
if (UniExpand(1))
|
||
|
/* write byte to current position and update */
|
||
|
unibuf[unipc++]=data;
|
||
|
}
|
||
|
|
||
|
void UniWriteWord(UWORD data)
|
||
|
{
|
||
|
if (UniExpand(2)) {
|
||
|
unibuf[unipc++]=data>>8;
|
||
|
unibuf[unipc++]=data&0xff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int MyCmp(UBYTE* a,UBYTE* b,UWORD l)
|
||
|
{
|
||
|
UWORD t;
|
||
|
|
||
|
for(t=0;t<l;t++)
|
||
|
if(*(a++)!=*(b++)) return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Closes the current row of a unitrk stream (updates the rep/len byte) and sets
|
||
|
pointers to start a new row. */
|
||
|
void UniNewline(void)
|
||
|
{
|
||
|
UWORD 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]+=0x20;
|
||
|
unipc = unitt+1;
|
||
|
} else {
|
||
|
if (UniExpand(unitt-unipc)) {
|
||
|
/* current and previous row aren't equal... update the pointers */
|
||
|
unibuf[unitt] = len;
|
||
|
lastp = unitt;
|
||
|
unitt = unipc++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Terminates the current unitrk stream and returns a pointer to a copy of the
|
||
|
stream. */
|
||
|
UBYTE* UniDup(void)
|
||
|
{
|
||
|
UBYTE *d;
|
||
|
|
||
|
if (!UniExpand(unitt-unipc)) return NULL;
|
||
|
unibuf[unitt] = 0;
|
||
|
|
||
|
if(!(d=(UBYTE *)MikMod_malloc(unipc))) return NULL;
|
||
|
memcpy(d,unibuf,unipc);
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
int UniInit(void)
|
||
|
{
|
||
|
unimax = BUFPAGE;
|
||
|
|
||
|
if(!(unibuf=(UBYTE*)MikMod_malloc(unimax*sizeof(UBYTE)))) return 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
void UniCleanup(void)
|
||
|
{
|
||
|
if(unibuf) MikMod_free(unibuf);
|
||
|
unibuf = NULL;
|
||
|
}
|
||
|
|
||
|
/* ex:set ts=4: */
|