void SkidderEditor::rateDisplayConvert(float value, char *string, void *temporatestring) { if ( onOffTest(theTempoSync) ) { strcpy(string, (char*)temporatestring); if (strlen(string) <= 3) strcat(string, " cycles/beat"); } else sprintf(string, "%.3f Hz", rateScaled(value)); }
float SkidderEditor::calculateTheCycleRate() { float tempoBPS; // tempo sync is being used if ( onOffTest(effect->getParameter(kTempoSync)) ) { // "auto" mode; get the current tempo from the effect if ( effect->getParameter(kTempo) <= 0.0f ) tempoBPS = ((Skidder*)effect)->currentTempoBPS; // otherwise use the user-inputted tempo else tempoBPS = tempoScaled(effect->getParameter(kTempo)) / 60.0f; return ( tempoBPS * (((Skidder*)effect)->tempoRateTable->getScalar(effect->getParameter(kTempoRate))) ); } // tempo sync is not being used so just return the simple "free" rate value else return rateScaled(effect->getParameter(kRate)); }
//----------------------------------------------------------------------------- void BufferOverride::updateBuffer(long samplePos) { bool doSmoothing = true; // but in some situations, we shouldn't bool barSync = false; // true if we need to sync up with the next bar start float divisorLFOvalue, bufferLFOvalue; // the current output values of the LFOs long prevForcedBufferSize; // the previous forced buffer size // take care of MIDI heedBufferOverrideEvents(samplePos); readPos = 0; // reset for starting a new minibuffer prevMinibufferSize = minibufferSize; prevForcedBufferSize = currentForcedBufferSize; //--------------------------PROCESS THE LFOs---------------------------- // update the LFOs' positions to the current position divisorLFO->updatePosition(prevMinibufferSize); bufferLFO->updatePosition(prevMinibufferSize); // Then get the current output values of the LFOs, which also updates their positions once more. // Scale the 0.0 - 1.0 LFO output values to 0.0 - 2.0 (oscillating around 1.0). divisorLFOvalue = processLFOzero2two(divisorLFO); bufferLFOvalue = 2.0f - processLFOzero2two(bufferLFO); // inverting it makes more pitch sense // & then update the stepSize for each LFO, in case the LFO parameters have changed if (onOffTest(divisorLFO->fTempoSync)) divisorLFO->stepSize = currentTempoBPS * (tempoRateTable->getScalar(divisorLFO->fRate)) * numLFOpointsDivSR; else divisorLFO->stepSize = LFOrateScaled(divisorLFO->fRate) * numLFOpointsDivSR; if (onOffTest(bufferLFO->fTempoSync)) bufferLFO->stepSize = currentTempoBPS * (tempoRateTable->getScalar(bufferLFO->fRate)) * numLFOpointsDivSR; else bufferLFO->stepSize = LFOrateScaled(bufferLFO->fRate) * numLFOpointsDivSR; //---------------------------CALCULATE FORCED BUFFER SIZE---------------------------- // check if it's the end of this forced buffer if (writePos >= currentForcedBufferSize) { writePos = 0; // start up a new forced buffer // check on the previous forced & minibuffers; don't smooth if the last forced buffer wasn't divided if (prevMinibufferSize >= currentForcedBufferSize) doSmoothing = false; else doSmoothing = true; // now update the the size of the current force buffer if ( onOffTest(fBufferTempoSync) && // the user wants to do tempo sync / beat division rate (currentTempoBPS > 0.0f) ) // avoid division by zero { currentForcedBufferSize = (long) ( SAMPLERATE / (currentTempoBPS * tempoRateTable->getScalar(fBuffer)) ); // set this true so that we make sure to do the measure syncronisation later on if (needResync) barSync = true; } else currentForcedBufferSize = forcedBufferSizeSamples(fBuffer); // apply the buffer LFO to the forced buffer size currentForcedBufferSize = (long) ((float)currentForcedBufferSize * bufferLFOvalue); // really low tempos & tempo rate values can cause huge forced buffer sizes, // so prevent going outside of the allocated buffer space if (currentForcedBufferSize > SUPER_MAX_BUFFER) currentForcedBufferSize = SUPER_MAX_BUFFER; if (currentForcedBufferSize < 2) currentForcedBufferSize = 2; // untrue this so that we don't do the measure sync calculations again unnecessarily needResync = false; } //-----------------------CALCULATE THE DIVISOR------------------------- currentBufferDivisor = bufferDivisorScaled(fDivisor); // apply the divisor LFO to the divisor value if there's an "active" divisor (i.e. 2 or greater) if (currentBufferDivisor >= 2.0f) { currentBufferDivisor *= divisorLFOvalue; // now it's possible that the LFO could make the divisor less than 2, // which will essentially turn the effect off, so we stop the modulation at 2 if (currentBufferDivisor < 2.0f) currentBufferDivisor = 2.0f; } //-----------------------CALCULATE THE MINIBUFFER SIZE------------------------- // this is not a new forced buffer starting up if (writePos > 0) { // if it's allowed, update the minibuffer size midway through this forced buffer if (onOffTest(fBufferInterrupt)) minibufferSize = (long) ( (float)currentForcedBufferSize / currentBufferDivisor ); // if it's the last minibuffer, then fill up the forced buffer to the end // by extending this last minibuffer to fill up the end of the forced buffer long remainingForcedBuffer = currentForcedBufferSize - writePos; if ( (minibufferSize*2) >= remainingForcedBuffer ) minibufferSize = remainingForcedBuffer; } // this is a new forced buffer just beginning, act accordingly, do bar sync if necessary else { long samplesToBar; if (barSync) { samplesToBar = samplesToNextBar(timeInfo); // do beat sync for each LFO if it ought to be done if (onOffTest(divisorLFO->fTempoSync)) divisorLFO->syncToTheBeat(samplesToBar); if (onOffTest(bufferLFO->fTempoSync)) bufferLFO->syncToTheBeat(samplesToBar); } // because there isn't really any division (given my implementation) when the divisor is < 2 if (currentBufferDivisor < 2.0f) { if (barSync) minibufferSize = currentForcedBufferSize = samplesToBar % currentForcedBufferSize; else minibufferSize = currentForcedBufferSize; } else { minibufferSize = (long) ( (float)currentForcedBufferSize / currentBufferDivisor ); if (barSync) { // calculate how long this forced buffer needs to be long countdown = samplesToBar % currentForcedBufferSize; // update the forced buffer size & number of minibuffers so that // the forced buffers sync up with the musical measures of the song if ( countdown < (minibufferSize*2) ) // extend the buffer if it would be too short... currentForcedBufferSize += countdown; else // ...otherwise chop it down to the length of the extra bit needed to sync with the next measure currentForcedBufferSize = countdown; } } } //-----------------------CALCULATE SMOOTHING DURATION------------------------- // no smoothing if the previous forced buffer wasn't divided if (!doSmoothing) smoothcount = smoothDur = 0; else { smoothDur = (long) (fSmooth * (float)minibufferSize); long maxSmoothDur; // if we're just starting a new forced buffer, // then the samples beyond the end of the previous one are not valid if (writePos <= 0) maxSmoothDur = prevForcedBufferSize - prevMinibufferSize; // otherwise just make sure that we don't go outside of the allocated arrays else maxSmoothDur = SUPER_MAX_BUFFER - prevMinibufferSize; if (smoothDur > maxSmoothDur) smoothDur = maxSmoothDur; smoothcount = smoothDur; smoothStep = 1.0f / (float)(smoothDur+1); // the gain increment for each smoothing step // sqrtFadeIn = sqrtf(smoothStep); // sqrtFadeOut = sqrtf(1.0f - smoothStep); // smoothFract = smoothStep; fadeOutGain = cosf(PI/(float)(4*smoothDur)); fadeInGain = sinf(PI/(float)(4*smoothDur)); realFadePart = (fadeOutGain * fadeOutGain) - (fadeInGain * fadeInGain); // cosf(3.141592/2/n) imaginaryFadePart = 2.0f * fadeOutGain * fadeInGain; // sinf(3.141592/2/n) } }
//--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- void BufferOverride::doTheProcess(float **inputs, float **outputs, long sampleFrames, bool replacing) { //-------------------------SAFETY CHECK---------------------- #if LINUX // no memory allocations during interrupt #else // there must have not been available memory or something (like WaveLab goofing up), // so try to allocate buffers now if ( (buffer1 == NULL) #ifdef BUFFEROVERRIDE_STEREO || (buffer2 == NULL) #endif ) createAudioBuffers(); #endif // if the creation failed, then abort audio processing if (buffer1 == NULL) return; #ifdef BUFFEROVERRIDE_STEREO if (buffer2 == NULL) return; #endif //-------------------------INITIALIZATIONS---------------------- // this is a handy value to have during LFO calculations & wasteful to recalculate at every sample numLFOpointsDivSR = NUM_LFO_POINTS_FLOAT / SAMPLERATE; divisorLFO->pickTheLFOwaveform(); bufferLFO->pickTheLFOwaveform(); // calculate this scaler value to minimize calculations later during processOutput() // float inputGain = 1.0f - fDryWetMix; // float outputGain = fDryWetMix; float inputGain = sqrtf(1.0f - fDryWetMix); float outputGain = sqrtf(fDryWetMix); //-----------------------TEMPO STUFF--------------------------- // figure out the current tempo if we're doing tempo sync if ( onOffTest(fBufferTempoSync) || (onOffTest(divisorLFO->fTempoSync) || onOffTest(bufferLFO->fTempoSync)) ) { // calculate the tempo at the current processing buffer if ( (fTempo > 0.0f) || (hostCanDoTempo != 1) ) // get the tempo from the user parameter { currentTempoBPS = tempoScaled(fTempo) / 60.0f; needResync = false; // we don't want it true if we're not syncing to host tempo } else // get the tempo from the host { timeInfo = getTimeInfo(kBeatSyncTimeInfoFlags); if (timeInfo) { if (kVstTempoValid & timeInfo->flags) currentTempoBPS = (float)timeInfo->tempo / 60.0f; else currentTempoBPS = tempoScaled(fTempo) / 60.0f; // currentTempoBPS = ((float)tempoAt(reportCurrentPosition())) / 600000.0f; // but zero & negative tempos are bad, so get the user tempo value instead if that happens if (currentTempoBPS <= 0.0f) currentTempoBPS = tempoScaled(fTempo) / 60.0f; // // check if audio playback has just restarted & reset buffer stuff if it has (for measure sync) if (timeInfo->flags & kVstTransportChanged) { needResync = true; currentForcedBufferSize = 1; writePos = 1; minibufferSize = 1; prevMinibufferSize = 0; smoothcount = smoothDur = 0; } } else // do the same stuff as above if the timeInfo gets a null pointer { currentTempoBPS = tempoScaled(fTempo) / 60.0f; needResync = false; // we don't want it true if we're not syncing to host tempo } } } //-----------------------AUDIO STUFF--------------------------- // here we begin the audio output loop, which has two checkpoints at the beginning for (long samplecount = 0; (samplecount < sampleFrames); samplecount++) { // check if it's the end of this minibuffer if (readPos >= minibufferSize) updateBuffer(samplecount); // store the latest input samples into the buffers buffer1[writePos] = inputs[0][samplecount]; #ifdef BUFFEROVERRIDE_STEREO buffer2[writePos] = inputs[1][samplecount]; #endif // get the current output without any smoothing float out1 = buffer1[readPos]; #ifdef BUFFEROVERRIDE_STEREO float out2 = buffer2[readPos]; #endif // and if smoothing is taking place, get the smoothed audio output if (smoothcount > 0) { // crossfade between the current input & its corresponding overlap sample // out1 *= 1.0f - (smoothStep * (float)smoothcount); // current // out1 += buffer1[readPos+prevMinibufferSize] * smoothStep*(float)smoothcount; // + previous // float smoothfract = smoothStep * (float)smoothcount; // float newgain = sqrt(1.0f - smoothfract); // float oldgain = sqrt(smoothfract); // out1 = (out1 * newgain) + (buffer1[readPos+prevMinibufferSize] * oldgain); // out1 = (out1 * sqrtFadeIn) + (buffer1[readPos+prevMinibufferSize] * sqrtFadeOut); out1 = (out1 * fadeInGain) + (buffer1[readPos+prevMinibufferSize] * fadeOutGain); #ifdef BUFFEROVERRIDE_STEREO // out2 *= 1.0f - (smoothStep * (float)smoothcount); // current // out2 += buffer2[readPos+prevMinibufferSize] * smoothStep*(float)smoothcount; // + previous // out2 = (out2 * newgain) + (buffer2[readPos+prevMinibufferSize] * oldgain); // out2 = (out2 * sqrtFadeIn) + (buffer2[readPos+prevMinibufferSize] * sqrtFadeOut); out2 = (out2 * fadeInGain) + (buffer2[readPos+prevMinibufferSize] * fadeOutGain); #endif smoothcount--; // smoothFract += smoothStep; // sqrtFadeIn = 0.5f * (sqrtFadeIn + (smoothFract / sqrtFadeIn)); // sqrtFadeOut = 0.5f * (sqrtFadeOut + ((1.0f-smoothFract) / sqrtFadeOut)); fadeInGain = (fadeOutGain * imaginaryFadePart) + (fadeInGain * realFadePart); fadeOutGain = (realFadePart * fadeOutGain) - (imaginaryFadePart * fadeInGain); } // write the output samples into the output stream if (replacing) { outputs[0][samplecount] = (out1 * outputGain) + (inputs[0][samplecount] * inputGain); #ifdef BUFFEROVERRIDE_STEREO outputs[1][samplecount] = (out2 * outputGain) + (inputs[1][samplecount] * inputGain); #endif } else { outputs[0][samplecount] += (out1 * outputGain) + (inputs[0][samplecount] * inputGain); #ifdef BUFFEROVERRIDE_STEREO outputs[1][samplecount] += (out2 * outputGain) + (inputs[1][samplecount] * inputGain); #endif } // increment the position trackers readPos++; writePos++; } //-----------------------MIDI STUFF--------------------------- // check to see if there may be a note or pitchbend message left over that hasn't been implemented if (midistuff->numBlockEvents > 0) { long eventcount; for (eventcount = 0; eventcount < midistuff->numBlockEvents; eventcount++) { if (isNote(midistuff->blockEvents[eventcount].status)) { // regardless of whether it's a note-on or note-off, we've found some note message oldNote = true; // store the note & update the notes table if it's a note-on message if (midistuff->blockEvents[eventcount].status == kMidiNoteOn) { midistuff->insertNote(midistuff->blockEvents[eventcount].byte1); lastNoteOn = midistuff->blockEvents[eventcount].byte1; // since we're not doing the fDivisor updating yet, this needs to be falsed divisorWasChangedByHand = false; } // otherwise remove the note from the notes table else midistuff->removeNote(midistuff->blockEvents[eventcount].byte1); } else if (midistuff->blockEvents[eventcount].status == ccAllNotesOff) { oldNote = true; midistuff->removeAllNotes(); } } for (eventcount = (midistuff->numBlockEvents-1); (eventcount >= 0); eventcount--) { if (midistuff->blockEvents[eventcount].status == kMidiPitchbend) { // set this pitchbend message as lastPitchbend lastPitchbend = midistuff->blockEvents[eventcount].byte2; break; // leave this for loop } } } // always reset numBlockEvents because processEvents() may not get called before the next process() midistuff->numBlockEvents = 0; }
//----------------------------------------------------------------------------- // called from SkidderEdit void SkidderEditor::setParameter(long index, float value) { if (!frame) return; long rateTag = onOffTest(effect->getParameter(kTempoSync)) ? kTempoRate : kRate; switch (index) { case kRate: case kTempoRate: // store these into the static global variables so that the string convert function can see them theCycleRate = calculateTheCycleRate(); strcpy( tempoRateString, ((Skidder*)effect)->tempoRateTable->getDisplay(effect->getParameter(kTempoRate)) ); theTempoSync = ((Skidder*)effect)->fTempoSync; if (rateTag == index) { if (rateFader) rateFader->setValue(value); if (rateDisplay) rateDisplay->setValue(value); // update the rate random range display if (rateRandRangeDisplay) rateRandRangeDisplay->setDirty(); } break; case kTempoSync: strcpy( tempoRateString, ((Skidder*)effect)->tempoRateTable->getDisplay(effect->getParameter(kTempoRate)) ); theTempoSync = ((Skidder*)effect)->fTempoSync; theCycleRate = calculateTheCycleRate(); if (tempoSyncButton) tempoSyncButton->setValue(value); // update the rate displays if the sync mode is changed if (rateRandRangeDisplay) rateRandRangeDisplay->setDirty(); // see if we need to swap the parameter assignment for the rate controls if (rateFader) { if (rateTag != rateFader->getTag()) { rateFader->setTag(rateTag); rateFader->setValue(effect->getParameter(rateTag)); } } if (rateDisplay) { if (rateTag != rateDisplay->getTag()) { rateDisplay->setTag(rateTag); rateDisplay->setValue(effect->getParameter(rateTag)); rateDisplay->setDirty(); } } break; case kRateRandFactor: theCycleRate = calculateTheCycleRate(); if (rateRandFactorFader) rateRandFactorFader->setValue(value); if (rateRandFactorDisplay) rateRandFactorDisplay->setValue(value); if (rangeDisplay) rangeDisplay->setValue(value); if (rateRandRangeDisplay) { rateRandRangeDisplay->setValue(value); // makes the rate random range display disappear when there's no rate randomness if ( effect->getParameter(kRateRandFactor) <= 0.0003f ) { rateRandRangeDisplay->setBackColor(kBackgroundCColor); rateRandRangeDisplay->setFrameColor(kBackgroundCColor); } else { rateRandRangeDisplay->setBackColor(kMyPaleGreenCColor); rateRandRangeDisplay->setFrameColor(kBlackCColor); } rateRandRangeDisplay->setDirty(); } break; case kTempo: theCycleRate = calculateTheCycleRate(); if (tempoFader) tempoFader->setValue(value); if (tempoTextEdit) { if ( (value > 0.0f) || (((Skidder*)effect)->hostCanDoTempo != 1) ) sprintf(tempoString, "%.3f bpm", tempoScaled(value)); else strcpy(tempoString, "auto"); tempoTextEdit->setText(tempoString); } // update the rate random range display // (this crashes for some reason?) // if (rateRandRangeDisplay) // rateRandRangeDisplay->setDirty(); break; case kPulsewidth: if (pulsewidthFader) pulsewidthFader->setValueTagged(index, value); if (pulsewidthDisplay) pulsewidthDisplay->setValue(value); if (randomMinimumDisplay) randomMinimumDisplay->setValue(effect->getParameter(kPulsewidthRandMin)); if (pulsewidthRandMinDisplay) pulsewidthRandMinDisplay->setValue(effect->getParameter(kPulsewidthRandMin)); // because the regular pulsewidth can affect the random's read-out if (pulsewidthRandMinDisplay) pulsewidthRandMinDisplay->setDirty(); if (randomMinimumDisplay) randomMinimumDisplay->setDirty(); break; case kPulsewidthRandMin: if (pulsewidthFader) pulsewidthFader->setValueTagged(index, value); if (pulsewidthRandMinDisplay) pulsewidthRandMinDisplay->setValue(value); if (randomMinimumDisplay) randomMinimumDisplay->setValue(value); if (pulsewidthDisplay) pulsewidthDisplay->setValue(effect->getParameter(kPulsewidth)); break; case kSlope: if (slopeFader) slopeFader->setValue(value); if (slopeDisplay) slopeDisplay->setValue(value); break; case kFloor: if (floorFader) floorFader->setValueTagged(index, value); if (floorDisplay) floorDisplay->setValue(value); // because the regular floor can affect the random's read-out if (floorRandMinDisplay) floorRandMinDisplay->setDirty(); if (randomMinimum2Display) randomMinimum2Display->setDirty(); break; case kFloorRandMin: if (floorFader) floorFader->setValueTagged(index, value); if (floorRandMinDisplay) floorRandMinDisplay->setValue(value); if (randomMinimum2Display) randomMinimum2Display->setValue(value); break; case kPan: if (panFader) panFader->setValue(value); if (panDisplay) panDisplay->setValue(value); break; case kNoise: if (noiseFader) noiseFader->setValue(value); if (noiseDisplay) noiseDisplay->setValue(value); break; #ifdef MSKIDDER case kMidiMode: if (midiModeButton) midiModeButton->setValue(value); break; case kVelocity: if (velocityButton) velocityButton->setValue(value); break; #ifdef HUNGRY case kConnect: if (connectButton) connectButton->setValue(value); if (connectDisplay) connectDisplay->setValue(value); break; #endif #endif default: return; } postUpdate(); }
//----------------------------------------------------------------------------- long SkidderEditor::open(void *ptr) { CPoint displayOffset; // for positioning the background graphic behind display boxes // !!! always call this !!! AEffGUIEditor::open(ptr); // load some bitmaps if (!gFaderSlide) gFaderSlide = new CBitmap (kFaderSlideID); if (!gFaderHandle) gFaderHandle = new CBitmap (kFaderHandleID); if (!gGlowingFaderHandle) gGlowingFaderHandle = new CBitmap (kGlowingFaderHandleID); if (!gFaderHandleLeft) gFaderHandleLeft = new CBitmap (kFaderHandleLeftID); if (!gGlowingFaderHandleLeft) gGlowingFaderHandleLeft = new CBitmap (kGlowingFaderHandleLeftID); if (!gFaderHandleRight) gFaderHandleRight = new CBitmap (kFaderHandleRightID); if (!gGlowingFaderHandleRight) gGlowingFaderHandleRight = new CBitmap (kGlowingFaderHandleRightID); if (!gTempoSyncButton) gTempoSyncButton = new CBitmap (kTempoSyncButtonID); if (!gMidiLearnButton) gMidiLearnButton = new CBitmap (kMidiLearnButtonID); if (!gMidiResetButton) gMidiResetButton = new CBitmap (kMidiResetButtonID); if (!gGoButton) gGoButton = new CBitmap (kGoButtonID); if (!gDestroyFXlink) gDestroyFXlink = new CBitmap (kDestroyFXlinkID); if (!gSmartElectronixLink) gSmartElectronixLink = new CBitmap (kSmartElectronixLinkID); #ifdef MSKIDDER if (!gMidiModeButton) gMidiModeButton = new CBitmap (kMidiModeButtonID); if (!gVelocityButton) gVelocityButton = new CBitmap (kVelocityButtonID); #ifdef HUNGRY if (!gConnectButton) gConnectButton = new CBitmap (kConnectButtonID); #endif #endif chunk->resetLearning(); goError = kNoGoDisplay; long rateTag = onOffTest(effect->getParameter(kTempoSync)) ? kTempoRate : kRate; //--initialize the background frame-------------------------------------- CRect size (0, 0, gBackground->getWidth(), gBackground->getHeight()); frame = new CFrame (size, ptr, this); frame->setBackground(gBackground); //--initialize the faders----------------------------------------------- int minPos = kFaderX; int maxPos = kFaderX + gFaderSlide->getWidth() - gFaderHandle->getWidth() - 1; CPoint point (0, 0); CPoint offset (0, 1); // rate (single slider for "free" Hz rate & tempo synced rate) // size stores left, top, right, & bottom positions size (kFaderX, kFaderY, kFaderX + gFaderSlide->getWidth(), kFaderY + gFaderSlide->getHeight()); rateFader = new CHorizontalSlider (size, this, rateTag, minPos, maxPos, gFaderHandle, gFaderSlide, point, kLeft); rateFader->setOffsetHandle(offset); rateFader->setValue(effect->getParameter(rateTag)); rateFader->setDefaultValue(rateUnscaled(3.0f)); frame->addView(rateFader); // rate random factor size.offset (0, kFaderInc); rateRandFactorFader = new CHorizontalSlider (size, this, kRateRandFactor, minPos, maxPos, gFaderHandle, gFaderSlide, point, kLeft); rateRandFactorFader->setOffsetHandle(offset); rateRandFactorFader->setValue(effect->getParameter(kRateRandFactor)); rateRandFactorFader->setDefaultValue(0.0f); frame->addView(rateRandFactorFader); // tempo (in bpm) size.offset (0, kFaderInc); tempoFader = new CHorizontalSlider (size, this, kTempo, minPos, maxPos, gFaderHandle, gFaderSlide, point, kLeft); tempoFader->setOffsetHandle(offset); tempoFader->setValue(effect->getParameter(kTempo)); tempoFader->setDefaultValue(0.0f); frame->addView(tempoFader); int minRPos = kFaderX; int maxRPos = kFaderX + gFaderSlide->getWidth(); // pulsewidth size.offset (0, kFaderInc); pulsewidthFader = new CHorizontalRangeSlider (size, this, kPulsewidthRandMin, kPulsewidth, minRPos, maxRPos, gFaderHandleLeft, gFaderSlide, point, kLeft, kMaxCanPush, gFaderHandleRight); pulsewidthFader->setOffsetHandle(point); pulsewidthFader->setValueTagged(kPulsewidth, effect->getParameter(kPulsewidth)); pulsewidthFader->setValueTagged(kPulsewidthRandMin, effect->getParameter(kPulsewidthRandMin)); pulsewidthFader->setDefaultValueTagged(kPulsewidth, 0.5f); pulsewidthFader->setDefaultValueTagged(kPulsewidthRandMin, 0.5f); frame->addView(pulsewidthFader); // slope size.offset (0, kFaderInc); slopeFader = new CHorizontalSlider (size, this, kSlope, minPos, maxPos, gFaderHandle, gFaderSlide, point, kLeft); slopeFader->setOffsetHandle(offset); slopeFader->setValue(effect->getParameter(kSlope)); slopeFader->setDefaultValue(3.0f/SLOPEMAX); frame->addView(slopeFader); // floor size.offset (0, kFaderInc); floorFader = new CHorizontalRangeSlider (size, this, kFloorRandMin, kFloor, minRPos, maxRPos, gFaderHandleLeft, gFaderSlide, point, kLeft, kMaxCanPush, gFaderHandleRight); floorFader->setOffsetHandle(point); floorFader->setValueTagged(kFloor, effect->getParameter(kFloor)); floorFader->setValueTagged(kFloorRandMin, effect->getParameter(kFloorRandMin)); floorFader->setDefaultValueTagged(kFloor, 0.0f); floorFader->setDefaultValueTagged(kFloorRandMin, 0.0f); frame->addView(floorFader); // pan size.offset (0, kFaderInc); panFader = new CHorizontalSlider (size, this, kPan, minPos, maxPos, gFaderHandle, gFaderSlide, point, kLeft); panFader->setOffsetHandle(offset); panFader->setValue(effect->getParameter(kPan)); panFader->setDefaultValue(0.6f); frame->addView(panFader); // noise size.offset (0, kFaderInc); noiseFader = new CHorizontalSlider (size, this, kNoise, minPos, maxPos, gFaderHandle, gFaderSlide, point, kLeft); noiseFader->setOffsetHandle(offset); noiseFader->setValue(effect->getParameter(kNoise)); noiseFader->setDefaultValue(1.0f); frame->addView(noiseFader); //--initialize the buttons---------------------------------------------- // choose the rate type ("free" or synced) size (kTempoSyncButtonX, kTempoSyncButtonY, kTempoSyncButtonX + gTempoSyncButton->getWidth(), kTempoSyncButtonY + (gTempoSyncButton->getHeight())/2); tempoSyncButton = new COnOffButton (size, this, kTempoSync, gTempoSyncButton); tempoSyncButton->setValue(effect->getParameter(kTempoSync)); frame->addView(tempoSyncButton); // go! // size stores left, top, right, & bottom positions size (kGoButtonX, kGoButtonY, kGoButtonX + gGoButton->getWidth(), kGoButtonY + (gGoButton->getHeight())/2); goButton = new CWebLink (size, this, kGoButtonID, "http://www.whirledbank.org/", gGoButton); frame->addView(goButton); // Destroy FX web page link size (kDestroyFXlinkX, kDestroyFXlinkY, kDestroyFXlinkX + gDestroyFXlink->getWidth(), kDestroyFXlinkY + (gDestroyFXlink->getHeight())/2); DestroyFXlink = new CWebLink (size, this, kDestroyFXlinkID, DESTROYFX_URL, gDestroyFXlink); frame->addView(DestroyFXlink); // Smart Electronix web page link size (kSmartElectronixLinkX, kSmartElectronixLinkY, kSmartElectronixLinkX + gSmartElectronixLink->getWidth(), kSmartElectronixLinkY + (gSmartElectronixLink->getHeight())/2); SmartElectronixLink = new CWebLink (size, this, kSmartElectronixLinkID, SMARTELECTRONIX_URL, gSmartElectronixLink); frame->addView(SmartElectronixLink); #ifdef MSKIDDER // MIDI note control mode button size (kMidiModeButtonX, kMidiModeButtonY, kMidiModeButtonX + gMidiModeButton->getWidth(), kMidiModeButtonY + (gMidiModeButton->getHeight())/NUM_MIDI_MODES); midiModeButton = new CMultiToggle (size, this, kMidiMode, NUM_MIDI_MODES, gMidiModeButton->getHeight()/NUM_MIDI_MODES, gMidiModeButton, point); midiModeButton->setValue(effect->getParameter(kMidiMode)); frame->addView(midiModeButton); // use-note-velocity button size (kVelocityButtonX, kVelocityButtonY, kVelocityButtonX + gVelocityButton->getWidth(), kVelocityButtonY + (gVelocityButton->getHeight())/2); velocityButton = new COnOffButton (size, this, kVelocity, gVelocityButton); velocityButton->setValue(effect->getParameter(kVelocity)); frame->addView(velocityButton); // turn on/off MIDI learn mode for CC parameter automation size (kMidiLearnButtonX, kMidiLearnButtonY, kMidiLearnButtonX + gMidiLearnButton->getWidth(), kMidiLearnButtonY + (gMidiLearnButton->getHeight())/2); midiLearnButton = new COnOffButton (size, this, kMidiLearnButtonID, gMidiLearnButton); midiLearnButton->setValue(0.0f); frame->addView(midiLearnButton); // clear all MIDI CC assignments size (kMidiResetButtonX, kMidiResetButtonY, kMidiResetButtonX + gMidiResetButton->getWidth(), kMidiResetButtonY + (gMidiResetButton->getHeight())/2); midiResetButton = new CKickButton (size, this, kMidiResetButtonID, (gMidiResetButton->getHeight())/2, gMidiResetButton, point); midiResetButton->setValue(0.0f); frame->addView(midiResetButton); #ifdef HUNGRY if ( ((Skidder*)effect)->foodEater->hostIsLogic ) { // connect to food size (kConnectButtonX, kConnectButtonY, kConnectButtonX + gConnectButton->getWidth(), kConnectButtonY + (gConnectButton->getHeight())/2); connectButton = new COnOffButton (size, this, kConnect, gConnectButton); connectButton->setValue(effect->getParameter(kConnect)); frame->addView(connectButton); } #endif #endif //--initialize the displays--------------------------------------------- // first store the proper values for all of the globals so that displays are correct strcpy( tempoRateString, ((Skidder*)effect)->tempoRateTable->getDisplay(effect->getParameter(kTempoRate)) ); theTempoSync = ((Skidder*)effect)->fTempoSync; theCycleRate = calculateTheCycleRate(); // rate (unified display for "free" Hz rate & tempo synced rate) size (kDisplayX, kDisplayY, kDisplayX + kDisplayWidth, kDisplayY + kDisplayHeight); rateDisplay = new CParamDisplay (size, gBackground); displayOffset (kDisplayX, kDisplayY); rateDisplay->setBackOffset(displayOffset); rateDisplay->setHoriAlign(kLeftText); rateDisplay->setFont(kNormalFontSmall); rateDisplay->setFontColor(kWhiteCColor); rateDisplay->setValue(effect->getParameter(rateTag)); rateDisplay->setStringConvert(rateDisplayConvert, tempoRateString); rateDisplay->setTag(rateTag); frame->addView(rateDisplay); // rate random factor size.offset (0, kFaderInc); rateRandFactorDisplay = new CParamDisplay (size, gBackground); displayOffset (kDisplayX, kDisplayY); rateRandFactorDisplay->setBackOffset(displayOffset); rateRandFactorDisplay->setHoriAlign(kLeftText); rateRandFactorDisplay->setFont(kNormalFontSmall); rateRandFactorDisplay->setFontColor(kWhiteCColor); rateRandFactorDisplay->setValue(effect->getParameter(kRateRandFactor)); rateRandFactorDisplay->setStringConvert(rateRandFactorDisplayConvert); rateRandFactorDisplay->setTag(kRateRandFactor); frame->addView(rateRandFactorDisplay); // tempo (in bpm) (editable) size.offset (0, kFaderInc); tempoTextEdit = new CTextEdit (size, this, kTempoTextEdit, 0, gBackground); displayOffset.offset (0, kFaderInc); tempoTextEdit->setBackOffset(displayOffset); tempoTextEdit->setFont (kNormalFontSmall); tempoTextEdit->setFontColor (kWhiteCColor); tempoTextEdit->setHoriAlign (kLeftText); frame->addView (tempoTextEdit); // this makes it display the current value setParameter(kTempo, effect->getParameter(kTempo)); // pulsewidth size.offset (0, kFaderInc - 1); pulsewidthDisplay = new CNumberBox (size, this, kPulsewidth, gBackground, 0, kVertical); displayOffset.offset (0, kFaderInc - 1); pulsewidthDisplay->setBackOffset(displayOffset); pulsewidthDisplay->setHoriAlign(kLeftText); pulsewidthDisplay->setFont(kNormalFontSmall); pulsewidthDisplay->setFontColor(kWhiteCColor); pulsewidthDisplay->setValue(effect->getParameter(kPulsewidth)); pulsewidthDisplay->setStringConvert(pulsewidthDisplayConvert); pulsewidthDisplay->setTag(kPulsewidth); frame->addView(pulsewidthDisplay); // pulsewidth random minimum size.offset (1, kRandomMinimumIncY + 1); pulsewidthRandMinDisplay = new CParamDisplay (size, gBackground); displayOffset.offset (1, kRandomMinimumIncY + 1); pulsewidthRandMinDisplay->setBackOffset(displayOffset); pulsewidthRandMinDisplay->setHoriAlign(kLeftText); pulsewidthRandMinDisplay->setFont(kNormalFontVerySmall); pulsewidthRandMinDisplay->setFontColor(kWhiteCColor); pulsewidthRandMinDisplay->setValue(effect->getParameter(kPulsewidthRandMin)); pulsewidthRandMinDisplay->setStringConvert( pulsewidthRandMinDisplayConvert, &(((Skidder*)effect)->fPulsewidth) ); pulsewidthRandMinDisplay->setTag(kPulsewidthRandMin); frame->addView(pulsewidthRandMinDisplay); // the words "random minimum" size (kRandomMinimumX, kDisplayY + (kFaderInc*3) + kRandomMinimumIncY, kRandomMinimumX + kRandomMinimumWidth, kDisplayY + (kFaderInc*3) + kRandomMinimumIncY + kDisplayHeight); randomMinimumDisplay = new CParamDisplay (size, gBackground); displayOffset (kRandomMinimumX, kDisplayY + (kFaderInc*4) + kRandomMinimumIncY + 1); randomMinimumDisplay->setBackOffset(displayOffset); randomMinimumDisplay->setHoriAlign(kRightText); randomMinimumDisplay->setFont(kNormalFontVerySmall); randomMinimumDisplay->setFontColor(kWhiteCColor); randomMinimumDisplay->setValue(effect->getParameter(kPulsewidthRandMin)); randomMinimumDisplay->setStringConvert( randomMinimumDisplayConvert, &(((Skidder*)effect)->fPulsewidth) ); randomMinimumDisplay->setTag(kPulsewidthRandMin); frame->addView(randomMinimumDisplay); // slope size (kDisplayX, kDisplayY + (kFaderInc*4), kDisplayX + kDisplayWidth, kDisplayY + (kFaderInc*4) + kDisplayHeight); slopeDisplay = new CParamDisplay (size, gBackground); displayOffset (kDisplayX, kDisplayY + (kFaderInc*5)); slopeDisplay->setBackOffset(displayOffset); slopeDisplay->setHoriAlign(kLeftText); slopeDisplay->setFont(kNormalFontSmall); slopeDisplay->setFontColor(kWhiteCColor); slopeDisplay->setValue(effect->getParameter(kSlope)); slopeDisplay->setStringConvert(slopeDisplayConvert); frame->addView(slopeDisplay); // floor size.offset (0, kFaderInc - 1); floorDisplay = new CParamDisplay (size, gBackground); displayOffset.offset (0, kFaderInc - 1); floorDisplay->setBackOffset(displayOffset); floorDisplay->setHoriAlign(kLeftText); floorDisplay->setFont(kNormalFontSmall); floorDisplay->setFontColor(kWhiteCColor); floorDisplay->setValue(effect->getParameter(kFloor)); floorDisplay->setStringConvert(floorDisplayConvert); frame->addView(floorDisplay); // floor random minimum size.offset (1, kRandomMinimumIncY + 1); floorRandMinDisplay = new CParamDisplay (size, gBackground); displayOffset.offset (1, kRandomMinimumIncY + 1); floorRandMinDisplay->setBackOffset(displayOffset); floorRandMinDisplay->setHoriAlign(kLeftText); floorRandMinDisplay->setFont(kNormalFontVerySmall); floorRandMinDisplay->setFontColor(kWhiteCColor); floorRandMinDisplay->setValue(effect->getParameter(kFloorRandMin)); floorRandMinDisplay->setStringConvert( floorRandMinDisplayConvert, &(((Skidder*)effect)->fFloor) ); floorRandMinDisplay->setTag(kFloorRandMin); frame->addView(floorRandMinDisplay); // the words "random minimum" again size (kRandomMinimumX, kDisplayY + (kFaderInc*5) + kRandomMinimumIncY, kRandomMinimumX + kRandomMinimumWidth, kDisplayY + (kFaderInc*5) + kRandomMinimumIncY + kDisplayHeight); randomMinimum2Display = new CParamDisplay (size, gBackground); displayOffset (kRandomMinimumX, kDisplayY + (kFaderInc*6) + kRandomMinimumIncY + 1); randomMinimum2Display->setBackOffset(displayOffset); randomMinimum2Display->setHoriAlign(kRightText); randomMinimum2Display->setFont(kNormalFontVerySmall); randomMinimum2Display->setFontColor(kWhiteCColor); randomMinimum2Display->setValue(effect->getParameter(kFloorRandMin)); randomMinimum2Display->setStringConvert( randomMinimumDisplayConvert, &(((Skidder*)effect)->fFloor) ); randomMinimum2Display->setTag(kFloorRandMin); frame->addView(randomMinimum2Display); // pan size (kDisplayX, kDisplayY + (kFaderInc*6), kDisplayX + kDisplayWidth, kDisplayY + (kFaderInc*6) + kDisplayHeight); panDisplay = new CParamDisplay (size, gBackground); displayOffset.offset (0, kFaderInc); panDisplay->setBackOffset(displayOffset); panDisplay->setHoriAlign(kLeftText); panDisplay->setFont(kNormalFontSmall); panDisplay->setFontColor(kWhiteCColor); panDisplay->setValue(effect->getParameter(kPan)); panDisplay->setStringConvert(panDisplayConvert); frame->addView(panDisplay); // noise size.offset (0, kFaderInc); noiseDisplay = new CParamDisplay (size, gBackground); displayOffset.offset (0, kFaderInc); noiseDisplay->setBackOffset(displayOffset); noiseDisplay->setHoriAlign(kLeftText); noiseDisplay->setFont(kNormalFontSmall); noiseDisplay->setFontColor(kWhiteCColor); noiseDisplay->setValue(effect->getParameter(kNoise)); noiseDisplay->setStringConvert(noiseDisplayConvert); frame->addView(noiseDisplay); // the word "range" size (kRangeDisplayX, kRangeDisplayY, kRangeDisplayX + kRangeDisplayWidth, kRangeDisplayY + kRangeDisplayHeight); rangeDisplay = new CParamDisplay (size, gBackground); displayOffset (kRangeDisplayX, kRangeDisplayY); rangeDisplay->setBackOffset(displayOffset); rangeDisplay->setHoriAlign(kLeftText); rangeDisplay->setFont(kNormalFontVerySmall); rangeDisplay->setFontColor(kBlackCColor); rangeDisplay->setValue(effect->getParameter(kRateRandFactor)); rangeDisplay->setStringConvert(rangeDisplayConvert); frame->addView(rangeDisplay); // the rate random factor range read-out size (kRateRandRangeDisplayX, kRateRandRangeDisplayY, kRateRandRangeDisplayX + kRateRandRangeDisplayWidth, kRateRandRangeDisplayY + kRateRandRangeDisplayHeight); rateRandRangeDisplay = new CParamDisplay (size); // if/else stuff to make this display disappear when there's no rate randomness if ( effect->getParameter(kRateRandFactor) <= 0.0f ) { rateRandRangeDisplay->setBackColor(kBackgroundCColor); rateRandRangeDisplay->setFrameColor(kBackgroundCColor); } else { rateRandRangeDisplay->setBackColor(kMyPaleGreenCColor); rateRandRangeDisplay->setFrameColor(kBlackCColor); } rateRandRangeDisplay->setHoriAlign(kCenterText); rateRandRangeDisplay->setFont(kNormalFontVerySmall); rateRandRangeDisplay->setFontColor(kMyDarkBlueCColor); rateRandRangeDisplay->setValue(effect->getParameter(kRateRandFactor)); rateRandRangeDisplay->setStringConvert(rateRandRangeDisplayConvert, &theCycleRate); frame->addView(rateRandRangeDisplay); // go! error/success display size (kGoButtonX, kGoButtonY + 17, kGoButtonX + gGoButton->getWidth(), kGoButtonY + kDisplayHeight + 17); goDisplay = new CParamDisplay (size, gBackground); displayOffset (kGoButtonX, kGoButtonY + 17); goDisplay->setBackOffset(displayOffset); goDisplay->setHoriAlign(kCenterText); goDisplay->setFont(kNormalFontSmall); goDisplay->setFontColor(kWhiteCColor); goDisplay->setValue(0.0f); goDisplay->setStringConvert(goDisplayConvert, &goError); frame->addView(goDisplay); #ifdef MSKIDDER #ifdef HUNGRY if ( ((Skidder*)effect)->foodEater->hostIsLogic ) { // connect to food error display size (kConnectButtonX, kConnectButtonY + 17, kConnectButtonX + gConnectButton->getWidth(), kConnectButtonY + kDisplayHeight + 17); connectDisplay = new CParamDisplay (size, gBackground); displayOffset (kConnectButtonX, kConnectButtonY + 17); connectDisplay->setBackOffset(displayOffset); connectDisplay->setHoriAlign(kCenterText); connectDisplay->setFont(kNormalFontSmall); connectDisplay->setFontColor(kWhiteCColor); connectDisplay->setValue(effect->getParameter(kConnect)); connectDisplay->setStringConvert( connectDisplayConvert, &(((Skidder*)effect)->foodEater->foodError) ); frame->addView(connectDisplay); } #endif #endif for (long i=0; i < NUM_PARAMETERS; i++) faders[i] = NULL; faders[kRateRandFactor] = rateRandFactorFader; faders[kTempo] = tempoFader; faders[kSlope] = slopeFader; faders[kPan] = panFader; faders[kNoise] = noiseFader; isOpen = true; return true; }
//----------------------------------------------------------------------------------------- void skidder::processReplacing(float **inputs, float **outputs, long sampleFrames, bool replacing) { float *in1 = inputs[0]; float *in2 = inputs[1]; float *out1 = outputs[0]; float *out2 = outputs[1]; float SAMPLERATE = Samplerate(); // just in case the host responds with something wacky if (SAMPLERATE <= 0.0f) SAMPLERATE = 44100.0f; long samplecount; floor = gainScaled(fFloor); // the parameter scaled real value gainRange = 1.0f - floor; // the range of the skidding on/off gain useRandomFloor = (fFloorRandMin < fFloor); // figure out the current tempo if we're doing tempo sync if (onOffTest(fTempoSync)) { // calculate the tempo at the current processing buffer currentTempoBPS = tempoScaled(fTempo) / 60.0f; } for (samplecount=0; (samplecount < sampleFrames); samplecount++) { switch (state) { case slopeIn: // get the average sqareroot of the current input samples rms += sqrtf( fabsf(((*in1)+(*in2))*0.5f) ); rmscount++; // this counter is later used for getting the mean processSlopeIn(); break; case plateau: rms += sqrtf( fabsf(((*in1)+(*in2))*0.5f) ); rmscount++; processPlateau(); break; case slopeOut: processSlopeOut(); break; case valley: processValley(SAMPLERATE); break; } // ((panRander*fPan)+1.0) ranges from 0.0 to 2.0 if (replacing) { *out1 = processOutput( *in1, *in2, ((panRander*fPan)+1.0f) ); *out2 = processOutput( *in2, *in1, (2.0f - ((panRander*fPan)+1.0f)) ); } else { *out1 += processOutput( *in1, *in2, ((panRander*fPan)+1.0f) ); *out2 += processOutput( *in2, *in1, (2.0f - ((panRander*fPan)+1.0f)) ); } // move forward in the i/o sample streams in1++; in2++; out1++; out2++; } }
//----------------------------------------------------------------------------------------- void skidder::processValley(float SAMPLERATE) { float rateRandFactor = rateRandFactorScaled(fRateRandFactor); // stores the real value float cycleRate; // the base current skid rate value float randFloat, randomRate; // the current randomized rate value float fPulsewidthRandomized; // stores the latest randomized pulsewidth 0.0 - 1.0 value bool barSync = false; // true if we need to sync up with the next bar start long countdown; if (useRandomFloor) amp = randomFloor; else amp = floor; valleySamples--; if (valleySamples <= 0) { rms = 0.0f; // reset rms now because valley is over // // This is where we figure out how many samples long each // envelope section is for the next skid cycle. // if (onOffTest(fTempoSync)) // the user wants to do tempo sync / beat division rate { cycleRate = currentTempoBPS * (tempoRateTable->getScalar(fTempoRate)); // set this true so that we make sure to do the measure syncronisation later on } else cycleRate = rateScaled(fRate); // if (fRateRandFactor > 0.0f) { // get a random value from 0.0 to 1.0 randFloat = (float)rand() * ONE_DIV_RAND_MAX; // square-scale the random value & then scale it with the random rate range randomRate = ( randFloat * randFloat * ((cycleRate*rateRandFactor)-(cycleRate/rateRandFactor)) ) + (cycleRate/rateRandFactor); cycleSamples = (long) (SAMPLERATE / randomRate); barSync = false; // we can't do the bar sync if the skids durations are random } else cycleSamples = (long) (SAMPLERATE / cycleRate); // if (fPulsewidth > fPulsewidthRandMin) { fPulsewidthRandomized = ( ((float)rand()*ONE_DIV_RAND_MAX) * (fPulsewidth-fPulsewidthRandMin) ) + fPulsewidthRandMin; pulseSamples = (long) ( ((float)cycleSamples) * pulsewidthScaled(fPulsewidthRandomized) ); } else pulseSamples = (long) ( ((float)cycleSamples) * pulsewidthScaled(fPulsewidth) ); valleySamples = cycleSamples - pulseSamples; slopeSamples = (long) ((SAMPLERATE/1000.0f)*(fSlope*(SLOPEMAX))); slopeDur = slopeSamples; slopeStep = 1.0f / (float)slopeDur; // calculate the fade increment scalar plateauSamples = pulseSamples - (slopeSamples * 2); if (plateauSamples < 1) // this shrinks the slope to 1/3 of the pulse if the user sets slope too high { slopeSamples = (long) (((float)pulseSamples) / 3.0f); slopeDur = slopeSamples; slopeStep = 1.0f / (float)slopeDur; // calculate the fade increment scalar plateauSamples = pulseSamples - (slopeSamples * 2); } // go to slopeIn next if slope is not 0.0, otherwise go to plateau if (slopeDur > 0) state = slopeIn; else state = plateau; // this puts random float values from -1.0 to 1.0 into panRander panRander = ( ((float)rand()*ONE_DIV_RAND_MAX) * 2.0f ) - 1.0f; } //end of the "valley is over" if-statement }