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.

167 lines
5.2KB

  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. namespace juce
  20. {
  21. SamplerSound::SamplerSound (const String& soundName,
  22. AudioFormatReader& source,
  23. const BigInteger& notes,
  24. int midiNoteForNormalPitch,
  25. double attackTimeSecs,
  26. double releaseTimeSecs,
  27. double maxSampleLengthSeconds)
  28. : name (soundName),
  29. sourceSampleRate (source.sampleRate),
  30. midiNotes (notes),
  31. midiRootNote (midiNoteForNormalPitch)
  32. {
  33. if (sourceSampleRate > 0 && source.lengthInSamples > 0)
  34. {
  35. length = jmin ((int) source.lengthInSamples,
  36. (int) (maxSampleLengthSeconds * sourceSampleRate));
  37. data.reset (new AudioBuffer<float> (jmin (2, (int) source.numChannels), length + 4));
  38. source.read (data.get(), 0, length + 4, 0, true, true);
  39. params.attack = static_cast<float> (attackTimeSecs);
  40. params.release = static_cast<float> (releaseTimeSecs);
  41. }
  42. }
  43. SamplerSound::~SamplerSound()
  44. {
  45. }
  46. bool SamplerSound::appliesToNote (int midiNoteNumber)
  47. {
  48. return midiNotes[midiNoteNumber];
  49. }
  50. bool SamplerSound::appliesToChannel (int /*midiChannel*/)
  51. {
  52. return true;
  53. }
  54. //==============================================================================
  55. SamplerVoice::SamplerVoice() {}
  56. SamplerVoice::~SamplerVoice() {}
  57. bool SamplerVoice::canPlaySound (SynthesiserSound* sound)
  58. {
  59. return dynamic_cast<const SamplerSound*> (sound) != nullptr;
  60. }
  61. void SamplerVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound* s, int /*currentPitchWheelPosition*/)
  62. {
  63. if (auto* sound = dynamic_cast<const SamplerSound*> (s))
  64. {
  65. pitchRatio = std::pow (2.0, (midiNoteNumber - sound->midiRootNote) / 12.0)
  66. * sound->sourceSampleRate / getSampleRate();
  67. sourceSamplePosition = 0.0;
  68. lgain = velocity;
  69. rgain = velocity;
  70. adsr.setSampleRate (sound->sourceSampleRate);
  71. adsr.setParameters (sound->params);
  72. adsr.noteOn();
  73. }
  74. else
  75. {
  76. jassertfalse; // this object can only play SamplerSounds!
  77. }
  78. }
  79. void SamplerVoice::stopNote (float /*velocity*/, bool allowTailOff)
  80. {
  81. if (allowTailOff)
  82. {
  83. adsr.noteOff();
  84. }
  85. else
  86. {
  87. clearCurrentNote();
  88. adsr.reset();
  89. }
  90. }
  91. void SamplerVoice::pitchWheelMoved (int /*newValue*/) {}
  92. void SamplerVoice::controllerMoved (int /*controllerNumber*/, int /*newValue*/) {}
  93. //==============================================================================
  94. void SamplerVoice::renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples)
  95. {
  96. if (auto* playingSound = static_cast<SamplerSound*> (getCurrentlyPlayingSound().get()))
  97. {
  98. auto& data = *playingSound->data;
  99. const float* const inL = data.getReadPointer (0);
  100. const float* const inR = data.getNumChannels() > 1 ? data.getReadPointer (1) : nullptr;
  101. float* outL = outputBuffer.getWritePointer (0, startSample);
  102. float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample) : nullptr;
  103. while (--numSamples >= 0)
  104. {
  105. auto pos = (int) sourceSamplePosition;
  106. auto alpha = (float) (sourceSamplePosition - pos);
  107. auto invAlpha = 1.0f - alpha;
  108. // just using a very simple linear interpolation here..
  109. float l = (inL[pos] * invAlpha + inL[pos + 1] * alpha);
  110. float r = (inR != nullptr) ? (inR[pos] * invAlpha + inR[pos + 1] * alpha)
  111. : l;
  112. auto envelopeValue = adsr.getNextSample();
  113. l *= lgain * envelopeValue;
  114. r *= rgain * envelopeValue;
  115. if (outR != nullptr)
  116. {
  117. *outL++ += l;
  118. *outR++ += r;
  119. }
  120. else
  121. {
  122. *outL++ += (l + r) * 0.5f;
  123. }
  124. sourceSamplePosition += pitchRatio;
  125. if (sourceSamplePosition > playingSound->length)
  126. {
  127. stopNote (0.0f, false);
  128. break;
  129. }
  130. }
  131. }
  132. }
  133. } // namespace juce