From 1515ff852224c822a6d3db8c458eab2c9037704f Mon Sep 17 00:00:00 2001 From: Stepan Moskovchenko Date: Mon, 15 Oct 2007 05:11:37 +0000 Subject: 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 --- apps/plugins/midi/midiplay.c | 14 +-- apps/plugins/midi/midiutil.c | 1 + apps/plugins/midi/midiutil.h | 4 +- apps/plugins/midi/sequencer.c | 78 ++++++++++++- apps/plugins/midi/synth.c | 261 +++++++++++++++++++++--------------------- 5 files changed, 218 insertions(+), 140 deletions(-) (limited to 'apps/plugins') diff --git a/apps/plugins/midi/midiplay.c b/apps/plugins/midi/midiplay.c index 99f05718d6..325d90c375 100644 --- a/apps/plugins/midi/midiplay.c +++ b/apps/plugins/midi/midiplay.c @@ -93,6 +93,7 @@ int numberOfSamples IBSS_ATTR; 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 @@ static inline void synthbuf(void) outptr=gmbuf; #endif - for(i=0; iwf=wf; unsigned int delta= 0; - delta = (((gustable[note]<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]<rootFreq)) * wf->sampRate / (SAMPLE_RATE)); + delta = (delta * pitchTbl[pitchOffset+256])>> 16; so->delta = delta; } @@ -280,6 +343,13 @@ static void sendEvent(struct Event * ev) 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 @@ static void sendEvent(struct Event * ev) 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 568c7bb1ce..f0fa93d60e 100644 --- a/apps/plugins/midi/synth.c +++ b/apps/plugins/midi/synth.c @@ -65,6 +65,7 @@ int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig) 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 @@ inline void stopVoice(struct SynthObject * so) 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) - { - if(so->decay != 0) /* Ramp has been started */ - { - so->decay = so->decay / 2; + const unsigned int num_samples = (wf->numSamples-1) << FRACTSIZE; - if(so->decay < 10 && so->decay > -10) - so->isUsed = 0; + const unsigned int end_loop = wf->endLoop << FRACTSIZE; + const unsigned int start_loop = wf->startLoop << FRACTSIZE; + const int diff_loop = end_loop-start_loop; - return so->decay; - } - } else /* OK to advance voice */ + while(samples > 0) { - so->cp += so->delta; - } - - - cpShifted = so->cp >> FRACTSIZE; + samples--; + /* Is voice being ramped? */ + if(so->state == STATE_RAMPDOWN) + { + if(so->decay != 0) /* Ramp has been started */ + { + so->decay = so->decay / 2; + if(so->decay < 10 && so->decay > -10) + so->isUsed = 0; + 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; + } - 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) - { - cpShifted = wf->endLoop-(wf->startLoop-cpShifted); - so->cp = (cpShifted)<loopState == STATE_LOOPING && (cp_temp < start_loop)) { - so->delta = -so->delta; /* At this point cpShifted is wrong. We need to take a step */ - so->loopDir = LOOPDIR_FORWARD; + if(mode_mask_looprev) + { + cp_temp += diff_loop; + s2=getSample((cp_temp >> FRACTSIZE), wf); + } + else + { + so->delta = -so->delta; /* At this point cp_temp 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) + if(mode_mask28 && (cp_temp >= end_loop)) { - cpShifted = wf->startLoop + (cpShifted-wf->endLoop); - so->cp = (cpShifted)<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; + } } - else + + /* Have we overrun? */ + if(cp_temp >= num_samples) { - so->delta = -so->delta; - so->loopDir = LOOPDIR_REVERSE; + cp_temp -= so->delta; + s2 = getSample((cp_temp >> FRACTSIZE)+1, wf); + stopVoice(so); } - } - - /* 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); + /* Better, working, linear interpolation */ + s1=getSample((cp_temp >> FRACTSIZE), wf); + s = s1 + ((signed)((s2 - s1) * (cp_temp & ((1<>FRACTSIZE); - if(so->curRate == 0) - { - stopVoice(so); -// so->isUsed = 0; + 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) + if(so->ch != 9 && so->state != STATE_RAMPDOWN) /* Stupid ADSR code... and don't do ADSR for drums */ { - so->curOffset += (so->curRate); - if(so -> curOffset > so->targetOffset && so->curPoint != 2) + if(so->curOffset < so->targetOffset) { - if(so->curPoint != 5) + so->curOffset += (so->curRate); + if(so -> curOffset > so->targetOffset && so->curPoint != 2) { - setPoint(so, so->curPoint+1); + if(so->curPoint != 5) + { + setPoint(so, so->curPoint+1); + } + else + { + stopVoice(so); + } } - else + } else + { + so->curOffset -= (so->curRate); + if(so -> curOffset < so->targetOffset && so->curPoint != 2) { - stopVoice(so); + + if(so->curPoint != 5) + { + setPoint(so, so->curPoint+1); + } + else + { + stopVoice(so); + } + } } - } else + } + + if(so->curOffset < 0) { - so->curOffset -= (so->curRate); - if(so -> curOffset < so->targetOffset && so->curPoint != 2) - { + so->curOffset = so->targetOffset; + stopVoice(so); + } - if(so->curPoint != 5) - { - setPoint(so, so->curPoint+1); - } - else - { - 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.. */ } - } - if(so->curOffset < 0) - { - so->curOffset = so->targetOffset; - stopVoice(so); - } - s = (s * (so->curOffset >> 22) >> 8); + /* 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)); - /* 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) - { - dL=0; - dR=0; - voicept=&voices[0]; + struct SynthObject *voicept; + + rb->memset(samp_buf, 0, num_samples*4); - for(i=MAX_VOICES; i > 0; i--) + for(i=0; i < MAX_VOICES; 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)); + rb->memcpy(buf_ptr, samp_buf, num_samples*4); - buf_ptr++; - num_samples--; - } /* TODO: Automatic Gain Control, anyone? */ /* Or, should this be implemented on the DSP's output volume instead? */ -- cgit v1.2.3