/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2005 Stepan Moskovchenko * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include "midiutil.h" #include "guspat.h" #include "synth.h" extern struct plugin_api * rb; extern int playingTime IBSS_ATTR; extern int samplesThisSecond IBSS_ATTR; long tempo=375000; /* From the old patch config.... each patch is scaled. * Should be moved into patchset.cfg * But everyone would need a new config file. * * While this really does need to go into the patch config, * I doubt anyone has made their own custom rockbox patchset * (if you have, please send a copy my way :) ) */ static const unsigned char patchScale[]= { 125,115,115,100,100,80,115,100,100,100,100,80,100,100,100,100, 100,100,100,100,60,100,100,100,150,155,145,100,125,86,125,85, 161,160,133,160,135,133,100,128,150,100,100,150,100,130,100,200, 100,100,125,125,100,100,100,100,124,110,111,100,139,113,115,115, 125,115,95,140,100,100,105,100,90,100,80,80,100,125,100,80, 100,100,100,250,130,100,100,100,115,100,100,120,200,100,100,80, 130,100,100,150,100,100,100,100,100,100,200,100,100,100,100,100, 100,100,113,100,200,100,100,100,30,100,100,100,100,100,100,100 }; /* Sets the volume scaling by channel volume and note volume */ /* This way we can do the multiplication/indexing once per */ /* MIDI event at the most, instead of once per sample. */ static inline void setVolScale(int a) { struct SynthObject * so = &voices[a]; int ch = so->ch; so->volscale = so->vol * chVol[ch]*patchScale[chPat[ch]] / 100; //((signed short int)so->vol*(signed short int)chVol[ch])*patchScale[chPat[ch]]; } static inline void setVol(int ch, int vol) { int a=0; chVol[ch]=vol; /* If channel volume changes, we need to recalculate the volume scale */ /* factor for all voices active on this channel */ for(a=0; awaveforms[patchSet[chPat[ch]]->noteTable[note]]; so->wf=wf; /* Used to be unsigned int, but math had to be done in different order to avoid overflow */ unsigned long long delta= 0; /* Old formula: delta = (((gustable[note+chPBNoteOffset[ch]]<rootFreq)) * wf->sampRate / (SAMPLE_RATE)); Plus some pitch stuff. See old SVN for how it used to be */ delta = (((gustable[note+chPBNoteOffset[ch]]))); /* anywhere from 8000 to 8000000 */ delta = delta * wf->sampRate; /* approx 20000 - 44000 but can vary with tuning */ delta = (delta * chPBFractBend[ch]); /* approx 60000 - 70000 */ delta = delta / (SAMPLE_RATE); /* 44100 or 22050 */ delta = delta / (wf->rootFreq); /* anywhere from 8000 to 8000000 */ /* Pitch bend is encoded as a fractional of 16 bits, hence the 16 */ delta = delta >> (16 - FRACTSIZE); /* a shift of approx 4 bits */ so->delta = delta; } static inline void computeDeltas(int ch) { int a=0; for(a = 0; a>5; int totalBend = (chPW[ch]-256) * chPBDepth[ch]; chPBNoteOffset[ch] = totalBend >> 8; chPBFractBend[ch] = pitchTbl[(totalBend & 0xFF) + 256]; computeDeltas(ch); } inline void pressNote(int ch, int note, int vol) { static int lastKill = 0; /* Silences all channels but one, for easy debugging, for me. */ /* if(ch == 0) return; if(ch == 1) return; if(ch == 2) return; if(ch == 3) return; if(ch == 4) return; if(ch == 5) return; if(ch == 6) return; if(ch == 7) return; if(ch == 8) return; if(ch == 9) return; if(ch == 10) return; if(ch == 11) return; if(ch == 12) return; if(ch == 13) return; if(ch == 14) return; if(ch == 15) return; */ int a=0; for(a=0; awaveforms[0]; voices[a].wf=wf; voices[a].delta = (((gustable[note]<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=true; setPoint(&voices[a], 0); } else { /* printf("\nWarning: drum %d does not have a patch defined... Ignoring it", note); */ } } } static void releaseNote(int ch, int note) { if(ch==9) return; int a=0; for(a=0; amode & 28)) { setPoint(&voices[a], 3); } } } } static void sendEvent(struct Event * ev) { const unsigned char status_low = ev->status & 0x0F; const unsigned char d1 = ev->d1; const unsigned char d2 = ev->d2; switch(ev->status & 0xF0) { case MIDI_CONTROL: switch(d1) { case CTRL_VOLUME: { setVol((status_low), d2); return; } case CTRL_PANNING: { chPan[status_low]=d2; return; } case CTRL_DATAENT_MSB: { /* TODO: Update all deltas. Is this really needed? */ if(chLastCtrlMSB[status_low] == REG_PITCHBEND_MSB && chLastCtrlLSB[status_low] == REG_PITCHBEND_LSB) { // printf("Pitch bend depth set to %d\n", d2); chPBDepth[status_low] = d2; } return; } case CTRL_NONREG_LSB: { chLastCtrlLSB[status_low] = 0xFF; /* Ignore nonregistered writes */ return; } case CTRL_NONREG_MSB: { chLastCtrlMSB[status_low] = 0xFF; /* Ignore nonregistered writes */ return; } case CTRL_REG_LSB: { chLastCtrlLSB[status_low] = d2; return; } case CTRL_REG_MSB: { chLastCtrlMSB[status_low] = d2; return; } } break; case MIDI_PITCHW: setPW((status_low), d2, d1); return; case MIDI_NOTE_ON: switch(d2) { case 0: /* Release by vol=0 */ releaseNote(status_low, d1); return; default: pressNote(status_low, d1, d2); return; } case MIDI_NOTE_OFF: releaseNote(status_low, d1); return; case MIDI_PRGM: if((status_low) != 9) setPatch(status_low, d1); } } void rewindFile(void) { int i=0; for(i=0; inumTracks; i++) { mf->tracks[i]->delta = 0; mf->tracks[i]->pos = 0; } } int tick(void) ICODE_ATTR; void seekBackward(int nsec) { int notesUsed = 0, a=0; int desiredTime = playingTime - nsec; /* Rewind 5 sec */ if(desiredTime < 0) desiredTime = 0; /* Set controllers to default values */ resetControllers(); /* Set the tempo to defalt */ bpm=mf->div*1000000/tempo; numberOfSamples=SAMPLE_RATE/bpm; /* Reset the tracks to start */ rewindFile(); /* Reset the time counter to 0 */ playingTime = 0; samplesThisSecond = 0; /* Quickly run through any initial things that occur before notes */ do { notesUsed = 0; for(a=0; anumTracks; a++) { struct Track * tr = mf->tracks[a]; if(tr == NULL) printf("NULL 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) { 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); */ bpm=mf->div*1000000/tempo; numberOfSamples=SAMPLE_RATE/bpm; } } tr->delta = 0; tr->pos++; if(tr->pos>=(tr->numEvents-1)) break; } } } samplesThisSecond += numberOfSamples; while(samplesThisSecond >= SAMPLE_RATE) { samplesThisSecond -= SAMPLE_RATE; playingTime++; // printf("Time: %d sec\n", playingTime); } if(tracksAdv != 0) return 1; else return 0; }