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.

241 lines
7.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. SamplerSound::SamplerSound (const String& name_,
  21. AudioFormatReader& source,
  22. const BigInteger& midiNotes_,
  23. const int midiNoteForNormalPitch,
  24. const double attackTimeSecs,
  25. const double releaseTimeSecs,
  26. const double maxSampleLengthSeconds)
  27. : name (name_),
  28. midiNotes (midiNotes_),
  29. midiRootNote (midiNoteForNormalPitch)
  30. {
  31. sourceSampleRate = source.sampleRate;
  32. if (sourceSampleRate <= 0 || source.lengthInSamples <= 0)
  33. {
  34. length = 0;
  35. attackSamples = 0;
  36. releaseSamples = 0;
  37. }
  38. else
  39. {
  40. length = jmin ((int) source.lengthInSamples,
  41. (int) (maxSampleLengthSeconds * sourceSampleRate));
  42. data = new AudioSampleBuffer (jmin (2, (int) source.numChannels), length + 4);
  43. source.read (data, 0, length + 4, 0, true, true);
  44. attackSamples = roundToInt (attackTimeSecs * sourceSampleRate);
  45. releaseSamples = roundToInt (releaseTimeSecs * sourceSampleRate);
  46. }
  47. }
  48. SamplerSound::~SamplerSound()
  49. {
  50. }
  51. //==============================================================================
  52. bool SamplerSound::appliesToNote (const int midiNoteNumber)
  53. {
  54. return midiNotes [midiNoteNumber];
  55. }
  56. bool SamplerSound::appliesToChannel (const int /*midiChannel*/)
  57. {
  58. return true;
  59. }
  60. //==============================================================================
  61. SamplerVoice::SamplerVoice()
  62. : pitchRatio (0.0),
  63. sourceSamplePosition (0.0),
  64. lgain (0.0f),
  65. rgain (0.0f),
  66. isInAttack (false),
  67. isInRelease (false)
  68. {
  69. }
  70. SamplerVoice::~SamplerVoice()
  71. {
  72. }
  73. bool SamplerVoice::canPlaySound (SynthesiserSound* sound)
  74. {
  75. return dynamic_cast <const SamplerSound*> (sound) != nullptr;
  76. }
  77. void SamplerVoice::startNote (const int midiNoteNumber,
  78. const float velocity,
  79. SynthesiserSound* s,
  80. const int /*currentPitchWheelPosition*/)
  81. {
  82. const SamplerSound* const sound = dynamic_cast <const SamplerSound*> (s);
  83. jassert (sound != nullptr); // this object can only play SamplerSounds!
  84. if (sound != nullptr)
  85. {
  86. const double targetFreq = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
  87. const double naturalFreq = MidiMessage::getMidiNoteInHertz (sound->midiRootNote);
  88. pitchRatio = (targetFreq * sound->sourceSampleRate) / (naturalFreq * getSampleRate());
  89. sourceSamplePosition = 0.0;
  90. lgain = velocity;
  91. rgain = velocity;
  92. isInAttack = (sound->attackSamples > 0);
  93. isInRelease = false;
  94. if (isInAttack)
  95. {
  96. attackReleaseLevel = 0.0f;
  97. attackDelta = (float) (pitchRatio / sound->attackSamples);
  98. }
  99. else
  100. {
  101. attackReleaseLevel = 1.0f;
  102. attackDelta = 0.0f;
  103. }
  104. if (sound->releaseSamples > 0)
  105. {
  106. releaseDelta = (float) (-pitchRatio / sound->releaseSamples);
  107. }
  108. else
  109. {
  110. releaseDelta = 0.0f;
  111. }
  112. }
  113. }
  114. void SamplerVoice::stopNote (const bool allowTailOff)
  115. {
  116. if (allowTailOff)
  117. {
  118. isInAttack = false;
  119. isInRelease = true;
  120. }
  121. else
  122. {
  123. clearCurrentNote();
  124. }
  125. }
  126. void SamplerVoice::pitchWheelMoved (const int /*newValue*/)
  127. {
  128. }
  129. void SamplerVoice::controllerMoved (const int /*controllerNumber*/,
  130. const int /*newValue*/)
  131. {
  132. }
  133. //==============================================================================
  134. void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples)
  135. {
  136. const SamplerSound* const playingSound = static_cast <SamplerSound*> (getCurrentlyPlayingSound().getObject());
  137. if (playingSound != nullptr)
  138. {
  139. const float* const inL = playingSound->data->getSampleData (0, 0);
  140. const float* const inR = playingSound->data->getNumChannels() > 1
  141. ? playingSound->data->getSampleData (1, 0) : nullptr;
  142. float* outL = outputBuffer.getSampleData (0, startSample);
  143. float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getSampleData (1, startSample) : nullptr;
  144. while (--numSamples >= 0)
  145. {
  146. const int pos = (int) sourceSamplePosition;
  147. const float alpha = (float) (sourceSamplePosition - pos);
  148. const float invAlpha = 1.0f - alpha;
  149. // just using a very simple linear interpolation here..
  150. float l = (inL [pos] * invAlpha + inL [pos + 1] * alpha);
  151. float r = (inR != nullptr) ? (inR [pos] * invAlpha + inR [pos + 1] * alpha)
  152. : l;
  153. l *= lgain;
  154. r *= rgain;
  155. if (isInAttack)
  156. {
  157. l *= attackReleaseLevel;
  158. r *= attackReleaseLevel;
  159. attackReleaseLevel += attackDelta;
  160. if (attackReleaseLevel >= 1.0f)
  161. {
  162. attackReleaseLevel = 1.0f;
  163. isInAttack = false;
  164. }
  165. }
  166. else if (isInRelease)
  167. {
  168. l *= attackReleaseLevel;
  169. r *= attackReleaseLevel;
  170. attackReleaseLevel += releaseDelta;
  171. if (attackReleaseLevel <= 0.0f)
  172. {
  173. stopNote (false);
  174. break;
  175. }
  176. }
  177. if (outR != nullptr)
  178. {
  179. *outL++ += l;
  180. *outR++ += r;
  181. }
  182. else
  183. {
  184. *outL++ += (l + r) * 0.5f;
  185. }
  186. sourceSamplePosition += pitchRatio;
  187. if (sourceSamplePosition > playingSound->length)
  188. {
  189. stopNote (false);
  190. break;
  191. }
  192. }
  193. }
  194. }
  195. END_JUCE_NAMESPACE