/// <summary> /// Writes the wave to the supplied buffer array of floats (it'll contain the mono audio) /// </summary> bool SynthWave(float[] buffer, int bufferPos, uint length) { var finished = false; for (var i = 0; i < (int)length; i++) { if (finished) { return(true); } // Repeats every repeatLimit times, partially resetting the sound parameters if (repeatLimit != 0) { if (++repeatTime >= repeatLimit) { repeatTime = 0; Reset(false); } } changePeriodTime++; if (changePeriodTime >= changePeriod) { changeTime = 0; changeTime2 = 0; changePeriodTime = 0; if (changeReached) { period /= changeAmount; changeReached = false; } if (changeReached2) { period /= changeAmount2; changeReached2 = false; } } // If changeLimit is reached, shifts the pitch if (!changeReached) { if (++changeTime >= changeLimit) { changeReached = true; period *= changeAmount; } } // If changeLimit is reached, shifts the pitch if (!changeReached2) { if (++changeTime2 >= changeLimit2) { changeReached2 = true; period *= changeAmount2; } } // Accelerate and apply slide slide += deltaSlide; period *= slide; // Checks for frequency getting too low, and stops the sound if a minFrequency was set if (period > maxPeriod) { period = maxPeriod; if (minFrequency > 0) { finished = true; } } periodTemp = period; // Applies the vibrato effect if (vibratoAmplitude > 0) { vibratoPhase += vibratoSpeed; periodTemp = period * (1.0f + Mathf.Sin(vibratoPhase) * vibratoAmplitude); } periodTempInt = (int)periodTemp; if (periodTemp < 8) { periodTemp = periodTempInt = 8; } // Sweeps the square duty if (waveType == 0) { squareDuty += dutySweep; if (squareDuty < 0.0) { squareDuty = 0.0f; } else if (squareDuty > 0.5) { squareDuty = 0.5f; } } // Moves through the different stages of the volume envelope if (++envelopeTime > envelopeLength) { envelopeTime = 0; switch (++envelopeStage) { case 1: envelopeLength = envelopeLength1; break; case 2: envelopeLength = envelopeLength2; break; } } // Sets the volume based on the position in the envelope switch (envelopeStage) { case 0: envelopeVolume = envelopeTime * envelopeOverLength0; break; case 1: envelopeVolume = 1.0f + (1.0f - envelopeTime * envelopeOverLength1) * 2.0f * sustainPunch; break; case 2: envelopeVolume = 1.0f - envelopeTime * envelopeOverLength2; break; case 3: envelopeVolume = 0.0f; finished = true; break; } // Moves the phaser offset if (phaser) { phaserOffset += phaserDeltaOffset; phaserInt = (int)phaserOffset; if (phaserInt < 0) { phaserInt = -phaserInt; } else if (phaserInt > 1023) { phaserInt = 1023; } } // Moves the high-pass filter cutoff if (filters && hpFilterDeltaCutoff != 0) { hpFilterCutoff *= hpFilterDeltaCutoff; if (hpFilterCutoff < 0.00001f) { hpFilterCutoff = 0.00001f; } else if (hpFilterCutoff > 0.1f) { hpFilterCutoff = 0.1f; } } superSample = 0; int j; for (j = 0; j < 8; j++) { // Cycles through the period phase++; if (phase >= periodTempInt) { phase = phase % periodTempInt; // Generates new random noise for this period int n; if (waveType == WaveType.Noise) { // Noise for (n = 0; n < 32; n++) { noiseBuffer[n] = SfxrParams.GetRandom() * 2.0f - 1.0f; } } else if (waveType == WaveType.PinkNoise) { // Pink noise for (n = 0; n < 32; n++) { pinkNoiseBuffer[n] = pinkNumber.getNextValue(); } } else if (waveType == WaveType.Tan) { // Tan for (n = 0; n < 32; n++) { loResNoiseBuffer[n] = ((n % LoResNoisePeriod) == 0) ? SfxrParams.GetRandom() * 2.0f - 1.0f : loResNoiseBuffer[n - 1]; } } } sample = 0; float sampleTotal = 0; var overtoneStrength = 1f; int k; for (k = 0; k <= overtones; k++) { var tempPhase = phase * (k + 1) % periodTemp; // Gets the sample from the oscillator switch (waveType) { case WaveType.Square: sample = ((tempPhase / periodTemp) < squareDuty) ? 0.5f : -0.5f; break; case WaveType.Sawtooth: sample = 1.0f - (tempPhase / periodTemp) * 2.0f; break; case WaveType.Sine: pos = tempPhase / periodTemp; pos = pos > 0.5f ? (pos - 1.0f) * 6.28318531f : pos * 6.28318531f; sample = pos < 0 ? 1.27323954f * pos + 0.405284735f * pos * pos : 1.27323954f * pos - 0.405284735f * pos * pos; sample = sample < 0 ? 0.225f * (sample * -sample - sample) + sample : 0.225f * (sample * sample - sample) + sample; break; case WaveType.Noise: // Noise sample = noiseBuffer[(uint)(tempPhase * 32f / periodTempInt) % 32]; break; case WaveType.Triangle: sample = Math.Abs(1f - (tempPhase / periodTemp) * 2f) - 1f; break; case WaveType.PinkNoise: sample = pinkNoiseBuffer[(uint)(tempPhase * 32f / periodTempInt) % 32]; break; case WaveType.Tan: // Tan sample = (float)Math.Tan(Math.PI * tempPhase / periodTemp); break; case WaveType.Whistle: // Sine wave code pos = tempPhase / periodTemp; pos = pos > 0.5f ? (pos - 1.0f) * 6.28318531f : pos * 6.28318531f; sample = pos < 0 ? 1.27323954f * pos + 0.405284735f * pos * pos : 1.27323954f * pos - 0.405284735f * pos * pos; sample = 0.75f * (sample < 0 ? 0.225f * (sample * -sample - sample) + sample : 0.225f * (sample * sample - sample) + sample); // Then whistle (essentially an overtone with frequencyx20 and amplitude0.25 pos = ((tempPhase * 20f) % periodTemp) / periodTemp; pos = pos > 0.5f ? (pos - 1.0f) * 6.28318531f : pos * 6.28318531f; sample2 = pos < 0 ? 1.27323954f * pos + .405284735f * pos * pos : 1.27323954f * pos - 0.405284735f * pos * pos; sample += 0.25f * (sample2 < 0 ? .225f * (sample2 * -sample2 - sample2) + sample2 : .225f * (sample2 * sample2 - sample2) + sample2); break; case WaveType.Breaker: // Breaker amp = tempPhase / periodTemp; sample = Math.Abs(1f - amp * amp * 2f) - 1f; break; } sampleTotal += overtoneStrength * sample; overtoneStrength *= (1f - overtoneFalloff); } sample = sampleTotal; // Applies the low and high pass filters if (filters) { lpFilterOldPos = lpFilterPos; lpFilterCutoff *= lpFilterDeltaCutoff; if (lpFilterCutoff < 0.0) { lpFilterCutoff = 0.0f; } else if (lpFilterCutoff > 0.1) { lpFilterCutoff = 0.1f; } if (lpFilterOn) { lpFilterDeltaPos += (sample - lpFilterPos) * lpFilterCutoff; lpFilterDeltaPos *= lpFilterDamping; } else { lpFilterPos = sample; lpFilterDeltaPos = 0.0f; } lpFilterPos += lpFilterDeltaPos; hpFilterPos += lpFilterPos - lpFilterOldPos; hpFilterPos *= 1.0f - hpFilterCutoff; sample = hpFilterPos; } // Applies the phaser effect if (phaser) { phaserBuffer[phaserPos & 1023] = sample; sample += phaserBuffer[(phaserPos - phaserInt + 1024) & 1023]; phaserPos = (phaserPos + 1) & 1023; } superSample += sample; } // Averages out the super samples and applies volumes superSample = masterVolume * envelopeVolume * superSample * 0.125f; // Bit crush bitcrushPhase += bitcrushFreq; if (bitcrushPhase > 1f) { bitcrushPhase = 0; bitcrushLast = superSample; } bitcrushFreq = Mathf.Max(Mathf.Min(bitcrushFreq + bitcrushFreqSweep, 1f), 0f); superSample = bitcrushLast; // Compressor if (superSample > 0f) { superSample = Mathf.Pow(superSample, compressionFactor); } else { superSample = -Mathf.Pow(-superSample, compressionFactor); } // Clipping if too loud if (superSample < -1f) { superSample = -1f; } else if (superSample > 1f) { superSample = 1f; } // Writes value to list, ignoring left/right sound channels (this is applied when filtering the audio later) buffer[i + bufferPos] = superSample; } return(false); }
/// <summary> /// Resets the running variables from the params /// Used once at the start (total reset) and for the repeat effect (partial reset) /// @param totalReset If the reset is total /// </summary> void Reset(bool totalReset) { // Shorter reference var p = param; period = 100.0f / (p.startFrequency * p.startFrequency + 0.001f); maxPeriod = 100.0f / (p.minFrequency * p.minFrequency + 0.001f); slide = 1.0f - p.slide * p.slide * p.slide * 0.01f; deltaSlide = -p.deltaSlide * p.deltaSlide * p.deltaSlide * 0.000001f; if (p.waveType == 0) { squareDuty = 0.5f - p.squareDuty * 0.5f; dutySweep = -p.dutySweep * 0.00005f; } changePeriod = Mathf.Max(((1f - p.changeRepeat) + 0.1f) / 1.1f) * 20000f + 32f; changePeriodTime = 0; if (p.changeAmount > 0.0) { changeAmount = 1.0f - p.changeAmount * p.changeAmount * 0.9f; } else { changeAmount = 1.0f + p.changeAmount * p.changeAmount * 10.0f; } changeTime = 0; changeReached = false; if (p.changeSpeed == 1.0f) { changeLimit = 0; } else { changeLimit = (int)((1f - p.changeSpeed) * (1f - p.changeSpeed) * 20000f + 32f); } if (p.changeAmount2 > 0f) { changeAmount2 = 1f - p.changeAmount2 * p.changeAmount2 * 0.9f; } else { changeAmount2 = 1f + p.changeAmount2 * p.changeAmount2 * 10f; } changeTime2 = 0; changeReached2 = false; if (p.changeSpeed2 == 1.0f) { changeLimit2 = 0; } else { changeLimit2 = (int)((1f - p.changeSpeed2) * (1f - p.changeSpeed2) * 20000f + 32f); } changeLimit = (int)(changeLimit * ((1f - p.changeRepeat + 0.1f) / 1.1f)); changeLimit2 = (int)(changeLimit2 * ((1f - p.changeRepeat + 0.1f) / 1.1f)); if (!totalReset) { return; } masterVolume = p.masterVolume * p.masterVolume; waveType = p.waveType; if (p.sustainTime < 0.01) { p.sustainTime = 0.01f; } var totalTime = p.attackTime + p.sustainTime + p.decayTime; if (totalTime < 0.18f) { var multiplier = 0.18f / totalTime; p.attackTime *= multiplier; p.sustainTime *= multiplier; p.decayTime *= multiplier; } sustainPunch = p.sustainPunch; phase = 0; overtones = (int)(p.overtones * 10f); overtoneFalloff = p.overtoneFalloff; minFrequency = p.minFrequency; bitcrushFreq = 1f - Mathf.Pow(p.bitCrush, 1f / 3f); bitcrushFreqSweep = -p.bitCrushSweep * 0.000015f; bitcrushPhase = 0; bitcrushLast = 0; compressionFactor = 1f / (1f + 4f * p.compressionAmount); filters = p.lpFilterCutoff != 1.0 || p.hpFilterCutoff != 0.0; lpFilterPos = 0.0f; lpFilterDeltaPos = 0.0f; lpFilterCutoff = p.lpFilterCutoff * p.lpFilterCutoff * p.lpFilterCutoff * 0.1f; lpFilterDeltaCutoff = 1.0f + p.lpFilterCutoffSweep * 0.0001f; lpFilterDamping = 5.0f / (1.0f + p.lpFilterResonance * p.lpFilterResonance * 20.0f) * (0.01f + lpFilterCutoff); if (lpFilterDamping > 0.8f) { lpFilterDamping = 0.8f; } lpFilterDamping = 1.0f - lpFilterDamping; lpFilterOn = p.lpFilterCutoff != 1.0f; hpFilterPos = 0.0f; hpFilterCutoff = p.hpFilterCutoff * p.hpFilterCutoff * 0.1f; hpFilterDeltaCutoff = 1.0f + p.hpFilterCutoffSweep * 0.0003f; vibratoPhase = 0.0f; vibratoSpeed = p.vibratoSpeed * p.vibratoSpeed * 0.01f; vibratoAmplitude = p.vibratoDepth * 0.5f; envelopeVolume = 0.0f; envelopeStage = 0; envelopeTime = 0; envelopeLength0 = p.attackTime * p.attackTime * 100000.0f; envelopeLength1 = p.sustainTime * p.sustainTime * 100000.0f; envelopeLength2 = p.decayTime * p.decayTime * 100000.0f + 10f; envelopeLength = envelopeLength0; envelopeFullLength = (uint)(envelopeLength0 + envelopeLength1 + envelopeLength2); envelopeOverLength0 = 1.0f / envelopeLength0; envelopeOverLength1 = 1.0f / envelopeLength1; envelopeOverLength2 = 1.0f / envelopeLength2; phaser = p.phaserOffset != 0.0f || p.phaserSweep != 0.0f; phaserOffset = p.phaserOffset * p.phaserOffset * 1020.0f; if (p.phaserOffset < 0.0f) { phaserOffset = -phaserOffset; } phaserDeltaOffset = p.phaserSweep * p.phaserSweep * p.phaserSweep * 0.2f; phaserPos = 0; if (phaserBuffer == null) { phaserBuffer = new float[1024]; } if (noiseBuffer == null) { noiseBuffer = new float[32]; } if (pinkNoiseBuffer == null) { pinkNoiseBuffer = new float[32]; } if (pinkNumber == null) { pinkNumber = new PinkNumber(); } if (loResNoiseBuffer == null) { loResNoiseBuffer = new float[32]; } uint i; for (i = 0; i < 1024; i++) { phaserBuffer[i] = 0.0f; } for (i = 0; i < 32; i++) { noiseBuffer[i] = SfxrParams.GetRandom() * 2.0f - 1.0f; } for (i = 0; i < 32; i++) { pinkNoiseBuffer[i] = pinkNumber.getNextValue(); } for (i = 0; i < 32; i++) { loResNoiseBuffer[i] = i % LoResNoisePeriod == 0 ? SfxrParams.GetRandom() * 2.0f - 1.0f : loResNoiseBuffer[i - 1]; } repeatTime = 0; if (p.repeatSpeed == 0.0) { repeatLimit = 0; } else { repeatLimit = (int)((1.0 - p.repeatSpeed) * (1.0 - p.repeatSpeed) * 20000) + 32; } }