From 215e492a12275c8bf3399c9d40eee0dcbc7c04c0 Mon Sep 17 00:00:00 2001 From: Stepan Moskovchenko Date: Fri, 15 Apr 2005 06:08:55 +0000 Subject: Added MIDI synthesizer plugin git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6287 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/SOURCES | 1 + apps/plugins/midi/guspat.c | 151 +++++++++++++++ apps/plugins/midi/guspat.h | 94 +++++++++ apps/plugins/midi/midifile.c | 305 ++++++++++++++++++++++++++++++ apps/plugins/midi/midiutil.c | 262 +++++++++++++++++++++++++ apps/plugins/midi/sequencer.c | 298 +++++++++++++++++++++++++++++ apps/plugins/midi/synth.c | 430 ++++++++++++++++++++++++++++++++++++++++++ apps/plugins/midi2wav.c | 168 +++++++++++++++++ 8 files changed, 1709 insertions(+) create mode 100644 apps/plugins/midi/guspat.c create mode 100644 apps/plugins/midi/guspat.h create mode 100644 apps/plugins/midi/midifile.c create mode 100644 apps/plugins/midi/midiutil.c create mode 100644 apps/plugins/midi/sequencer.c create mode 100644 apps/plugins/midi/synth.c create mode 100644 apps/plugins/midi2wav.c diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 3dd7eb473f..50b523576f 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -74,5 +74,6 @@ flac2wav.c 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 0000000000..f0f1effd2a --- /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; anumWaveforms; 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; anumWaves; 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 0000000000..5007c4d2ca --- /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 0000000000..08efb73529 --- /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; alen; 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; anumTracks; a++) + { + int b=0; + + if(mf->tracks[a] != NULL) + for(b=0; btracks[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 0000000000..0ab5a35a78 --- /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 0000000000..fb98fdaf51 --- /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; arootFreq; + 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; amode & 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; anumTracks; 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 0000000000..0d07ed91d0 --- /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; anumTracks; a++) + { + int ts=0; + + if(mf->tracks[a] == NULL) + { + printf("\nNULL TRACK !!!"); + exit(1); + return; + } + + for(ts=0; tstracks[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>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 0000000000..f8368535d1 --- /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 +#include +#include +#include +*/ + +#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>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; +} -- cgit v1.2.3