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.

227 lines
6.8KB

  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. SamplerSound::SamplerSound (const String& soundName,
  20. AudioFormatReader& source,
  21. const BigInteger& notes,
  22. const int midiNoteForNormalPitch,
  23. const double attackTimeSecs,
  24. const double releaseTimeSecs,
  25. const double maxSampleLengthSeconds)
  26. : name (soundName),
  27. midiNotes (notes),
  28. midiRootNote (midiNoteForNormalPitch)
  29. {
  30. sourceSampleRate = source.sampleRate;
  31. if (sourceSampleRate <= 0 || source.lengthInSamples <= 0)
  32. {
  33. length = 0;
  34. attackSamples = 0;
  35. releaseSamples = 0;
  36. }
  37. else
  38. {
  39. length = jmin ((int) source.lengthInSamples,
  40. (int) (maxSampleLengthSeconds * sourceSampleRate));
  41. data = new AudioSampleBuffer (jmin (2, (int) source.numChannels), length + 4);
  42. source.read (data, 0, length + 4, 0, true, true);
  43. attackSamples = roundToInt (attackTimeSecs * sourceSampleRate);
  44. releaseSamples = roundToInt (releaseTimeSecs * sourceSampleRate);
  45. }
  46. }
  47. SamplerSound::~SamplerSound()
  48. {
  49. }
  50. bool SamplerSound::appliesToNote (int midiNoteNumber)
  51. {
  52. return midiNotes [midiNoteNumber];
  53. }
  54. bool SamplerSound::appliesToChannel (int /*midiChannel*/)
  55. {
  56. return true;
  57. }
  58. //==============================================================================
  59. SamplerVoice::SamplerVoice()
  60. : pitchRatio (0.0),
  61. sourceSamplePosition (0.0),
  62. lgain (0.0f), rgain (0.0f),
  63. attackReleaseLevel (0), attackDelta (0), releaseDelta (0),
  64. isInAttack (false), isInRelease (false)
  65. {
  66. }
  67. SamplerVoice::~SamplerVoice()
  68. {
  69. }
  70. bool SamplerVoice::canPlaySound (SynthesiserSound* sound)
  71. {
  72. return dynamic_cast<const SamplerSound*> (sound) != nullptr;
  73. }
  74. void SamplerVoice::startNote (const int midiNoteNumber,
  75. const float velocity,
  76. SynthesiserSound* s,
  77. const int /*currentPitchWheelPosition*/)
  78. {
  79. if (const SamplerSound* const sound = dynamic_cast<const SamplerSound*> (s))
  80. {
  81. pitchRatio = pow (2.0, (midiNoteNumber - sound->midiRootNote) / 12.0)
  82. * sound->sourceSampleRate / getSampleRate();
  83. sourceSamplePosition = 0.0;
  84. lgain = velocity;
  85. rgain = velocity;
  86. isInAttack = (sound->attackSamples > 0);
  87. isInRelease = false;
  88. if (isInAttack)
  89. {
  90. attackReleaseLevel = 0.0f;
  91. attackDelta = (float) (pitchRatio / sound->attackSamples);
  92. }
  93. else
  94. {
  95. attackReleaseLevel = 1.0f;
  96. attackDelta = 0.0f;
  97. }
  98. if (sound->releaseSamples > 0)
  99. releaseDelta = (float) (-pitchRatio / sound->releaseSamples);
  100. else
  101. releaseDelta = -1.0f;
  102. }
  103. else
  104. {
  105. jassertfalse; // this object can only play SamplerSounds!
  106. }
  107. }
  108. void SamplerVoice::stopNote (float /*velocity*/, bool allowTailOff)
  109. {
  110. if (allowTailOff)
  111. {
  112. isInAttack = false;
  113. isInRelease = true;
  114. }
  115. else
  116. {
  117. clearCurrentNote();
  118. }
  119. }
  120. void SamplerVoice::pitchWheelMoved (const int /*newValue*/)
  121. {
  122. }
  123. void SamplerVoice::controllerMoved (const int /*controllerNumber*/,
  124. const int /*newValue*/)
  125. {
  126. }
  127. //==============================================================================
  128. void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples)
  129. {
  130. if (const SamplerSound* const playingSound = static_cast<SamplerSound*> (getCurrentlyPlayingSound().get()))
  131. {
  132. const float* const inL = playingSound->data->getReadPointer (0);
  133. const float* const inR = playingSound->data->getNumChannels() > 1
  134. ? playingSound->data->getReadPointer (1) : nullptr;
  135. float* outL = outputBuffer.getWritePointer (0, startSample);
  136. float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample) : nullptr;
  137. while (--numSamples >= 0)
  138. {
  139. const int pos = (int) sourceSamplePosition;
  140. const float alpha = (float) (sourceSamplePosition - pos);
  141. const float invAlpha = 1.0f - alpha;
  142. // just using a very simple linear interpolation here..
  143. float l = (inL [pos] * invAlpha + inL [pos + 1] * alpha);
  144. float r = (inR != nullptr) ? (inR [pos] * invAlpha + inR [pos + 1] * alpha)
  145. : l;
  146. l *= lgain;
  147. r *= rgain;
  148. if (isInAttack)
  149. {
  150. l *= attackReleaseLevel;
  151. r *= attackReleaseLevel;
  152. attackReleaseLevel += attackDelta;
  153. if (attackReleaseLevel >= 1.0f)
  154. {
  155. attackReleaseLevel = 1.0f;
  156. isInAttack = false;
  157. }
  158. }
  159. else if (isInRelease)
  160. {
  161. l *= attackReleaseLevel;
  162. r *= attackReleaseLevel;
  163. attackReleaseLevel += releaseDelta;
  164. if (attackReleaseLevel <= 0.0f)
  165. {
  166. stopNote (0.0f, false);
  167. break;
  168. }
  169. }
  170. if (outR != nullptr)
  171. {
  172. *outL++ += l;
  173. *outR++ += r;
  174. }
  175. else
  176. {
  177. *outL++ += (l + r) * 0.5f;
  178. }
  179. sourceSamplePosition += pitchRatio;
  180. if (sourceSamplePosition > playingSound->length)
  181. {
  182. stopNote (0.0f, false);
  183. break;
  184. }
  185. }
  186. }
  187. }