MIDI: At long last, though quick and dirty, pitch bend depth! Or, I think it works. Tested on two 
files. Let me know if anyone discovers any problems with this. This commit also includes Nils's synth 
loop optimization patch. I hope committing it does not cause problems.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15112 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/plugins/midi/midiplay.c b/apps/plugins/midi/midiplay.c
index 99f0571..325d90c 100644
--- a/apps/plugins/midi/midiplay.c
+++ b/apps/plugins/midi/midiplay.c
@@ -93,6 +93,7 @@
 long bpm IBSS_ATTR;
 
 int32_t gmbuf[BUF_SIZE*NBUF];
+static unsigned int samples_in_buf;
 
 int quit=0;
 struct plugin_api * rb;
@@ -160,7 +161,8 @@
     outptr=gmbuf;
 #endif
 
-    for(i=0; i<BUF_SIZE/numberOfSamples; i++)
+    /* synth samples for as many whole ticks as we can fit in the buffer */
+    for(i=0; i < BUF_SIZE/numberOfSamples; i++)
     {
         synthSamples((int32_t*)outptr, numberOfSamples);
         outptr += numberOfSamples;
@@ -168,11 +170,9 @@
             quit=1;
     }
 
-    if(BUF_SIZE%numberOfSamples)
-    {
-        synthSamples((int32_t*)outptr, BUF_SIZE%numberOfSamples);
-        outptr += BUF_SIZE%numberOfSamples;
-    }
+    /* how many samples did we write to the buffer? */
+    samples_in_buf = BUF_SIZE-(BUF_SIZE%numberOfSamples);
+
 }
 
 void get_more(unsigned char** start, size_t* size)
@@ -187,7 +187,7 @@
     synthbuf();  // For some reason midiplayer crashes when an update is forced
 #endif
 
-    *size = sizeof(gmbuf)/NBUF;
+    *size = samples_in_buf*sizeof(int32_t);
 #ifndef SYNC
     *start = (unsigned char*)((swap ? gmbuf : gmbuf + BUF_SIZE));
     swap=!swap;
diff --git a/apps/plugins/midi/midiutil.c b/apps/plugins/midi/midiutil.c
index aba56c5..8cf8ffc 100644
--- a/apps/plugins/midi/midiutil.c
+++ b/apps/plugins/midi/midiutil.c
@@ -25,6 +25,7 @@
 int chPan[16] IBSS_ATTR;       /* Channel panning               */
 int chPat[16] IBSS_ATTR;                  /* Channel patch                 */
 int chPW[16] IBSS_ATTR;                   /* Channel pitch wheel, MSB only */
+int chPBDepth[16] IBSS_ATTR;                   /* Channel pitch wheel, MSB only */
 
 struct GPatch * gusload(char *);
 struct GPatch * patchSet[128];
diff --git a/apps/plugins/midi/midiutil.h b/apps/plugins/midi/midiutil.h
index a94c257..9117744 100644
--- a/apps/plugins/midi/midiutil.h
+++ b/apps/plugins/midi/midiutil.h
@@ -63,7 +63,8 @@
 #define MIDI_PITCHW 224
 
 /* MIDI Controllers */
-#define CTRL_VOLUME 7
+#define CTRL_PWDEPTH    6
+#define CTRL_VOLUME     7
 #define CTRL_BALANCE    8
 #define CTRL_PANNING    10
 #define CHANNEL     1
@@ -159,6 +160,7 @@
 extern int chPan[16];       /* Channel panning               */
 extern int chPat[16];       /* Channel patch                 */
 extern int chPW[16];        /* Channel pitch wheel, MSB only */
+extern int chPBDepth[16];   /* Channel pitch bend depth (Controller 6 */
 
 extern struct GPatch * gusload(char *);
 extern struct GPatch * patchSet[128];
diff --git a/apps/plugins/midi/sequencer.c b/apps/plugins/midi/sequencer.c
index 1a00c07..638c9ba 100644
--- a/apps/plugins/midi/sequencer.c
+++ b/apps/plugins/midi/sequencer.c
@@ -75,7 +75,60 @@
 };
 */
 
+
+/* 512 entries here */
+/*
+    for i=0:512, fprintf('%d,', round(2^16*2^((i-256)/1536))); end
+*/
+
 const uint32_t pitchTbl[] ICONST_ATTR={
+    61858,61872,61886,61900,61914,61928,61942,61956,61970,61983,61997,62011,
+    62025,62039,62053,62067,62081,62095,62109,62124,62138,62152,62166,62180,
+    62194,62208,62222,62236,62250,62264,62278,62292,62306,62320,62334,62348,
+    62362,62376,62390,62404,62419,62433,62447,62461,62475,62489,62503,62517,
+    62531,62545,62560,62574,62588,62602,62616,62630,62644,62658,62673,62687,
+    62701,62715,62729,62743,62757,62772,62786,62800,62814,62828,62843,62857,
+    62871,62885,62899,62913,62928,62942,62956,62970,62984,62999,63013,63027,
+    63041,63056,63070,63084,63098,63112,63127,63141,63155,63169,63184,63198,
+    63212,63227,63241,63255,63269,63284,63298,63312,63326,63341,63355,63369,
+    63384,63398,63412,63427,63441,63455,63470,63484,63498,63512,63527,63541,
+    63555,63570,63584,63599,63613,63627,63642,63656,63670,63685,63699,63713,
+    63728,63742,63757,63771,63785,63800,63814,63829,63843,63857,63872,63886,
+    63901,63915,63929,63944,63958,63973,63987,64002,64016,64030,64045,64059,
+    64074,64088,64103,64117,64132,64146,64161,64175,64190,64204,64219,64233,
+    64248,64262,64277,64291,64306,64320,64335,64349,64364,64378,64393,64407,
+    64422,64436,64451,64465,64480,64494,64509,64524,64538,64553,64567,64582,
+    64596,64611,64626,64640,64655,64669,64684,64699,64713,64728,64742,64757,
+    64772,64786,64801,64815,64830,64845,64859,64874,64889,64903,64918,64933,
+    64947,64962,64976,64991,65006,65020,65035,65050,65065,65079,65094,65109,
+    65123,65138,65153,65167,65182,65197,65211,65226,65241,65256,65270,65285,
+    65300,65315,65329,65344,65359,65374,65388,65403,65418,65433,65447,65462,
+    65477,65492,65506,65521,65536,65551,65566,65580,65595,65610,65625,65640,
+    65654,65669,65684,65699,65714,65729,65743,65758,65773,65788,65803,65818,
+    65832,65847,65862,65877,65892,65907,65922,65936,65951,65966,65981,65996,
+    66011,66026,66041,66056,66071,66085,66100,66115,66130,66145,66160,66175,
+    66190,66205,66220,66235,66250,66265,66280,66294,66309,66324,66339,66354,
+    66369,66384,66399,66414,66429,66444,66459,66474,66489,66504,66519,66534,
+    66549,66564,66579,66594,66609,66624,66639,66654,66670,66685,66700,66715,
+    66730,66745,66760,66775,66790,66805,66820,66835,66850,66865,66880,66896,
+    66911,66926,66941,66956,66971,66986,67001,67016,67032,67047,67062,67077,
+    67092,67107,67122,67137,67153,67168,67183,67198,67213,67228,67244,67259,
+    67274,67289,67304,67320,67335,67350,67365,67380,67395,67411,67426,67441,
+    67456,67472,67487,67502,67517,67532,67548,67563,67578,67593,67609,67624,
+    67639,67655,67670,67685,67700,67716,67731,67746,67761,67777,67792,67807,
+    67823,67838,67853,67869,67884,67899,67915,67930,67945,67961,67976,67991,
+    68007,68022,68037,68053,68068,68083,68099,68114,68129,68145,68160,68176,
+    68191,68206,68222,68237,68252,68268,68283,68299,68314,68330,68345,68360,
+    68376,68391,68407,68422,68438,68453,68468,68484,68499,68515,68530,68546,
+    68561,68577,68592,68608,68623,68639,68654,68670,68685,68701,68716,68732,
+    68747,68763,68778,68794,68809,68825,68840,68856,68871,68887,68902,68918,
+    68933,68949,68965,68980,68996,69011,69027,69042,69058,69074,69089,69105,
+    69120,69136,69152,69167,69183,69198,69214,69230,69245,69261,69276,69292,
+    69308,69323,69339,69355,69370,69386,69402,69417,69433
+
+};
+
+/*
    58386,58412,58439,58465,58491,58518,58544,58571,58597,58624,58650,58676,
    58703,58729,58756,58782,58809,58836,58862,58889,58915,58942,58968,58995,
    59022,59048,59075,59102,59128,59155,59182,59208,59235,59262,59289,59315,
@@ -119,7 +172,10 @@
    72507,72540,72573,72605,72638,72671,72704,72736,72769,72802,72835,72868,
    72901,72934,72967,72999,73032,73065,73098,73131,73164,73197,73230,73264,
    73297,73330,73363,73396,73429,73462,73495,73528
-};
+};*/
+
+
+
 
 static void findDelta(struct SynthObject * so, int ch, int note)
 {
@@ -128,8 +184,15 @@
     so->wf=wf;
     unsigned int delta= 0;
 
-    delta = (((gustable[note]<<FRACTSIZE) / (wf->rootFreq)) * wf->sampRate / (SAMPLE_RATE));
-    delta = (delta * pitchTbl[chPW[ch]])>> 16;
+    int totalBend = (chPW[ch]-256) * chPBDepth[ch];
+
+    int noteOffset = totalBend >> 8;
+
+    int pitchOffset = totalBend - (noteOffset<<8);
+
+
+    delta = (((gustable[note+noteOffset]<<FRACTSIZE) / (wf->rootFreq)) * wf->sampRate / (SAMPLE_RATE));
+    delta = (delta * pitchTbl[pitchOffset+256])>> 16;
 
     so->delta = delta;
 }
@@ -280,6 +343,13 @@
                     chPan[status_low]=d2;
                     return;
                 }
+                case CTRL_PWDEPTH:
+                {
+                    /* TODO: Update all deltas. Is this really needed? */
+                    chPBDepth[status_low] = d2;
+                    return;
+                }
+
             }
             break;
 
@@ -293,7 +363,7 @@
                 case 0: /* Release by vol=0 */
                     releaseNote(status_low, d1);
                     return;
-                    
+
                 default:
                     pressNote(status_low, d1, d2);
                     return;
diff --git a/apps/plugins/midi/synth.c b/apps/plugins/midi/synth.c
index 568c7bb..f0fa93d 100644
--- a/apps/plugins/midi/synth.c
+++ b/apps/plugins/midi/synth.c
@@ -65,6 +65,7 @@
         chPan[a]=64;             /* Center                          */
         chPat[a]=0;              /* Ac Gr Piano                     */
         chPW[a]=256;             /* .. not .. bent ?                */
+        chPBDepth[a]=2;          /* Default bend value is 2 */
     }
     for(a=0; a<128; a++)
     {
@@ -255,191 +256,195 @@
     so->decay = 0;
 }
 
-static inline int synthVoice(struct SynthObject * so)
+static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned int samples)
 {
     struct GWaveform * wf;
     register int s;
-    register unsigned int cpShifted;
-    register short s1;
-    register short s2;
+    register int s1;
+    register int s2;
+
+    register unsigned int cp_temp = so->cp;
 
     wf = so->wf;
 
+    const int mode_mask24 = wf->mode&24;
+    const int mode_mask28 = wf->mode&28;
+    const int mode_mask_looprev = wf->mode&LOOP_REVERSE;
 
-    /* Is voice being ramped? */
-    if(so->state == STATE_RAMPDOWN)
+    const unsigned int num_samples = (wf->numSamples-1) << FRACTSIZE;
+
+    const unsigned int end_loop = wf->endLoop << FRACTSIZE;
+    const unsigned int start_loop = wf->startLoop << FRACTSIZE;
+    const int diff_loop = end_loop-start_loop;
+
+    while(samples > 0)
     {
-        if(so->decay != 0)  /* Ramp has been started */
+        samples--;
+        /* Is voice being ramped? */
+        if(so->state == STATE_RAMPDOWN)
         {
-            so->decay = so->decay / 2;
+            if(so->decay != 0)  /* Ramp has been started */
+            {
+                so->decay = so->decay / 2;
 
-            if(so->decay < 10 && so->decay > -10)
-                so->isUsed = 0;
+                if(so->decay < 10 && so->decay > -10)
+                    so->isUsed = 0;
 
-            return so->decay;
+                s1=so->decay;
+                s2 = s1*chPan[so->ch];
+                s1 = (s1<<7) -s2;
+                *(out++)+=(((s1&0x7FFF80) << 9) | ((s2&0x7FFF80) >> 7));
+                continue;
+            }
+        } else  /* OK to advance voice */
+        {
+            cp_temp += so->delta;
         }
-    } else  /* OK to advance voice */
-    {
-        so->cp += so->delta;
-    }
 
-
-    cpShifted = so->cp >> FRACTSIZE;
-
-
-
-    s2 = getSample((cpShifted)+1, wf);
+        s2 = getSample((cp_temp >> FRACTSIZE)+1, wf);
 
         /* LOOP_REVERSE|LOOP_PINGPONG  = 24  */
-    if((wf->mode & (24)) && so->loopState == STATE_LOOPING && (cpShifted < (wf->startLoop)))
-    {
-        if(wf->mode & LOOP_REVERSE)
+        if(mode_mask24 && so->loopState == STATE_LOOPING && (cp_temp < start_loop))
         {
-            cpShifted = wf->endLoop-(wf->startLoop-cpShifted);
-            so->cp = (cpShifted)<<FRACTSIZE;
-            s2=getSample((cpShifted), wf);
-        }
-        else
-        {
-            so->delta = -so->delta; /* At this point cpShifted is wrong. We need to take a step */
-            so->loopDir = LOOPDIR_FORWARD;
-        }
-    }
-
-    if((wf->mode & 28) && (cpShifted >= wf->endLoop))
-    {
-        so->loopState = STATE_LOOPING;
-        if((wf->mode & (24)) == 0)
-        {
-            cpShifted = wf->startLoop + (cpShifted-wf->endLoop);
-            so->cp = (cpShifted)<<FRACTSIZE;
-            s2=getSample((cpShifted), wf);
-        }
-        else
-        {
-            so->delta = -so->delta;
-            so->loopDir = LOOPDIR_REVERSE;
-        }
-    }
-
-    /* Have we overrun? */
-    if( (cpShifted >= (wf->numSamples-1)))
-    {
-        so->cp -= so->delta;
-        cpShifted = so->cp >> FRACTSIZE;
-        s2 = getSample((cpShifted)+1, wf);
-        stopVoice(so);
-    }
-
-
-    /* Better, working, linear interpolation    */
-    s1=getSample((cpShifted), wf);
-
-    s = s1 + ((signed)((s2 - s1) * (so->cp & ((1<<FRACTSIZE)-1)))>>FRACTSIZE);
-
-
-    if(so->curRate == 0)
-    {
-        stopVoice(so);
-//        so->isUsed = 0;
-
-    }
-
-    if(so->ch != 9 && so->state != STATE_RAMPDOWN) /* 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(mode_mask_looprev)
             {
-                if(so->curPoint != 5)
-                {
-                    setPoint(so, so->curPoint+1);
-                }
-                else
-                {
-                    stopVoice(so);
-                }
+                cp_temp += diff_loop;
+                s2=getSample((cp_temp >> FRACTSIZE), wf);
             }
-        } else
-        {
-            so->curOffset -= (so->curRate);
-            if(so -> curOffset < so->targetOffset && so->curPoint != 2)
+            else
             {
-
-                if(so->curPoint != 5)
-                {
-                    setPoint(so, so->curPoint+1);
-                }
-                else
-                {
-                    stopVoice(so);
-                }
-
+                so->delta = -so->delta; /* At this point cp_temp is wrong. We need to take a step */
+                so->loopDir = LOOPDIR_FORWARD;
             }
         }
+
+        if(mode_mask28 && (cp_temp >= end_loop))
+        {
+            so->loopState = STATE_LOOPING;
+            if(!mode_mask24)
+            {
+                cp_temp -= diff_loop;
+                s2=getSample((cp_temp >> FRACTSIZE), wf);
+            }
+            else
+            {
+                so->delta = -so->delta;
+                so->loopDir = LOOPDIR_REVERSE;
+            }
+        }
+
+        /* Have we overrun? */
+        if(cp_temp >= num_samples)
+        {
+            cp_temp -= so->delta;
+            s2 = getSample((cp_temp >> FRACTSIZE)+1, wf);
+            stopVoice(so);
+        }
+
+        /* Better, working, linear interpolation    */
+        s1=getSample((cp_temp >> FRACTSIZE), wf);
+
+        s = s1 + ((signed)((s2 - s1) * (cp_temp & ((1<<FRACTSIZE)-1)))>>FRACTSIZE);
+
+        if(so->curRate == 0)
+        {
+            stopVoice(so);
+//          so->isUsed = 0;
+
+        }
+
+        if(so->ch != 9 && so->state != STATE_RAMPDOWN) /* 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->curOffset = so->targetOffset;
+            stopVoice(so);
+        }
+
+        s = (s * (so->curOffset >> 22) >> 8);
+
+        /* need to set ramp beginning */
+        if(so->state == STATE_RAMPDOWN && so->decay == 0)
+        {
+            so->decay = s*so->volscale>>14;
+            if(so->decay == 0)
+                so->decay = 1;  /* stupid junk.. */
+        }
+
+
+        /* Scaling by channel volume and note volume is done in sequencer.c */
+        /* That saves us some multiplication and pointer operations         */
+        s1=s*so->volscale>>14;
+
+        s2 = s1*chPan[so->ch];
+        s1 = (s1<<7) - s2;
+        *(out++)+=(((s1&0x7FFF80) << 9) | ((s2&0x7FFF80) >> 7));
+
     }
 
-    if(so->curOffset < 0)
-    {
-        so->curOffset = so->targetOffset;
-        stopVoice(so);
-    }
-
-    s = (s * (so->curOffset >> 22) >> 8);
-
-
-    /* need to set ramp beginning */
-    if(so->state == STATE_RAMPDOWN && so->decay == 0)
-    {
-        so->decay = s*so->volscale>>14;
-        if(so->decay == 0)
-            so->decay = 1;  /* stupid junk.. */
-    }
-
-
-    /* Scaling by channel volume and note volume is done in sequencer.c */
-    /* That saves us some multiplication and pointer operations         */
-    return s*so->volscale>>14;
+    so->cp=cp_temp; /* store this again */
+    return;
 }
 
+/* buffer to hold all the samples for the current tick, this is a hack
+   neccesary for coldfire targets as pcm_play_data uses the dma which cannot
+   access iram */
+int32_t samp_buf[256] IBSS_ATTR;
+
 /* synth num_samples samples and write them to the */
 /* buffer pointed to by buf_ptr                    */
 void synthSamples(int32_t *buf_ptr, unsigned int num_samples) ICODE_ATTR;
 void synthSamples(int32_t *buf_ptr, unsigned int num_samples)
 {
     int i;
-    register int dL;
-    register int dR;
-    register int sample;
-    register struct SynthObject *voicept;
-    while(num_samples>0)
+    struct SynthObject *voicept;
+
+    rb->memset(samp_buf, 0, num_samples*4);
+
+    for(i=0; i < MAX_VOICES; i++)
     {
-        dL=0;
-        dR=0;
-        voicept=&voices[0];
-
-        for(i=MAX_VOICES; i > 0; i--)
+        voicept=&voices[i];
+        if(voicept->isUsed==1)
         {
-            if(voicept->isUsed==1)
-            {
-                sample = synthVoice(voicept);
-                dL += sample;
-                sample *= chPan[voicept->ch];
-                dR += sample;
-            }
-            voicept++;
+            synthVoice(voicept, samp_buf, num_samples);
         }
-
-        dL = (dL << 7) - dR;
-
-        /* combine the left and right 16 bit samples into 32 bits and write */
-        /* to the buffer, left sample in the high word and right in the low word */
-        *buf_ptr=(((dL&0x7FFF80) << 9) | ((dR&0x7FFF80) >> 7));
-
-        buf_ptr++;
-        num_samples--;
     }
+
+    rb->memcpy(buf_ptr, samp_buf, num_samples*4);
+
     /* TODO: Automatic Gain Control, anyone? */
     /* Or, should this be implemented on the DSP's output volume instead? */