Added MIDI synthesizer plugin


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6287 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 3dd7eb4..50b5235 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -74,5 +74,6 @@
 vorbis2wav.c
 wv2wav.c
 mpc2wav.c
+midi2wav.c
 iriverify.c
 #endif
diff --git a/apps/plugins/midi/guspat.c b/apps/plugins/midi/guspat.c
new file mode 100644
index 0000000..f0f1eff
--- /dev/null
+++ b/apps/plugins/midi/guspat.c
@@ -0,0 +1,151 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2005 Stepan Moskovchenko
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+
+extern struct plugin_api * rb;
+
+unsigned int readWord(int file)
+{
+	return (readChar(file)<<0) | (readChar(file)<<8); // | (readChar(file)<<8) | (readChar(file)<<0);
+}
+
+unsigned int readDWord(int file)
+{
+	return (readChar(file)<<0) | (readChar(file)<<8) | (readChar(file)<<16) | (readChar(file)<<24);
+}
+
+struct GWaveform * loadWaveform(int file)
+{
+	struct GWaveform * wav = (struct GWaveform *)allocate(sizeof(struct GWaveform));
+	rb->memset(wav, 0, sizeof(struct GWaveform));
+
+	wav->name=readData(file, 7);
+	printf("\nWAVE NAME = [%s]", wav->name);
+	wav->fractions=readChar(file);
+	wav->wavSize=readDWord(file);
+	wav->startLoop=readDWord(file);
+	wav->endLoop=readDWord(file);
+	wav->sampRate=readWord(file);
+
+	wav->lowFreq=readDWord(file);
+	wav->highFreq=readDWord(file);
+	wav->rootFreq=readDWord(file);
+
+	wav->tune=readWord(file);
+
+	wav->balance=readChar(file);
+	wav->envRate=readData(file, 6);
+	wav->envOffset=readData(file, 6);
+
+	wav->tremSweep=readChar(file);
+	wav->tremRate==readChar(file);
+	wav->tremDepth=readChar(file);
+	wav->vibSweep=readChar(file);
+	wav->vibRate=readChar(file);
+	wav->vibDepth=readChar(file);
+	wav->mode=readChar(file);
+
+	wav->scaleFreq=readWord(file);
+	wav->scaleFactor=readWord(file);
+	printf("\nScaleFreq = %d   ScaleFactor = %d   RootFreq = %d", wav->scaleFreq, wav->scaleFactor, wav->rootFreq);
+	wav->res=readData(file, 36);
+	wav->data=readData(file, wav->wavSize);
+
+	return wav;
+}
+
+
+
+int selectWaveform(struct GPatch * pat, int midiNote)
+{
+	int tabFreq = gustable[midiNote]/100; //Comparison
+	int a=0;
+	for(a=0; a<pat->numWaveforms; a++)
+	{
+		if(pat->waveforms[a]->lowFreq/100 <= tabFreq &&
+		   pat->waveforms[a]->highFreq/100 >= tabFreq)
+		{
+			return a;
+		}
+	}
+	return 0;
+}
+
+
+struct GPatch * gusload(char * filename)
+{
+	struct GPatch * gp = (struct GPatch *)allocate(sizeof(struct GPatch));
+	rb->memset(gp, 0, sizeof(struct GPatch));
+
+	int file = rb->open(filename, O_RDONLY);
+
+	gp->header=readData(file, 12);
+	gp->gravisid=readData(file, 10);
+	gp->desc=readData(file, 60);
+	gp->inst=readChar(file);
+	gp->voc=readChar(file);
+	gp->chan=readChar(file);
+	gp->numWaveforms=readWord(file); //readWord(file);
+	gp->vol=readWord(file);
+	gp->datSize=readDWord(file);
+	gp->res=readData(file, 36);
+
+	gp->instrID=readWord(file);
+	gp->instrName=readData(file,16);
+	gp->instrSize=readDWord(file);
+	gp->layers=readChar(file);
+	gp->instrRes=readData(file,40);
+
+
+	gp->layerDup=readChar(file);
+	gp->layerID=readChar(file);
+	gp->layerSize=readDWord(file);
+	gp->numWaves=readChar(file);
+	gp->layerRes=readData(file,40);
+
+/*	printf("\n%s\n%s\n%s", gp->header, gp->gravisid, gp->desc);
+	printf("\nInst = %d", gp->inst);
+	printf("\nVoc  = %d", gp->voc);
+	printf("\nChan = %d", gp->chan);
+	printf("\nWav  = %d", gp->numWaveforms);
+	printf("\nVol  = %d", gp->vol);
+	printf("\nSize = %d", gp->datSize);
+
+	printf("\n\ninstrID = %d", gp->instrID);
+	printf("\ninstrName = %s", gp->instrName);
+//	printf("\ninstrSize = %d", gp->instrSize);
+//	printf("\nlayers = %d", gp->layers);
+*/
+	printf("\nFILE: %s", filename);
+	printf("\nlayerSamples=%d", gp->numWaves);
+
+	int a=0;
+	for(a=0; a<gp->numWaves; a++)
+		gp->waveforms[a] = loadWaveform(file);
+
+
+	printf("\nPrecomputing note table");
+
+	for(a=0; a<128; a++)
+	{
+		gp->noteTable[a] = selectWaveform(gp, a);
+	}
+	rb->close(file);
+
+	return gp;
+}
diff --git a/apps/plugins/midi/guspat.h b/apps/plugins/midi/guspat.h
new file mode 100644
index 0000000..5007c4d
--- /dev/null
+++ b/apps/plugins/midi/guspat.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2005 Stepan Moskovchenko
+ *
+ * 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 came from one of the Gravis documents
+const static unsigned int gustable[]=
+{
+  8175, 8661, 9177, 9722, 10300, 10913, 11562, 12249, 12978, 13750, 14567, 15433,
+  16351, 17323, 18354, 19445, 20601, 21826, 23124, 24499, 25956, 27500, 29135, 30867,
+  32703, 34647, 36708, 38890, 41203, 43653, 46249, 48999, 51913, 54999, 58270, 61735,
+  65406, 69295, 73416, 77781, 82406, 87306, 92498, 97998, 103826, 109999, 116540, 123470,
+  130812, 138591, 146832, 155563, 164813, 174614, 184997, 195997, 207652, 219999, 233081, 246941,
+  261625, 277182, 293664, 311126, 329627, 349228, 369994, 391995, 415304, 440000, 466163, 493883,
+  523251, 554365, 587329, 622254, 659255, 698456, 739989, 783991, 830609, 880000, 932328, 987767,
+  1046503, 1108731, 1174660, 1244509, 1318511, 1396914, 1479979, 1567983, 1661220, 1760002, 1864657, 1975536,
+  2093007, 2217464, 2349321, 2489019, 2637024, 2793830, 2959960, 3135968, 3322443, 3520006, 3729316, 3951073,
+  4186073, 4434930, 4698645, 4978041, 5274051, 5587663, 5919922, 6271939, 6644889, 7040015, 7458636, 7902150
+};
+
+struct GWaveform
+{
+	unsigned char * name;
+	unsigned char fractions;
+	unsigned int wavSize;
+	unsigned int startLoop;
+	unsigned int endLoop;
+	unsigned int sampRate;
+	unsigned int lowFreq;
+	unsigned int highFreq;
+	unsigned int rootFreq;
+	unsigned int tune;
+	unsigned int balance;
+	unsigned char * envRate;
+	unsigned char * envOffset;
+
+	unsigned char tremSweep;
+	unsigned char tremRate;
+	unsigned char tremDepth;
+	unsigned char vibSweep;
+	unsigned char vibRate;
+	unsigned char vibDepth;
+	unsigned char mode;
+
+	unsigned int scaleFreq;
+	unsigned int scaleFactor;
+
+	unsigned char * res;
+	signed char * data;
+};
+
+
+struct GPatch
+{
+	unsigned int patchNumber;
+	unsigned char * header;
+	unsigned char * gravisid;
+	unsigned char * desc;
+	unsigned char inst, voc, chan;
+	unsigned int numWaveforms;
+	unsigned int datSize;
+	unsigned int vol;
+	unsigned char * res;
+
+
+	unsigned int instrID;
+	unsigned char * instrName;
+	unsigned int instrSize;
+	unsigned int layers;
+	unsigned char * instrRes;
+
+	unsigned char layerDup;
+	unsigned char layerID;
+	unsigned int layerSize;
+	unsigned char numWaves;
+	unsigned char * layerRes;
+
+	unsigned char noteTable[128];
+	struct GWaveform * waveforms[255];
+};
+
diff --git a/apps/plugins/midi/midifile.c b/apps/plugins/midi/midifile.c
new file mode 100644
index 0000000..08efb73
--- /dev/null
+++ b/apps/plugins/midi/midifile.c
@@ -0,0 +1,305 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2005 Stepan Moskovchenko
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+
+
+extern struct plugin_api * rb;
+
+struct Track * readTrack(int file);
+int readID(int file);
+void bail(const char *);
+
+struct MIDIfile * loadFile(char * filename)
+{
+	struct MIDIfile * mf;
+	int file = rb->open (filename, O_RDONLY);
+
+	if(file==0)
+	{
+		bail("Could not open file\n");
+	}
+
+	mf = (struct MIDIfile*)allocate(sizeof(struct MIDIfile));
+
+	if(mf==NULL)
+	{
+		rb->close(file);
+		bail("Could not allocate memory for MIDIfile struct\n");
+	}
+
+	rb->memset(mf, 0, sizeof(struct MIDIfile));
+
+	if(readID(file) != ID_MTHD)
+	{
+		rb->close(file);
+		bail("Invalid file header chunk.");
+	}
+
+	if(readFourBytes(file)!=6)
+	{
+		rb->close(file);
+		bail("Header chunk size invalid.");
+	}
+
+	if(readTwoBytes(file)==2)
+	{
+		rb->close(file);
+		bail("MIDI file type not supported");
+	}
+
+	mf->numTracks = readTwoBytes(file);
+	mf->div = readTwoBytes(file);
+
+	int track=0;
+
+	printf("\nnumTracks=%d  div=%d\nBegin reading track data\n", mf->numTracks, mf->div);
+
+//	return;
+
+
+	while(! eof(file) && track < mf->numTracks)
+	{
+		unsigned char id = readID(file);
+
+
+		if(id == ID_EOF)
+		{
+			if(mf->numTracks != track)
+			{
+				printf("\nError: file claims to have %d tracks.\n       I only see %d here.\n", 	mf->numTracks, track);
+				mf->numTracks = track;
+			}
+			return mf;
+		}
+
+		if(id == ID_MTRK)
+		{
+			mf->tracks[track] = readTrack(file);
+			//exit(0);
+			track++;
+		} else
+		{
+			printf("\n SKIPPING TRACK");
+			int len = readFourBytes(file);
+			while(--len)
+				readChar(file);
+		}
+	}
+	return mf;
+
+}
+
+
+int rStatus = 0;
+
+//Returns 0 if done, 1 if keep going
+int readEvent(int file, void * dest)
+{
+	struct Event dummy;
+	struct Event * ev = (struct Event *) dest;
+
+	if(ev == NULL)
+		ev = &dummy; //If we are just counting events instead of loading them
+
+	ev->delta = readVarData(file);
+
+
+	int t=readChar(file);
+
+	if((t&0x80) == 0x80) //if not a running status event
+	{
+		ev->status = t;
+		if(t == 0xFF)
+		{
+			ev->d1 = readChar(file);
+			ev->len = readVarData(file);
+
+			 //Allocate and read in the data block
+			if(dest != NULL)
+			{
+				ev->evData = readData(file, ev->len);
+				printf("\nDATA: <%s>", ev->evData);
+			}
+			else
+			{
+				//Don't allocate anything, just see how much it would tale
+				//To make memory usage efficient
+
+				int a=0;
+				for(a=0; a<ev->len; a++)
+					readChar(file); //Skip skip
+			}
+
+			if(ev->d1 == 0x2F)
+			{
+ 				return 0; 	//Termination meta-event
+			}
+		} else             //If part of a running status event
+		{
+			rStatus = t;
+			ev->status = t;
+			ev->d1 = readChar(file);
+
+			if (  ((t & 0xF0) != 0xD0) && ((t & 0xF0) != 0xC0) && ((t & 0xF0) > 0x40) )
+			{
+				ev->d2 = readChar(file);
+			} else
+				ev->d2 = 127;
+		}
+	} else  //Running Status
+	{
+		ev->status = rStatus;
+		ev->d1 = t;
+		if (  ((rStatus & 0xF0) != 0xD0) && ((rStatus & 0xF0) != 0xC0) && ((rStatus & 0xF0) > 0x40) )
+		{
+			ev->d2 = readChar(file);
+		} else
+			ev->d2 = 127;
+	}
+	return 1;
+}
+
+
+
+struct Track * readTrack(int file)
+{
+	struct Track * trk = (struct Track *)allocate(sizeof(struct Track));
+	rb->memset(trk, 0, sizeof(struct Track));
+
+	trk->size = readFourBytes(file);
+	trk->pos = 0;
+	trk->delta = 0;
+
+	int len=0;
+	int numEvents=0;
+
+	int pos = rb->lseek(file, 0, SEEK_CUR);
+
+	while(readEvent(file, NULL))	//Memory saving technique
+		numEvents++;		//Attempt to read in events, count how many
+					//THEN allocate memory and read them in
+	rb->lseek(file, pos, SEEK_SET);
+
+	int trackSize = (numEvents+1) * sizeof(struct Event);
+	void * dataPtr = allocate(trackSize);
+	trk->dataBlock = dataPtr;
+
+	numEvents=0;
+
+	while(readEvent(file, dataPtr))
+	{
+		if(trackSize < dataPtr-trk->dataBlock)
+		{
+			printf("\nTrack parser memory out of  bounds");
+			exit(1);
+		}
+		dataPtr+=sizeof(struct Event);
+		numEvents++;
+	}
+	trk->numEvents = numEvents;
+
+	return trk;
+}
+
+
+int readID(int file)
+{
+	char id[5];
+	id[4]=0;
+	BYTE a;
+
+	for(a=0; a<4; a++)
+		id[a]=readChar(file);
+	if(eof(file))
+	{
+		printf("\End of file reached.");
+		return ID_EOF;
+	}
+	if(rb->strcmp(id, "MThd")==0)
+		return ID_MTHD;
+	if(rb->strcmp(id, "MTrk")==0)
+		return ID_MTRK;
+	return ID_UNKNOWN;
+}
+
+
+int readFourBytes(int file)
+{
+	int data=0;
+	BYTE a=0;
+	for(a=0; a<4; a++)
+		data=(data<<8)+readChar(file);
+	return data;
+}
+
+int readTwoBytes(int file)
+{
+	int data=(readChar(file)<<8)+readChar(file);
+	return data;
+}
+
+//This came from the MIDI file format guide
+int readVarData(int file)
+{
+    unsigned int value;
+    char c;
+    if ( (value = readChar(file)) & 0x80 )
+    {
+       value &= 0x7F;
+       do
+       {
+         value = (value << 7) + ((c = readChar(file)) & 0x7F);
+       } while (c & 0x80);
+    }
+    return(value);
+}
+
+//This function should not be needed because we
+//can just release the whole memory buffer at once
+void unloadFile(struct MIDIfile * mf)
+{
+	if(mf == NULL)
+		return;
+	int a=0;
+	//Unload each track
+	for(a=0; a<mf->numTracks; a++)
+	{
+		int b=0;
+
+		if(mf->tracks[a] != NULL)
+			for(b=0; b<mf->tracks[a]->numEvents; b++)
+			{
+				if(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData!=NULL)
+					free(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData);
+			}
+
+		if(mf->tracks[a]!=NULL && mf->tracks[a]->dataBlock != NULL)
+			free(mf->tracks[a]->dataBlock);	//Unload the event block
+
+		if(mf->tracks[a]!=NULL)
+			free(mf->tracks[a]);	//Unload the track structure itself
+	}
+	free(mf); 	//Unload the main struct
+}
+
+void bail(const char * err)
+{
+	printf("\nERROR: %s\n", err);
+	exit(0);
+}
+
diff --git a/apps/plugins/midi/midiutil.c b/apps/plugins/midi/midiutil.c
new file mode 100644
index 0000000..0ab5a35
--- /dev/null
+++ b/apps/plugins/midi/midiutil.c
@@ -0,0 +1,262 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2005 Stepan Moskovchenko
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+
+#define BYTE unsigned char
+
+//Data chunk ID types, returned by readID()
+#define ID_UNKNOWN 	-1
+#define ID_MTHD		1
+#define ID_MTRK 	2
+#define ID_EOF  	3
+
+//MIDI Commands
+#define MIDI_NOTE_OFF	128
+#define MIDI_NOTE_ON	144
+#define MIDI_AFTERTOUCH	160
+#define MIDI_CONTROL	176
+#define MIDI_PRGM	192
+#define MIDI_PITCHW	224
+
+
+//MIDI Controllers
+#define CTRL_VOLUME	7
+#define CTRL_BALANCE	8
+#define CTRL_PANNING	10
+#define CHANNEL 	1
+
+
+#define STATE_ATTACK  		1
+#define STATE_DECAY  		2
+#define STATE_SUSTAIN 		3
+#define STATE_RELEASE 		4
+#define STATE_RAMPDOWN		5
+
+#define STATE_LOOPING           7
+#define STATE_NONLOOPING	8
+
+#define LOOP_ENABLED	4
+#define LOOP_PINGPONG   8
+#define LOOP_REVERSE    16
+
+#define LOOPDIR_FORWARD 0
+#define LOOPDIR_REVERSE 1
+
+
+extern struct plugin_api * rb;
+
+
+unsigned char chVol[16];	//Channel volume
+unsigned char chPanLeft[16];	//Channel panning
+unsigned char chPanRight[16];
+unsigned char chPat[16];        //Channel patch
+unsigned char chPW[16];        //Channel pitch wheel, MSB
+
+struct GPatch * gusload(char *);
+struct GPatch * patchSet[128];
+struct GPatch * drumSet[128];
+struct SynthObject voices[MAX_VOICES];
+
+
+
+struct SynthObject
+{
+	int tmp;
+	struct GWaveform * wf;
+	unsigned int delta;
+	unsigned int decay;
+	unsigned int cp;
+	unsigned char state, pstate, loopState, loopDir;
+	unsigned char note, vol, ch, isUsed;
+	int curRate, curOffset, targetOffset;
+	int curPoint;
+};
+
+
+struct Event
+{
+	unsigned int delta;
+	unsigned char status, d1, d2;
+	unsigned int len;
+	unsigned char * evData;
+};
+
+struct Track
+{
+	unsigned int size;
+	unsigned int numEvents;
+	unsigned int delta;	//For sequencing
+	unsigned int pos;	//For sequencing
+	void * dataBlock;
+};
+
+
+struct MIDIfile
+{
+	int  Length;
+
+	//int Format; //We don't really care what type it is
+	unsigned short numTracks;
+	unsigned short div;	//Time division, X ticks per millisecond
+	struct Track * tracks[48];
+	unsigned char patches[128];
+	int numPatches;
+};
+
+void *my_malloc(int size);
+
+void *alloc(int size)
+{
+	static char *offset = NULL;
+	static int totalSize = 0;
+	char *ret;
+
+	if (offset == NULL)
+	{
+		offset = rb->plugin_get_audio_buffer(&totalSize);
+	}
+
+	if (size + 4 > totalSize)
+	{
+		return NULL;
+	}
+
+	ret = offset + 4;
+	*((unsigned int *)offset) = size;
+
+	offset += size + 4;
+	totalSize -= size + 4;
+	return ret;
+}
+
+void *ralloc(char *offset, int len)
+{
+	int size;
+	char *ret;
+
+	if (offset == NULL)
+	{
+		return alloc(len);
+	}
+
+	size = *((unsigned int *)offset - 4);
+
+	if (size >= 0x02000000)
+	{
+		return NULL;
+	}
+
+	ret = alloc(len);
+
+	if (len < size)
+	{
+		rb->memcpy(ret, offset, len);
+	}
+	else
+	{
+		rb->memcpy(ret, offset, size);
+		rb->memset(ret, 0, len - size);
+	}
+
+	return ret;
+}
+
+
+void * allocate(int size)
+{
+	return alloc(size);
+}
+
+void sendEvent(struct Event * ev);
+int tick(struct MIDIfile * mf);
+inline void setPoint(struct SynthObject * so, int pt);
+struct Event * getEvent(struct Track * tr, int evNum);
+
+unsigned char readChar(int file)
+{
+	char buf[2];
+	rb->read(file, &buf, 1);
+	return buf[0];
+}
+
+unsigned char * readData(int file, int len)
+{
+	unsigned char * dat = allocate(len);
+	rb->read(file, dat, len);
+	return dat;
+}
+
+int eof(int fd)
+{
+	int curPos = rb->lseek(fd, 0, SEEK_CUR);
+
+	int size = rb->lseek(fd, 0, SEEK_END);
+
+	rb->lseek(fd, curPos, SEEK_SET);
+	return size+1 == rb->lseek(fd, 0, SEEK_CUR);
+}
+
+void printf(char *fmt, ...) {}
+
+//#define my_malloc(a) malloc(a)
+
+
+void *audio_bufferbase;
+void *audio_bufferpointer;
+unsigned int audio_buffer_free;
+
+
+
+
+void *my_malloc(int size)
+{
+
+    void *alloc;
+
+    if (!audio_bufferbase)
+    {
+        audio_bufferbase = audio_bufferpointer
+            = rb->plugin_get_audio_buffer(&audio_buffer_free);
+#if MEM <= 8 && !defined(SIMULATOR)
+
+        if ((unsigned)(ovl_start_addr - (unsigned char *)audio_bufferbase)
+            < audio_buffer_free)
+            audio_buffer_free = ovl_start_addr - (unsigned char *)audio_bufferbase;
+#endif
+    }
+    if (size + 4 > audio_buffer_free)
+        return 0;
+    alloc = audio_bufferpointer;
+    audio_bufferpointer += size + 4;
+    audio_buffer_free -= size + 4;
+    return alloc;
+}
+
+void setmallocpos(void *pointer)
+{
+    audio_bufferpointer = pointer;
+    audio_buffer_free = audio_bufferpointer - audio_bufferbase;
+}
+
+void exit(int code)
+{
+}
+
+void free(void * ptr)
+{
+}
\ No newline at end of file
diff --git a/apps/plugins/midi/sequencer.c b/apps/plugins/midi/sequencer.c
new file mode 100644
index 0000000..fb98fda
--- /dev/null
+++ b/apps/plugins/midi/sequencer.c
@@ -0,0 +1,298 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2005 Stepan Moskovchenko
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+
+extern struct plugin_api * rb;
+
+long tempo=375000;
+
+
+void setVol(int ch, int vol)
+{
+	printf("\nvolume[%d]  %d ==> %d", ch, chVol[ch], vol);
+	chVol[ch]=vol;
+}
+
+void setPan(int ch, int pan)
+{
+	printf("\npanning[%d]  %d ==> %d", ch, chPanRight[ch], pan);
+
+	chPanLeft[ch]=128-pan;
+	chPanRight[ch]=pan;
+}
+
+
+void setPatch(int ch, int pat)
+{
+	chPat[ch]=pat;
+}
+
+
+/*
+ Pitch Bend table, Computed by
+ for i=0:127, fprintf('%d,', round(2^16*2^((i-64)/384))); end
+ (When typed into Matlab)
+ 16 bit pitch bend table
+*/
+long pitchTbl[]=
+{
+	58386,58491,58597,58703,58809,58915,59022,59128,59235,59342,59449,59557,59664,59772,59880,59988,60097,60205,
+	60314,60423,60532,60642,60751,60861,60971,61081,61191,61302,61413,61524,61635,61746,61858,61970,62081,62194,
+	62306,62419,62531,62644,62757,62871,62984,63098,63212,63326,63441,63555,63670,63785,63901,64016,64132,64248,
+	64364,64480,64596,64713,64830,64947,65065,65182,65300,65418,65536,65654,65773,65892,66011,66130,66250,66369,
+	66489,66609,66730,66850,66971,67092,67213,67335,67456,67578,67700,67823,67945,68068,68191,68314,68438,68561,
+	68685,68809,68933,69058,69183,69308,69433,69558,69684,69810,69936,70062,70189,70316,70443,70570,70698,70825,
+	70953,71082,71210,71339,71468,71597,71726,71856,71985,72115,72246,72376,72507,72638,72769,72901,73032,73164,
+	73297,73429
+};
+
+
+void findDelta(struct SynthObject * so, int ch, int note)
+{
+
+	struct GWaveform * wf = patchSet[chPat[ch]]->waveforms[patchSet[chPat[ch]]->noteTable[note]];
+	so->wf=wf;
+	so->delta = (((gustable[note]<<10) / wf->rootFreq) * wf->sampRate / SAMPLE_RATE);
+	so->delta = so->delta * pitchTbl[chPW[ch]] >> 16;
+}
+
+void setPW(int ch, int msb)
+{
+	printf("\npitchw[%d]  %d ==> %d", ch, chPW[ch], msb);
+	chPW[ch] = msb;
+
+	int a=0;
+	for(a = 0; a<MAX_VOICES; a++)
+	{
+		if(voices[a].isUsed==1 && voices[a].ch == ch)
+		{
+			findDelta(&voices[a], ch, voices[a].note);
+		}
+	}
+}
+
+void pressNote(int ch, int note, int vol)
+{
+	int a=0;
+	for(a=0; a<MAX_VOICES; a++)
+	{
+		if(voices[a].isUsed==0)
+			break;
+	}
+	if(a==MAX_VOICES-1)
+	{
+		printf("\nOVERFLOW: Too many voices playing at once. No more left");
+		printf("\nVOICE DUMP: ");
+		for(a=0; a<48; a++)
+			printf("\n#%d  Ch=%d  Note=%d  curRate=%d   curOffset=%d   curPoint=%d   targetOffset=%d", a, voices[a].ch, voices[a].note, voices[a].curRate, voices[a].curOffset, voices[a].curPoint, voices[a].targetOffset);
+		return; //None avaolable
+	}
+	voices[a].ch=ch;
+	voices[a].note=note;
+	voices[a].vol=vol;
+	voices[a].cp=0;
+	voices[a].state=STATE_ATTACK;
+	voices[a].pstate=STATE_ATTACK;
+	voices[a].decay=255;
+
+
+	voices[a].loopState=STATE_NONLOOPING;
+	voices[a].loopDir = LOOPDIR_FORWARD;
+	/*
+	  OKAY. Gt = Gus Table value
+	  rf = Root Frequency of wave
+	  SR = sound sampling rate
+	  sr = WAVE sampling rate
+	*/
+
+
+	                           /*
+	unsigned int gt = gustable[note];
+	unsigned int rf = wf->rootFreq;
+	unsigned int SR = SAMPLE_RATE;
+	unsigned int sr = wf->sampRate;
+	voices[a].delta=((((gt<<10) / rf) * sr / SR));
+	                             */
+
+
+	if(ch!=9)
+	{
+		findDelta(&voices[a], ch, note);
+		//Turn it on
+		voices[a].isUsed=1;
+		setPoint(&voices[a], 0);
+	} else
+	{
+		if(drumSet[note]!=NULL)
+		{
+                	if(note<35)
+				printf("\nNOTE LESS THAN 35, AND A DRUM PATCH EXISTS FOR THIS? WHAT THE HELL?");
+
+			struct GWaveform * wf = drumSet[note]->waveforms[0];
+			voices[a].wf=wf;
+			voices[a].delta = (((gustable[note]<<10) / wf->rootFreq) * wf->sampRate / SAMPLE_RATE);
+			if(wf->mode & 28)
+				printf("\nWoah, a drum patch has a loop. Stripping the loop...");
+			wf->mode = wf->mode & (255-28);
+			//Turn it on
+			voices[a].isUsed=1;
+			setPoint(&voices[a], 0);
+
+		} else
+		{
+			printf("\nWarning: drum %d does not have a patch defined... Ignoring it", note);
+		}
+	}
+}
+
+
+void releaseNote(int ch, int note)
+{
+	if(ch==9) // && note != 27 && note != 31 && note != 28)
+		return;
+	int a=0;
+	for(a=0; a<MAX_VOICES; a++)
+	{
+		if(voices[a].ch == ch && voices[a].note == note)
+		{
+			//voices[a].isUsed=0;
+			if((voices[a].wf->mode & 28))
+			{
+				voices[a].tmp=40;
+//				voices[a].state = STATE_RELEASE; //Ramp down
+
+//				voices[a].state = STATE_RAMPDOWN; //Ramp down
+
+//				voices[a].isUsed = 0;
+				setPoint(&voices[a], 3);
+			}
+		}
+	}
+}
+
+void sendEvent(struct Event * ev)
+{
+//	printf("\nEVENT S=%2x D1=%2x D2=%2x", ev->status, ev->d1, ev->d2);
+
+	if( ((ev->status & 0xF0) == MIDI_CONTROL) && (ev->d1 == CTRL_VOLUME) )
+	{
+		setVol((ev->status & 0xF), ev->d2);
+		return;
+	}
+
+	if( ((ev->status & 0xF0) == MIDI_CONTROL) && (ev->d1 == CTRL_PANNING))
+	{
+		setPan((ev->status & 0xF), ev->d2);
+		return;
+	}
+
+	if(((ev->status & 0xF0) == MIDI_PITCHW))
+	{
+		setPW((ev->status & 0xF), ev->d2);
+		return;
+	}
+
+	if(((ev->status & 0xF0) == MIDI_NOTE_ON) && (ev->d2 != 0))
+	{
+		pressNote(ev->status & 0x0F, ev->d1, ev->d2);
+		return;
+	}
+
+	if(((ev->status & 0xF0) == MIDI_NOTE_ON) && (ev->d2 == 0)) //Release by vol=0
+	{
+		releaseNote(ev->status & 0x0F, ev->d1);
+		return;
+	}
+
+
+	if((ev->status & 0xF0) == MIDI_NOTE_OFF)
+	{
+		releaseNote(ev->status & 0x0F, ev->d1);
+		return;
+	}
+
+	if((ev->status & 0xF0) == MIDI_PRGM)
+	{
+		if((ev->status & 0x0F) == 9)
+			printf("\nNOT PATCHING: Someone tried patching Channel 9 onto something?");
+		else
+			setPatch(ev->status & 0x0F, ev->d1);
+	}
+}
+
+
+
+
+
+int tick(struct MIDIfile * mf)
+{
+	if(mf==NULL)
+		return;
+
+	int a=0;
+	int tracksAdv=0;
+	for(a=0; a<mf->numTracks; a++)
+	{
+		struct Track * tr = mf->tracks[a];
+
+		if(tr == NULL)
+			printf("\nNULL TRACK: %d", a);
+
+
+		//BIG DEBUG STATEMENT
+		//printf("\nTrack %2d,  Event = %4d of %4d,   Delta = %5d,    Next = %4d", a, tr->pos, tr->numEvents, tr->delta, getEvent(tr, tr->pos)->delta);
+
+
+		if(tr != NULL && (tr->pos < tr->numEvents))
+		{
+			tr->delta++;
+			tracksAdv++;
+			while(getEvent(tr, tr->pos)->delta <= tr->delta)
+			{
+//				printf("\nDelta = %d", tr->delta);
+				struct Event * e = getEvent(tr, tr->pos);
+
+				if(e->status != 0xFF)
+				{
+					sendEvent(e);
+					if(((e->status&0xF0) == MIDI_PRGM))
+					{
+						printf("\nPatch Event, patch[%d] ==> %d", e->status&0xF, e->d1);
+					}
+				}
+				else
+				{
+					if(e->d1 == 0x51)
+					{
+						tempo =	(((short)e->evData[0])<<16)|(((short)e->evData[1])<<8)|(e->evData[2]);
+						printf("\nMeta-Event: Tempo Set = %d", tempo);
+					}
+				}
+				tr->delta = 0;
+				tr->pos++;
+				if(tr->pos>=(tr->numEvents-1))
+					break;
+			}
+		}
+	}
+
+	if(tracksAdv != 0)
+		return 1;
+	else
+		return 0;
+}
diff --git a/apps/plugins/midi/synth.c b/apps/plugins/midi/synth.c
new file mode 100644
index 0000000..0d07ed9
--- /dev/null
+++ b/apps/plugins/midi/synth.c
@@ -0,0 +1,430 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2005 Stepan Moskovchenko
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+
+
+extern struct plugin_api * rb;
+
+struct Event * getEvent(struct Track * tr, int evNum)
+{
+	return tr->dataBlock + (evNum*sizeof(struct Event));
+}
+
+void readTextBlock(int file, char * buf)
+{
+	char c = 0;
+	do
+	{
+		c = readChar(file);
+	} while(c == '\n' || c == ' ' || c=='\t');
+
+	rb->lseek(file, -1, SEEK_CUR);
+	int cp = 0;
+	do
+	{
+		c = readChar(file);
+		buf[cp] = c;
+		cp++;
+	} while (c != '\n' && c != ' ' && c != '\t' && !eof(file));
+	buf[cp-1]=0;
+	rb->lseek(file, -1, SEEK_CUR);
+}
+
+
+
+//Filename is the name of the config file
+//The MIDI file should have been loaded at this point
+void initSynth(struct MIDIfile * mf, char * filename, char * drumConfig)
+{
+	char patchUsed[128];
+	char drumUsed[128];
+	int a=0;
+	for(a=0; a<MAX_VOICES; a++)
+	{
+		voices[a].cp=0;
+		voices[a].vol=0;
+		voices[a].ch=0;
+		voices[a].isUsed=0;
+		voices[a].note=0;
+	}
+
+	for(a=0; a<16; a++)
+	{
+		chVol[a]=100; //Default, not quite full blast..
+        	chPanLeft[a]=64;   //Center
+		chPanRight[a]=64;   //Center
+		chPat[a]=0;   //Ac Gr Piano
+		chPW[a]=64; // .. not .. bent ?
+	}
+	for(a=0; a<128; a++)
+	{
+		patchSet[a]=NULL;
+		drumSet[a]=NULL;
+		patchUsed[a]=0;
+		drumUsed[a]=0;
+	}
+
+	//Always load the piano.
+	//Some files will assume its loaded without specifically
+	//issuing a Patch command... then we wonder why we can't hear anything
+	patchUsed[0]=1;
+
+	//Scan the file to see what needs to be loaded
+	for(a=0; a<mf->numTracks; a++)
+	{
+		int ts=0;
+
+		if(mf->tracks[a] == NULL)
+		{
+			printf("\nNULL TRACK !!!");
+			exit(1);
+			return;
+		}
+
+		for(ts=0; ts<mf->tracks[a]->numEvents; ts++)
+		{
+
+			if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9))
+				drumUsed[getEvent(mf->tracks[a], ts)->d1]=1;
+
+			if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM)
+			{
+				if(patchUsed[getEvent(mf->tracks[a], ts)->d1]==0)
+					printf("\nI need to load patch %d.", getEvent(mf->tracks[a], ts)->d1);
+				patchUsed[getEvent(mf->tracks[a], ts)->d1]=1;
+			}
+		}
+	}
+
+	int file = rb->open(filename, O_RDONLY);
+
+	char name[30];
+	char fn[30];
+
+	//Scan our config file and load the right patches as needed
+	int c = 0;
+	rb->snprintf(name, 30, "");
+	for(a=0; a<128; a++)
+	{
+		while(readChar(file)!=' ' && !eof(file));
+		readTextBlock(file, name);
+
+		rb->snprintf(fn, 30, "/patchset/%s.pat", name);
+		printf("\nLOADING: <%s> ", fn);
+		if(patchUsed[a]==1)
+			patchSet[a]=gusload(fn);
+
+		while((c != '\n'))
+			c = readChar(file);
+	}
+	rb->close(file);
+
+	file = rb->open(drumConfig, O_RDONLY);
+
+	//Scan our config file and load the drum data
+	int idx=0;
+	char number[30];
+	while(!eof(file))
+	{
+		readTextBlock(file, number);
+		readTextBlock(file, name);
+		rb->snprintf(fn, 30, "/patchset/%s.pat", name);
+
+		idx = rb->atoi(number);
+		if(idx == 0)
+			break;
+
+		if(drumUsed[idx]==1)
+			drumSet[idx]=gusload(fn);
+		while((c != '\n') && (c != 255) && (!eof(file)))
+		{
+			printf("loop");
+			c = readChar(file);
+		}
+	}
+	rb->close(file);
+}
+
+
+
+inline signed short int getSample(struct GWaveform * wf, unsigned int s)
+{
+
+	//16 bit samples
+	if(wf->mode&1)
+	{
+
+		if(s<<1 >= wf->wavSize)
+		{
+		//	printf("\nSAMPLE OUT OF RANGE: s=%d  2s=%d  ws=%d", s, 2*s, wf->wavSize);
+			return 0;
+		}
+
+
+		/*
+		 *	Probably put the signed/unsigned and and 8-16 bit conversion
+		 *	into the patch loader and have it run there, once.
+		*/
+
+
+		//If they are unsigned, convert them to signed
+		//or was it the other way around. Whatever, it works
+		unsigned char b1=wf->data[s<<1]+((wf->mode & 2) << 6);
+		unsigned char b2=wf->data[(s<<1)|1]+((wf->mode & 2) << 6);
+		return (b1 | (b2<<8));
+	}
+	else
+	{               //8-bit samples
+		unsigned char b1=wf->data[s]+((wf->mode & 2) << 6);
+		return b1<<8;
+	}
+}
+
+
+
+
+inline void setPoint(struct SynthObject * so, int pt)
+{
+	if(so->ch==9) //Drums, no ADSR
+	{
+		so->curOffset = 1<<27;
+		so->curRate = 1;
+		return;
+	}
+
+	if(so->wf==NULL)
+	{
+		printf("\nCrap... null waveform...");
+		exit(1);
+	}
+	if(so->wf->envRate==NULL)
+	{
+		printf("\nWaveform has no envelope set");
+		exit(1);
+	}
+
+	so->curPoint = pt;
+
+	int r=0;
+
+
+
+
+	int rate = so->wf->envRate[pt];
+
+	r=3-((rate>>6) & 0x3);		// Some blatant Timidity code for rate conversion...
+	r*=3;
+	r = (rate & 0x3f) << r;
+
+	/*
+	Okay. This is the rate shift. Timidity defaults to 9, and sets
+	it to 10 if you use the fast decay option. Slow decay sounds better
+	on some files, except on some other files... you get chords that aren't
+	done decaying yet.. and they dont harmonize with the next chord and it
+	sounds like utter crap. Yes, even Timitidy does that. So I'm going to
+	default this to 10, and maybe later have an option to set it to 9
+	for longer decays.
+	*/
+	so->curRate = r<<9;
+
+
+	so->targetOffset = so->wf->envOffset[pt]<<(20);
+	if(pt==0)
+		so->curOffset = 0;
+}
+
+
+long msi=0;
+
+inline void stopVoice(struct SynthObject * so)
+{
+	if(so->state == STATE_RAMPDOWN)
+		return;
+	so->state = STATE_RAMPDOWN;
+	so->decay = 255;
+
+}
+
+int rampDown = 0;
+
+inline signed short int synthVoice(int v)
+{
+	//Probably can combine these 2 lines into one..
+	//But for now, this looks more readable
+//	struct GPatch * pat = patchSet[chPat[voices[v].ch]];
+//	struct GWaveform * wf = pat->waveforms[pat->noteTable[voices[v].note]];
+	struct SynthObject * so = &voices[v];
+	struct GWaveform * wf = so->wf;
+
+	signed int s;
+
+	if(so->state != STATE_RAMPDOWN)
+	{
+		if(so->loopDir==LOOPDIR_FORWARD)
+		{
+			so->cp += so->delta;
+		}
+		else
+		{
+			so->cp -= so->delta;
+		}
+	}
+
+	if( (so->cp>>9 >= (wf->wavSize)) && (so->state != STATE_RAMPDOWN))
+		stopVoice(so);
+
+        /*
+	//Original, working, no interpolation
+		s=getSample(wf, (so->cp>>10));
+        */
+
+
+
+        int s2=getSample(wf, (so->cp>>10)+1);
+
+	if((wf->mode & (LOOP_REVERSE|LOOP_PINGPONG)) && so->loopState == STATE_LOOPING && (so->cp>>10 <= (wf->startLoop>>1)))
+	{
+		if(wf->mode & LOOP_REVERSE)
+		{
+			so->cp = (wf->endLoop)<<9;
+			s2=getSample(wf, (so->cp>>10));
+	        } else
+			so->loopDir = LOOPDIR_FORWARD;
+	}
+
+	if((wf->mode & 28) && (so->cp>>10 >= wf->endLoop>>1))
+	{
+		so->loopState = STATE_LOOPING;
+		if((wf->mode & (24)) == 0)
+		{
+			so->cp = (wf->startLoop)<<9;
+			s2=getSample(wf, (so->cp>>10));
+		} else
+			so->loopDir = LOOPDIR_REVERSE;
+	}
+
+	//Better, working, linear interpolation
+	int s1=getSample(wf, (so->cp>>10));
+	s = s1 + ((long)((s2 - s1) * (so->cp & 1023))>>10);
+
+	if(so->curRate == 0)
+		stopVoice(so);
+
+
+	if(so->ch != 9) //Stupid ADSR code... and don't do ADSR for drums
+	{
+		if(so->curOffset < so->targetOffset)
+		{
+			so->curOffset += (so->curRate);
+			if(so -> curOffset > so->targetOffset && so->curPoint != 2)
+			{
+				if(so->curPoint != 5)
+					setPoint(so, so->curPoint+1);
+				else
+					stopVoice(so);
+			}
+		} else
+		{
+			so->curOffset -= (so->curRate);
+			if(so -> curOffset < so->targetOffset && so->curPoint != 2)
+			{
+
+				if(so->curPoint != 5)
+					setPoint(so, so->curPoint+1);
+				else
+					stopVoice(so);
+
+			}
+		}
+	}
+
+	if(so->curOffset < 0)
+		so->isUsed=0;  //This is OK
+
+	s = s * (so->curOffset >> 22);
+	s = s>>6;
+
+
+	if(so->state == STATE_RAMPDOWN)
+	{
+		so->decay--;
+		if(so->decay == 0)
+			so->isUsed=0;
+	}
+
+	s = s * so->decay; s = s >> 9;
+
+	return s*((signed short int)so->vol*(signed short int)chVol[so->ch])>>14;
+}
+
+
+
+int mhL[16];
+int mhR[16];
+int mp=0;	//Mix position, for circular array
+//	Was stuff for Ghetto Lowpass Filter, now deprecated.
+
+
+inline void synthSample(int * mixL, int * mixR)
+{
+	int a=0;
+	signed long int leftMix=0, rightMix=0, sample=0;
+	for(a=0; a<MAX_VOICES; a++)
+	{
+		if(voices[a].isUsed==1)
+		{
+			sample = synthVoice(a);
+
+			leftMix += (sample*chPanLeft[voices[a].ch])>>7;
+			rightMix += (sample*chPanRight[voices[a].ch])>>7;
+		}
+	}
+
+	//TODO: Automatic Gain Control, anyone?
+	//Or, should this be implemented on the DSP's output volume instead?
+	*mixL = leftMix;
+	*mixR = rightMix;
+
+	return;  //No more ghetto lowpass filter.. linear intrpolation works well.
+
+
+	// HACK HACK HACK
+	// This is the infamous Ghetto Lowpass Filter
+	// Now that I have linear interpolation, it should not be needed anymore.
+	/*
+	mp++;
+	if(mp==4)
+		mp=0;
+
+	mhL[mp]=leftMix;
+	mhR[mp]=rightMix;
+
+	*mixL = 0;
+	*mixR = 0;
+
+
+	for(a=0; a<4; a++)
+	{
+		*mixL += mhL[a];
+		*mixR += mhR[a];
+	}
+	*mixL = *mixL>>4;
+	*mixR = *mixR>>4;
+	*/
+	// END HACK END HACK END HACK
+}
diff --git a/apps/plugins/midi2wav.c b/apps/plugins/midi2wav.c
new file mode 100644
index 0000000..f836853
--- /dev/null
+++ b/apps/plugins/midi2wav.c
@@ -0,0 +1,168 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2005 Stepan Moskovchenko
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#define SAMPLE_RATE 48000
+#define MAX_VOICES 100
+
+/*	This is for writing to the DSP directly from the Simulator
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/soundcard.h>
+#include <sys/ioctl.h>
+*/
+
+#include "../../plugin.h"
+#include "midi/midiutil.c"
+#include "midi/guspat.h"
+#include "midi/guspat.c"
+#include "midi/sequencer.c"
+#include "midi/midifile.c"
+#include "midi/synth.c"
+
+
+
+//#include "lib/xxx2wav.h"
+
+int fd=-1; //File descriptor, for opening /dev/dsp and writing to it
+
+extern long tempo; //The sequencer keeps track of this
+
+
+struct plugin_api * rb;
+
+
+
+enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
+{
+    TEST_PLUGIN_API(api);
+    (void)parameter;
+    rb = api;
+    rb->splash(HZ*2, true, "MIDI");
+    midimain();
+    rb->splash(HZ*2, true, "FINISHED PLAYING");
+    return PLUGIN_OK;
+}
+
+
+int midimain()
+{
+    	rb->splash(HZ*2, true, "OPENED DSP");
+	fd=rb->open("/dsp.raw", O_WRONLY|O_CREAT);
+/*
+        int arg, status;
+        int bit, samp, ch;
+
+        arg = 16;          // sample size
+        status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
+        status = ioctl(fd, SOUND_PCM_READ_BITS, &arg);
+        bit=arg;
+
+
+        arg = 2;        //Number of channels, 1=mono
+        status = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &arg);
+        status = ioctl(fd, SOUND_PCM_READ_CHANNELS, &arg);
+        ch=arg;
+
+        arg = SAMPLE_RATE; //Yeah. sampling rate
+        status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg);
+        status = ioctl(fd, SOUND_PCM_READ_RATE, &arg);
+        samp=arg;
+*/
+
+	printf("\nHello.\n");
+//	initSound();	//Open the computer's sound card
+	int a=0;
+
+	rb->splash(HZ*2, true, "LOADING MIDI");
+
+	struct MIDIfile * mf = loadFile("/test.mid");
+
+	rb->splash(HZ*2, true, "LOADED MIDI");
+	long bpm, nsmp, l;
+
+	int bp=0;
+
+	rb->splash(HZ*2, true, "LOADING PATCHES");
+	initSynth(mf, "/iriver2.cfg", "/drums.cfg");	//Initialize the MIDI syntehsizer
+	rb->splash(HZ*2, true, "START PLAYING");
+
+	signed char buf[3000];
+
+	// tick() will do one MIDI clock tick. Then, there's a loop here that
+	// will generate the right number of samples per MIDI tick. The whole
+	// MIDI playback is timed in terms of this value.. there are no forced
+	// delays or anything. It just produces enough samples for each tick, and
+	// the playback of these samples is what makes the timings right.
+	//
+	// This seems to work quite well.
+
+
+	printf("\nOkay, starting sequencing");
+
+	//Tick() will return 0 if there are no more events left to play
+	while(tick(mf))
+	{
+
+		//Some annoying math to compute the number of samples
+		//to syntehsize per each MIDI tick.
+		bpm=mf->div*1000000/tempo;
+		nsmp=SAMPLE_RATE/bpm;
+
+		//Yes we need to do this math each time because the tempo
+		//could have changed.
+
+		// On second thought, this can be moved to the event that
+		//recalculates the tempo, to save a little bit of CPU time.
+		for(l=0; l<nsmp; l++)
+		{
+			int s1, s2;
+
+			synthSample(&s1, &s2);
+
+
+			//16-bit audio because, well, it's better
+			// But really because ALSA's OSS emulation sounds extremely
+			//noisy and distorted when in 8-bit mode. I still do not know
+			//why this happens.
+			buf[bp]=s1&0XFF; // Low byte first
+			bp++;
+			buf[bp]=s1>>8;   //High byte second
+			bp++;
+
+			buf[bp]=s2&0XFF; // Low byte first
+			bp++;
+			buf[bp]=s2>>8;   //High byte second
+			bp++;
+
+
+			//As soon as we produce 2000 bytes of sound,
+			//write it to the sound card. Why 2000? I have
+			//no idea. It's 1 AM and I am dead tired.
+			if(bp>=2000)
+			{
+				rb->write(fd, buf, 2000);
+				bp=0;
+			}
+		}
+	}
+
+//	unloadFile(mf);
+	printf("\n");
+	rb->close(fd);
+	return 0;
+}