The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
4.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. /** A demo synth sound that's just a basic sine wave.. */
  20. class SineWaveSound : public SynthesiserSound
  21. {
  22. public:
  23. SineWaveSound() {}
  24. bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
  25. bool appliesToChannel (int /*midiChannel*/) override { return true; }
  26. };
  27. //==============================================================================
  28. /** A simple demo synth voice that just plays a sine wave.. */
  29. class SineWaveVoice : public SynthesiserVoice
  30. {
  31. public:
  32. SineWaveVoice()
  33. : currentAngle (0), angleDelta (0), level (0), tailOff (0)
  34. {
  35. }
  36. bool canPlaySound (SynthesiserSound* sound) override
  37. {
  38. return dynamic_cast<SineWaveSound*> (sound) != nullptr;
  39. }
  40. void startNote (int midiNoteNumber, float velocity,
  41. SynthesiserSound* /*sound*/,
  42. int /*currentPitchWheelPosition*/) override
  43. {
  44. currentAngle = 0.0;
  45. level = velocity * 0.15;
  46. tailOff = 0.0;
  47. double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
  48. double cyclesPerSample = cyclesPerSecond / getSampleRate();
  49. angleDelta = cyclesPerSample * 2.0 * double_Pi;
  50. }
  51. void stopNote (float /*velocity*/, bool allowTailOff) override
  52. {
  53. if (allowTailOff)
  54. {
  55. // start a tail-off by setting this flag. The render callback will pick up on
  56. // this and do a fade out, calling clearCurrentNote() when it's finished.
  57. if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
  58. // stopNote method could be called more than once.
  59. tailOff = 1.0;
  60. }
  61. else
  62. {
  63. // we're being told to stop playing immediately, so reset everything..
  64. clearCurrentNote();
  65. angleDelta = 0.0;
  66. }
  67. }
  68. void pitchWheelMoved (int /*newValue*/) override
  69. {
  70. // not implemented for the purposes of this demo!
  71. }
  72. void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override
  73. {
  74. // not implemented for the purposes of this demo!
  75. }
  76. void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override
  77. {
  78. if (angleDelta != 0.0)
  79. {
  80. if (tailOff > 0)
  81. {
  82. while (--numSamples >= 0)
  83. {
  84. const float currentSample = (float) (sin (currentAngle) * level * tailOff);
  85. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  86. outputBuffer.addSample (i, startSample, currentSample);
  87. currentAngle += angleDelta;
  88. ++startSample;
  89. tailOff *= 0.99;
  90. if (tailOff <= 0.005)
  91. {
  92. // tells the synth that this voice has stopped
  93. clearCurrentNote();
  94. angleDelta = 0.0;
  95. break;
  96. }
  97. }
  98. }
  99. else
  100. {
  101. while (--numSamples >= 0)
  102. {
  103. const float currentSample = (float) (sin (currentAngle) * level);
  104. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  105. outputBuffer.addSample (i, startSample, currentSample);
  106. currentAngle += angleDelta;
  107. ++startSample;
  108. }
  109. }
  110. }
  111. }
  112. private:
  113. double currentAngle, angleDelta, level, tailOff;
  114. };