123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // See https://github.com/OneLoneCoder/synth
- #ifndef SYNTH_HPP
- #define SYNTH_HPP
- ////////////////////////////////////////////////////////////
- // Headers
- ////////////////////////////////////////////////////////////
- #include <SFML/Audio.hpp>
- #include <cmath>
- namespace Synth
- {
- enum eWaveType { OSC_SINE, OSC_SQUARE, OSC_TRIANGLE, OSC_SAW_ANA, OSC_SAW_DIG, OSC_NOISE };
- struct sEnvelope
- {
- double dAttackTime = 0.0;
- double dDecayTime = 0.0;
- double dSustainTime = 1.0;
- double dReleaseTime = 0.0;
- double dStartAmplitude = 1.0;
- double dSustainAmplitude = 1.0;
- };
- struct sTone
- {
- eWaveType waveType = OSC_SINE;
- double dStartFrequency = 440.0;
- double dEndFrequency = 440.0;
- double dAmplitude = 1.0;
- };
-
- ////////////////////////////////////////////////////////////
- // Converts frequency (Hz) to angular velocity
- ////////////////////////////////////////////////////////////
- const double PI = 3.14159265359;
- double w(const double dHertz)
- {
- return dHertz * 2.0 * PI;
- }
- ////////////////////////////////////////////////////////////
- // Multi-Function Oscillator
- //
- // This function was mostly "borrowed" from One Lone Coder blog at
- // wwww.onelonecoder.com
- //
- ////////////////////////////////////////////////////////////
- double osc(double dHertz, double dTime, eWaveType waveType)
- {
- switch (waveType)
- {
- case OSC_SINE: // Sine wave bewteen -1 and +1
- return sin(w(dHertz) * dTime);
- case OSC_SQUARE: // Square wave between -1 and +1
- return sin(w(dHertz) * dTime) > 0 ? 1.0 : -1.0;
- case OSC_TRIANGLE: // Triangle wave between -1 and +1
- return asin(sin(w(dHertz) * dTime)) * (2.0 / PI);
- case OSC_SAW_ANA: // Saw wave (analogue / warm / slow)
- {
- double dOutput = 0.0;
- // this variable defines warmth, larger the number harsher and more similar do OSC_SAW_DIG sound becomes
- double dWarmth = 30.0;
- for (double n = 1.0; n < dWarmth; n++)
- dOutput += (sin(n * w(dHertz) * dTime)) / n;
- return dOutput * (2.0 / PI);
- }
- case OSC_SAW_DIG: // Saw Wave (optimised / harsh / fast)
- return (2.0 / PI) * (dHertz * PI * fmod(dTime, 1.0 / dHertz) - (PI / 2.0));
- case OSC_NOISE: // Pseudorandom noise
- return 2.0 * ((double)rand() / (double)RAND_MAX) - 1.0;
- default:
- return 0.0;
- }
- }
- ////////////////////////////////////////////////////////////
- // Amplitude Modulator
- ////////////////////////////////////////////////////////////
- double amplitude(double dTime, sEnvelope env)
- {
- // double dTimeOn = 0;
- double dTimeOff = env.dAttackTime + env.dDecayTime + env.dSustainTime;
- double dAmplitude = 0.0;
- if (dTime > dTimeOff) // Release phase
- dAmplitude = ((dTime - dTimeOff) / env.dReleaseTime) * (0.0 - env.dSustainAmplitude) + env.dSustainAmplitude;
- else if (dTime > (env.dAttackTime + env.dDecayTime)) // Sustain phase
- dAmplitude = env.dSustainAmplitude;
- else if (dTime > env.dAttackTime && dTime <= (env.dAttackTime + env.dDecayTime)) // Decay phase
- dAmplitude = ((dTime - env.dAttackTime) / env.dDecayTime) * (env.dSustainAmplitude - env.dStartAmplitude) + env.dStartAmplitude;
- else if (dTime <= env.dAttackTime) // Attack phase
- dAmplitude = (dTime / env.dAttackTime) * env.dStartAmplitude;
- // Amplitude should not be negative, check just in case
- if (dAmplitude <= 0.000)
- dAmplitude = 0.0;
- return dAmplitude;
- }
- ////////////////////////////////////////////////////////////
- /// \brief Generate sound and store in SoundBuffer
- ///
- /// This function uses case-in
- ///
- /// \param buffer is address to SoundBuffer where the result will be stored
- /// \param envelope structure defining the ADSR Envelope
- /// \param tones vector of tone structures to be stacked
- /// \param master volume for the volume of sound
- /// \param sample rate to set quality of sound
- ///
- /// \return True if the sound was generate, false if it failed
- ///
- ////////////////////////////////////////////////////////////
- bool generate(sf::SoundBuffer* buffer, sEnvelope env, std::vector<sTone> tones, unsigned uMasterVol, unsigned uSampleRate)
- {
- if (!buffer)
- return false;
- // Calculate and allocate buffer
- double dTotalDuration = env.dAttackTime + env.dDecayTime + env.dSustainTime + env.dReleaseTime;
- unsigned iBufferSize = unsigned(dTotalDuration * uSampleRate);
- sf::Int16 * iRaw;
- iRaw = new sf::Int16[iBufferSize];
- // Generate sound
- double dIncrement = 1.0 / double(uSampleRate);
- double dTime = 0.0;
- for (unsigned i = 0; i < iBufferSize; i++)
- {
- double signal = 0.0;
-
- // Generate multiple tones and stack them together
- for (sTone t : tones)
- {
- double dFrequency = t.dStartFrequency + ((t.dEndFrequency - t.dStartFrequency) * (double(i) / double(iBufferSize)));
- signal = signal + (osc(dFrequency, dTime, t.waveType) * t.dAmplitude);
- }
- // Calculate Amplitude based on ADSR envelope
- double dEnvelopeAmplitude = amplitude(dTime, env);
- // Apply master volume, envelope and store to buffer
- *(iRaw + i) = sf::Int16(uMasterVol * signal * dEnvelopeAmplitude);
- dTime += dIncrement;
- }
- // Load into SFML SoundBuffer
- bool bSuccess = buffer->loadFromSamples(iRaw, iBufferSize, 1, uSampleRate);
- delete[] iRaw;
- return bSuccess;
- }
- ////////////////////////////////////////////////////////////
- /// \brief Generate sound and store in SoundBuffer
- ///
- /// This function uses case-in
- ///
- /// \param buffer is address to SoundBuffer where the result will be stored
- /// \param envelope structure defining the ADSR Envelope
- /// \param tone structure for simple tone definition
- /// \param master volume for the volume of sound
- /// \param sample rate to set quality of sound
- ///
- /// \return True if the sound was generate, false if it failed
- ///
- ////////////////////////////////////////////////////////////
- bool generate(sf::SoundBuffer* buffer, sEnvelope env, sTone tone, unsigned uMasterVol, unsigned uSampleRate)
- {
- std::vector<sTone> tones;
- tones.push_back(tone);
- return generate(buffer, env, tones, uMasterVol, uSampleRate);
- }
- }
- #endif // SYNTH_HPP
|