I know that the specification has parameters that specify percentages(0-100%) of Chorus and Reverb, but how do I apply them to a running sample? I would need to render the samples and volume negatively compared to the original stream(the sample stream read from the loaded soundfont that has the volume envelope and looping applied to it. This one has to be the first to finish making sound(during release/finish) in order for the volume envelope to create valid tails using the same volume envelope.
Thus:
Original stream(raw sound) sample&volume envelope position >= duplicate(delayed/chorus/reverb) streams sample&volume envelope position.
Of course, the samples themselves can be modulated according to pitch and effects, but the origin position must still apply(the position which isn't modulated, i.o.w. the output sample position).
The problem is that the Soundfont 2.04 spec only specifies a number that goes from 0%-100%. How do I convert the sample stream to use that value(combined with the continuous controllers for reverb and chorus) to produce the correct output stream with the effects? I can multiply the origin position with a <=1.0 number to apply them, as well as substract time(as long as it's lower or equal to the origin stream position), but what is the correct way to handle it?
I've tried to implement the chorus and reverb into my Soundfont sound generation module, but I keep getting sound which sounds strange:
Initialization executed during when a note ON event is received:
1 //Chorus percentage 2 panningtemp = 0.0f; //Default to none! 3 if (lookupSFInstrumentModGlobal(soundfont, instrumentptr.genAmount.wAmount, ibag, 0x00DD, &applymod)) //Gotten panning modulator? 4 { 5 panningtemp = (float)applymod.modAmount; //Get the amount specified! 6 panningtemp *= 0.0002f; //Make into a percentage, it's in 0.02% units! 7 } 8 9 for (chorusreverbdepth=1;chorusreverbdepth<0x100;chorusreverbdepth++) //Process all possible chorus depths! 10 { 11 voice->chorusdepth[chorusreverbdepth] = powf(panningtemp,(float)chorusreverbdepth); //Apply the volume! 12 } 13 voice->chorusdepth[0] = 0.0; //Always none at the original level! 14 15 //Reverb percentage 16 panningtemp = 0.0f; //Default to none! 17 if (lookupSFInstrumentModGlobal(soundfont, instrumentptr.genAmount.wAmount, ibag, 0x00DB, &applymod)) //Gotten panning modulator? 18 { 19 panningtemp = (float)applymod.modAmount; //Get the amount specified! 20 panningtemp *= 0.0002f; //Make into a percentage, it's in 0.02% units! 21 } 22 for (chorusreverbdepth=0;chorusreverbdepth<0x100;chorusreverbdepth++) //Process all possible chorus depths! 23 { 24 for (chorusreverbchannel=0;chorusreverbchannel<4;chorusreverbchannel++) //Process all channels! 25 { 26 if (chorusreverbdepth==0) 27 { 28 voice->reverbdepth[chorusreverbdepth][chorusreverbchannel] = 0.0; //Nothing at the main channel! 29 } 30 else //Valid depth? 31 { 32 voice->reverbdepth[chorusreverbdepth][chorusreverbchannel] = (float)dB2factor((pow(panningtemp, chorusreverbdepth),chorusreverbchannel),1.0); //Apply the volume! 33 } 34 } 35 } 36 37 voice->currentchorusdepth = channel->choruslevel; //Current chorus depth! 38 voice->currentreverbdepth = channel->reverblevel; //Curernt reverb depth! 39 40 for (chorusreverbchannel=0;chorusreverbchannel<4;++chorusreverbchannel) //Process all reverb&chorus channels, precalculating every used value! 41 { 42 voice->activechorusdepth[chorusreverbchannel] = voice->chorusdepth[voice->currentchorusdepth]; //The chorus feedback strength for that channel! 43 voice->activereverbdepth[chorusreverbchannel] = voice->reverbdepth[voice->currentreverbdepth][chorusreverbchannel]; //The 44 } 45 46 //Setup default channel chorus/reverb! 47 voice->activechorusdepth[0] = 1.0; //Always the same: produce full sound! 48 voice->activereverbdepth[0] = 1.0; //Always the same: produce full sound!
Voice rendering routine:
1/* 2 3Voice support 4 5*/ 6 7OPTINLINE static void MIDIDEVICE_getsample(int_32 *leftsample, int_32 *rightsample, int_64 play_counter, float samplerate, float samplespeedup, MIDIDEVICE_VOICE *voice, float Volume, float Modulation, byte chorus, byte reverb, float chorusvol, float reverbvol) //Get a sample from an MIDI note! 8{ 9 //Our current rendering routine: 10 INLINEREGISTER uint_32 temp; 11 INLINEREGISTER int_64 samplepos; 12 float lchannel, rchannel; //Both channels to use! 13 byte loopflags; //Flags used during looping! 14 static sword readsample = 0; //The sample retrieved! 15 16 samplepos = play_counter; //Load the current play counter! 17 samplepos -= (int_64)(chorus_delay[chorus]*samplerate); //Apply specified chorus delay! 18 samplepos -= (int_64)(reverb_delay[reverb]*samplerate); //Apply specified reverb delay! 19 samplepos = (int_64)(samplepos*samplespeedup); //Affect speed through cents and other global factors! 20 if (voice->modenv_pitchfactor) //Gotten a modulation envelope to process to the pitch? 21 { 22 samplepos = (int_64)(samplepos*cents2samplesfactor(voice->modenv_pitchfactor)); //Apply the pitch bend to the sample to retrieve! 23 } 24 if (chorus) //Chorus has modulation of the pitch as well? 25 { 26 samplepos = (int_64)(samplepos*cents2samplesfactor((float)sin((double)play_counter*0.01)*(chorus*(1.0/256.0))+(1200.0f-1.0f))); //Apply the pitch bend to the sample to retrieve! 27 } 28 29 samplepos += voice->startaddressoffset; //The start of the sample! 30 31 //First: apply looping! 32 loopflags = voice->currentloopflags; 33 if (voice->has_finallooppos && (play_counter >= voice->finallooppos)) //Executing final loop? 34 { 35 samplepos -= voice->finallooppos; //Take the relative offset to the start of the final loop! 36 samplepos += voice->finallooppos_playcounter; //Add the relative offset to the start of our data of the final loop! 37 } 38 else if (loopflags & 1) //Currently looping and active? 39 { 40 if (samplepos >= voice->endloopaddressoffset) //Past/at the end of the loop! 41 { 42 if ((loopflags & 0xC2) == 0x82) //We're depressed, depress action is allowed (not holding) and looping until depressed? 43 { 44 if (!voice->has_finallooppos) //No final loop position set yet? 45 { 46 voice->currentloopflags &= ~0x80; //Clear depress bit! 47 //Loop for the last time! 48 voice->finallooppos = samplepos; //Our new position for our final execution till the end! 49 voice->has_finallooppos = 1; //We have a final loop position set! 50 loopflags |= 0x20; //We're to update our final loop start! 51 } 52 } 53 54 //Loop according to loop data! 55 temp = voice->startloopaddressoffset; //The actual start of the loop! 56 //Loop the data! 57 samplepos -= temp; //Take the ammount past the start of the loop! 58 samplepos %= voice->loopsize; //Loop past startloop by endloop! 59 samplepos += temp; //The destination position within the loop! 60 //Check for depress special actions!
…Show last 180 lines
61 if (loopflags&0x20) //Extra information needed for the final loop? 62 { 63 voice->finallooppos_playcounter = samplepos; //The start position within the loop to use at this point in time! 64 } 65 } 66 } 67 68 //Next, apply finish! 69 loopflags = (samplepos >= voice->endaddressoffset) || (play_counter<0); //Expired or not started yet? 70 if (loopflags) //Sound is finished? 71 { 72 //No sample! 73 return; //Done! 74 } 75 76 if (getSFSample16(soundfont, (uint_32)samplepos, &readsample)) //Sample found? 77 { 78 lchannel = (float)readsample; //Convert to floating point for our calculations! 79 80 //First, apply filters and current envelope! 81 applyMIDILowpassFilter(voice, &lchannel, Modulation); //Low pass filter! 82 lchannel *= Volume; //Apply ADSR Volume envelope! 83 lchannel *= voice->initialAttenuation; //The volume of the samples! 84 //Now the sample is ready for output into the actual final volume! 85 86 rchannel = lchannel; //Load into both channels! 87 //Now, apply panning! 88 lchannel *= voice->lvolume; //Apply left panning, also according to the CC! 89 rchannel *= voice->rvolume; //Apply right panning, also according to the CC! 90 91 lchannel *= VOLUME; 92 rchannel *= VOLUME; 93 94 //Clip the samples to prevent overflow! 95 if (lchannel>SHRT_MAX) lchannel = SHRT_MAX; 96 if (lchannel<SHRT_MIN) lchannel = SHRT_MIN; 97 if (rchannel>SHRT_MAX) rchannel = SHRT_MAX; 98 if (rchannel<SHRT_MIN) rchannel = SHRT_MIN; 99 100 lchannel *= chorusvol; //Apply chorus volume for this stream! 101 rchannel *= chorusvol; //Apply chorus volume for this stream! 102 103 lchannel *= reverbvol; //Apply reverb volume for this stream! 104 rchannel *= reverbvol; //Apply reverb volume for this stream! 105 106 //Give the result! 107 *leftsample += (sword)lchannel; //LChannel! 108 *rightsample += (sword)rchannel; //RChannel! 109 } 110} 111 112byte MIDIDEVICE_renderer(void* buf, uint_32 length, byte stereo, void *userdata) //Sound output renderer! 113{ 114#ifdef __HW_DISABLED 115 return 0; //We're disabled! 116#endif 117 if (!stereo) return 0; //Can't handle non-stereo output! 118 //Initialisation info 119 float pitchcents, currentsamplespeedup, lvolume, rvolume, panningtemp; 120 INLINEREGISTER float VolumeEnvelope=0; //Current volume envelope data! 121 INLINEREGISTER float ModulationEnvelope=0; //Current modulation envelope data! 122 //Initialised values! 123 MIDIDEVICE_VOICE *voice = (MIDIDEVICE_VOICE *)userdata; 124 sample_stereo_t* ubuf = (sample_stereo_t *)buf; //Our sample buffer! 125 ADSR *VolumeADSR = &voice->VolumeEnvelope; //Our used volume envelope ADSR! 126 ADSR *ModulationADSR = &voice->ModulationEnvelope; //Our used modulation envelope ADSR! 127 MIDIDEVICE_CHANNEL *channel = voice->channel; //Get the channel to use! 128 uint_32 numsamples = length; //How many samples to buffer! 129 ++numsamples; //Take one sample more! 130 byte currentchorusreverb; //Current chorus and reverb levels we're processing! 131 132 #ifdef MIDI_LOCKSTART 133 //lock(voice->locknumber); //Lock us! 134 #endif 135 136 if (voice->VolumeEnvelope.active==0) //Simple check! 137 { 138 #ifdef MIDI_LOCKSTART 139 //unlock(voice->locknumber); //Lock us! 140 #endif 141 return SOUNDHANDLER_RESULT_NOTFILLED; //Empty buffer: we're unused! 142 } 143 if (memprotect(soundfont,sizeof(*soundfont),"RIFF_FILE")!=soundfont) 144 { 145 #ifdef MIDI_LOCKSTART 146 //unlock(voice->locknumber); //Lock us! 147 #endif 148 return SOUNDHANDLER_RESULT_NOTFILLED; //Empty buffer: we're unable to render anything! 149 } 150 if (!soundfont) 151 { 152 #ifdef MIDI_LOCKSTART 153 //unlock(voice->locknumber); //Lock us! 154 #endif 155 return SOUNDHANDLER_RESULT_NOTFILLED; //The same! 156 } 157 if (!channel) //Unknown channel? 158 { 159 #ifdef MIDI_LOCKSTART 160 //unlock(voice->locknumber); //Lock us! 161 #endif 162 return SOUNDHANDLER_RESULT_NOTFILLED; //The same! 163 } 164 165 166 #ifdef MIDI_LOCKSTART 167 lock(voice->locknumber); //Actually check! 168 #endif 169 170 if (voice->VolumeEnvelope.active == 0) //Not active after all? 171 { 172 #ifdef MIDI_LOCKSTART 173 unlock(voice->locknumber); 174 #endif 175 return SOUNDHANDLER_RESULT_NOTFILLED; //Empty buffer: we're unused! 176 } 177 //Calculate the pitch bend speedup! 178 pitchcents = (float)(channel->pitch%0x1FFF); //Load active pitch bend (unsigned), Only low 14 bits are used! 179 pitchcents -= (float)0x2000; //Convert to a signed value! 180 pitchcents /= 128.0f; //Create a value between -1 and 1! 181 pitchcents *= (float)cents2samplesfactor(voice->pitchwheelmod*pitchcents); //Influence by pitch wheel! 182 183 //Now apply to the default speedup! 184 currentsamplespeedup = voice->initsamplespeedup; //Load the default sample speedup for our tone! 185 currentsamplespeedup *= (float)cents2samplesfactor(pitchcents); //Calculate the sample speedup!; //Apply pitch bend! 186 voice->effectivesamplespeedup = currentsamplespeedup; //Load the speedup of the samples we need! 187 188 //Determine panning! 189 lvolume = rvolume = 0.5f; //Default to 50% each (center)! 190 panningtemp = voice->initpanning; //Get the panning specified! 191 panningtemp += voice->panningmod*((float)(voice->channel->panposition-0x2000)/128); //Apply panning CC! 192 lvolume -= panningtemp; //Left percentage! 193 rvolume += panningtemp; //Right percentage! 194 voice->lvolume = lvolume; //Left panning! 195 voice->rvolume = rvolume; //Right panning! 196 197 if (voice->request_off) //Requested turn off? 198 { 199 voice->currentloopflags |= 0x80; //Request quit looping if needed: finish sound! 200 } //Requested off? 201 202 //Apply sustain 203 voice->currentloopflags &= ~0x40; //Sustain disabled by default! 204 voice->currentloopflags |= (channel->sustain << 6); //Sustaining? 205 206 VolumeEnvelope = voice->CurrentVolumeEnvelope; //Make sure we don't clear! 207 ModulationEnvelope = voice->CurrentModulationEnvelope; //Make sure we don't clear! 208 209 int_32 lchannel, rchannel; //Left&right samples, big enough for all chorus and reverb to be applied! 210 211 //Now produce the sound itself! 212 for (; --numsamples;) //Produce the samples! 213 { 214 lchannel = 0; //Reset left channel! 215 rchannel = 0; //Reset right channel! 216 for (currentchorusreverb=0;currentchorusreverb<16;++currentchorusreverb) //Process all reverb&chorus used(4 chorus channels within 4 reverb channels)! 217 { 218 VolumeEnvelope = ADSR_tick(VolumeADSR,voice->play_counter,((voice->currentloopflags & 0xC0) != 0x80),voice->note->noteon_velocity, voice->note->noteoff_velocity); //Apply Volume Envelope! 219 ModulationEnvelope = ADSR_tick(ModulationADSR,voice->play_counter,((voice->currentloopflags & 0xC0) != 0x80),voice->note->noteon_velocity, voice->note->noteoff_velocity); //Apply Modulation Envelope! 220 MIDIDEVICE_getsample(&lchannel,&rchannel, voice->play_counter, (float)voice->sample.dwSampleRate, voice->effectivesamplespeedup, voice, VolumeEnvelope, ModulationEnvelope, (byte)(currentchorusreverb&0x3), (byte)(currentchorusreverb>>2),voice->activechorusdepth[(currentchorusreverb&3)],voice->activereverbdepth[(currentchorusreverb>>3)]); //Get the sample from the MIDI device! 221 } 222 //Clip the samples to prevent overflow! 223 if (lchannel>SHRT_MAX) lchannel = SHRT_MAX; 224 if (lchannel<SHRT_MIN) lchannel = SHRT_MIN; 225 if (rchannel>SHRT_MAX) rchannel = SHRT_MAX; 226 if (rchannel<SHRT_MIN) rchannel = SHRT_MIN; 227 ubuf->l = lchannel; //Left sample! 228 ubuf->r = rchannel; //Right sample! 229 ++voice->play_counter; //Next sample! 230 ++ubuf; //Prepare for the next sample! 231 } 232 233 voice->CurrentVolumeEnvelope = VolumeEnvelope; //Current volume envelope updated! 234 voice->CurrentModulationEnvelope = ModulationEnvelope; //Current volume envelope updated! 235 236 #ifdef MIDI_LOCKSTART 237 unlock(voice->locknumber); //Lock us! 238 #endif 239 return SOUNDHANDLER_RESULT_FILLED; //We're filled! 240}