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.

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