1
0
Fork 0
forked from len0rd/rockbox

Added MIDI synthesizer plugin

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6287 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Stepan Moskovchenko 2005-04-15 06:08:55 +00:00
parent 9ae63e4a3e
commit 215e492a12
8 changed files with 1709 additions and 0 deletions

View file

@ -74,5 +74,6 @@ flac2wav.c
vorbis2wav.c vorbis2wav.c
wv2wav.c wv2wav.c
mpc2wav.c mpc2wav.c
midi2wav.c
iriverify.c iriverify.c
#endif #endif

151
apps/plugins/midi/guspat.c Normal file
View file

@ -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;
}

View file

@ -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];
};

View file

@ -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);
}

View file

@ -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)
{
}

View file

@ -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;
}

430
apps/plugins/midi/synth.c Normal file
View file

@ -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
}

168
apps/plugins/midi2wav.c Normal file
View file

@ -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;
}