Synth.hpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // See https://github.com/OneLoneCoder/synth
  2. #ifndef SYNTH_HPP
  3. #define SYNTH_HPP
  4. ////////////////////////////////////////////////////////////
  5. // Headers
  6. ////////////////////////////////////////////////////////////
  7. #include <SFML/Audio.hpp>
  8. #include <cmath>
  9. namespace Synth
  10. {
  11. enum eWaveType { OSC_SINE, OSC_SQUARE, OSC_TRIANGLE, OSC_SAW_ANA, OSC_SAW_DIG, OSC_NOISE };
  12. struct sEnvelope
  13. {
  14. double dAttackTime = 0.0;
  15. double dDecayTime = 0.0;
  16. double dSustainTime = 1.0;
  17. double dReleaseTime = 0.0;
  18. double dStartAmplitude = 1.0;
  19. double dSustainAmplitude = 1.0;
  20. };
  21. struct sTone
  22. {
  23. eWaveType waveType = OSC_SINE;
  24. double dStartFrequency = 440.0;
  25. double dEndFrequency = 440.0;
  26. double dAmplitude = 1.0;
  27. };
  28. ////////////////////////////////////////////////////////////
  29. // Converts frequency (Hz) to angular velocity
  30. ////////////////////////////////////////////////////////////
  31. const double PI = 3.14159265359;
  32. double w(const double dHertz)
  33. {
  34. return dHertz * 2.0 * PI;
  35. }
  36. ////////////////////////////////////////////////////////////
  37. // Multi-Function Oscillator
  38. //
  39. // This function was mostly "borrowed" from One Lone Coder blog at
  40. // wwww.onelonecoder.com
  41. //
  42. ////////////////////////////////////////////////////////////
  43. double osc(double dHertz, double dTime, eWaveType waveType)
  44. {
  45. switch (waveType)
  46. {
  47. case OSC_SINE: // Sine wave bewteen -1 and +1
  48. return sin(w(dHertz) * dTime);
  49. case OSC_SQUARE: // Square wave between -1 and +1
  50. return sin(w(dHertz) * dTime) > 0 ? 1.0 : -1.0;
  51. case OSC_TRIANGLE: // Triangle wave between -1 and +1
  52. return asin(sin(w(dHertz) * dTime)) * (2.0 / PI);
  53. case OSC_SAW_ANA: // Saw wave (analogue / warm / slow)
  54. {
  55. double dOutput = 0.0;
  56. // this variable defines warmth, larger the number harsher and more similar do OSC_SAW_DIG sound becomes
  57. double dWarmth = 30.0;
  58. for (double n = 1.0; n < dWarmth; n++)
  59. dOutput += (sin(n * w(dHertz) * dTime)) / n;
  60. return dOutput * (2.0 / PI);
  61. }
  62. case OSC_SAW_DIG: // Saw Wave (optimised / harsh / fast)
  63. return (2.0 / PI) * (dHertz * PI * fmod(dTime, 1.0 / dHertz) - (PI / 2.0));
  64. case OSC_NOISE: // Pseudorandom noise
  65. return 2.0 * ((double)rand() / (double)RAND_MAX) - 1.0;
  66. default:
  67. return 0.0;
  68. }
  69. }
  70. ////////////////////////////////////////////////////////////
  71. // Amplitude Modulator
  72. ////////////////////////////////////////////////////////////
  73. double amplitude(double dTime, sEnvelope env)
  74. {
  75. // double dTimeOn = 0;
  76. double dTimeOff = env.dAttackTime + env.dDecayTime + env.dSustainTime;
  77. double dAmplitude = 0.0;
  78. if (dTime > dTimeOff) // Release phase
  79. dAmplitude = ((dTime - dTimeOff) / env.dReleaseTime) * (0.0 - env.dSustainAmplitude) + env.dSustainAmplitude;
  80. else if (dTime > (env.dAttackTime + env.dDecayTime)) // Sustain phase
  81. dAmplitude = env.dSustainAmplitude;
  82. else if (dTime > env.dAttackTime && dTime <= (env.dAttackTime + env.dDecayTime)) // Decay phase
  83. dAmplitude = ((dTime - env.dAttackTime) / env.dDecayTime) * (env.dSustainAmplitude - env.dStartAmplitude) + env.dStartAmplitude;
  84. else if (dTime <= env.dAttackTime) // Attack phase
  85. dAmplitude = (dTime / env.dAttackTime) * env.dStartAmplitude;
  86. // Amplitude should not be negative, check just in case
  87. if (dAmplitude <= 0.000)
  88. dAmplitude = 0.0;
  89. return dAmplitude;
  90. }
  91. ////////////////////////////////////////////////////////////
  92. /// \brief Generate sound and store in SoundBuffer
  93. ///
  94. /// This function uses case-in
  95. ///
  96. /// \param buffer is address to SoundBuffer where the result will be stored
  97. /// \param envelope structure defining the ADSR Envelope
  98. /// \param tones vector of tone structures to be stacked
  99. /// \param master volume for the volume of sound
  100. /// \param sample rate to set quality of sound
  101. ///
  102. /// \return True if the sound was generate, false if it failed
  103. ///
  104. ////////////////////////////////////////////////////////////
  105. bool generate(sf::SoundBuffer* buffer, sEnvelope env, std::vector<sTone> tones, unsigned uMasterVol, unsigned uSampleRate)
  106. {
  107. if (!buffer)
  108. return false;
  109. // Calculate and allocate buffer
  110. double dTotalDuration = env.dAttackTime + env.dDecayTime + env.dSustainTime + env.dReleaseTime;
  111. unsigned iBufferSize = unsigned(dTotalDuration * uSampleRate);
  112. sf::Int16 * iRaw;
  113. iRaw = new sf::Int16[iBufferSize];
  114. // Generate sound
  115. double dIncrement = 1.0 / double(uSampleRate);
  116. double dTime = 0.0;
  117. for (unsigned i = 0; i < iBufferSize; i++)
  118. {
  119. double signal = 0.0;
  120. // Generate multiple tones and stack them together
  121. for (sTone t : tones)
  122. {
  123. double dFrequency = t.dStartFrequency + ((t.dEndFrequency - t.dStartFrequency) * (double(i) / double(iBufferSize)));
  124. signal = signal + (osc(dFrequency, dTime, t.waveType) * t.dAmplitude);
  125. }
  126. // Calculate Amplitude based on ADSR envelope
  127. double dEnvelopeAmplitude = amplitude(dTime, env);
  128. // Apply master volume, envelope and store to buffer
  129. *(iRaw + i) = sf::Int16(uMasterVol * signal * dEnvelopeAmplitude);
  130. dTime += dIncrement;
  131. }
  132. // Load into SFML SoundBuffer
  133. bool bSuccess = buffer->loadFromSamples(iRaw, iBufferSize, 1, uSampleRate);
  134. delete[] iRaw;
  135. return bSuccess;
  136. }
  137. ////////////////////////////////////////////////////////////
  138. /// \brief Generate sound and store in SoundBuffer
  139. ///
  140. /// This function uses case-in
  141. ///
  142. /// \param buffer is address to SoundBuffer where the result will be stored
  143. /// \param envelope structure defining the ADSR Envelope
  144. /// \param tone structure for simple tone definition
  145. /// \param master volume for the volume of sound
  146. /// \param sample rate to set quality of sound
  147. ///
  148. /// \return True if the sound was generate, false if it failed
  149. ///
  150. ////////////////////////////////////////////////////////////
  151. bool generate(sf::SoundBuffer* buffer, sEnvelope env, sTone tone, unsigned uMasterVol, unsigned uSampleRate)
  152. {
  153. std::vector<sTone> tones;
  154. tones.push_back(tone);
  155. return generate(buffer, env, tones, uMasterVol, uSampleRate);
  156. }
  157. }
  158. #endif // SYNTH_HPP