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.

178 lines
7.1KB

  1. /*
  2. ==============================================================================
  3. Spatializer.cpp
  4. Created: 23 Nov 2015 3:08:33pm
  5. Author: Fabian Renn
  6. ==============================================================================
  7. */
  8. #include "../JuceLibraryCode/JuceHeader.h"
  9. #include "../../GenericEditor.h"
  10. //==============================================================================
  11. /**
  12. */
  13. class Spatializer : public AudioProcessor
  14. {
  15. public:
  16. struct SpeakerPosition
  17. {
  18. float radius, phi;
  19. };
  20. struct SpeakerLayout
  21. {
  22. AudioChannelSet set;
  23. Array<SpeakerPosition> positions;
  24. };
  25. // this needs at least c++11
  26. static Array<SpeakerLayout> speakerPositions;
  27. //==============================================================================
  28. Spatializer() : currentSpeakerLayout (0)
  29. {
  30. // clear the default bus arrangements which were created by the base class
  31. busArrangement.inputBuses .clear();
  32. busArrangement.outputBuses.clear();
  33. // add mono in and default out
  34. busArrangement.inputBuses .add (AudioProcessorBus ("Input", AudioChannelSet::mono()));
  35. busArrangement.outputBuses.add (AudioProcessorBus ("Output", speakerPositions[currentSpeakerLayout].set));
  36. addParameter (radius = new AudioParameterFloat ("radius", "Radius", 0.0f, 1.0f, 0.5f));
  37. addParameter (phi = new AudioParameterFloat ("phi", "Phi", 0.0f, 1.0f, 0.0f));
  38. }
  39. ~Spatializer() {}
  40. //==============================================================================
  41. bool setPreferredBusArrangement (bool isInputBus, int busIndex,
  42. const AudioChannelSet& preferred) override
  43. {
  44. // we only allow mono in
  45. if (isInputBus && preferred != AudioChannelSet::mono())
  46. return false;
  47. // the output must be one of the supported speaker layouts
  48. if (! isInputBus)
  49. {
  50. int i;
  51. for (i = 0; i < speakerPositions.size(); ++i)
  52. if (speakerPositions[i].set == preferred) break;
  53. if (i >= speakerPositions.size())
  54. return false;
  55. currentSpeakerLayout = i;
  56. }
  57. return AudioProcessor::setPreferredBusArrangement (isInputBus, busIndex, preferred);
  58. }
  59. //==============================================================================
  60. void prepareToPlay (double sampleRate, int samplesPerBlock) override
  61. {
  62. scratchBuffer.setSize (1, samplesPerBlock);
  63. }
  64. void releaseResources() override {}
  65. void processBlock (AudioSampleBuffer& buffer, MidiBuffer&) override
  66. {
  67. // copy the input into a scratch buffer
  68. AudioSampleBuffer scratch (scratchBuffer.getArrayOfWritePointers(), 1, buffer.getNumSamples());
  69. scratch.copyFrom(0, 0, buffer, 0, 0, buffer.getNumSamples());
  70. const Array<SpeakerPosition>& positions = speakerPositions.getReference (currentSpeakerLayout).positions;
  71. const float* inputBuffer = scratch.getReadPointer (0);
  72. const float kMaxDistanceGain = -20.0f;
  73. for (int speakerIdx = 0; speakerIdx < positions.size(); ++speakerIdx)
  74. {
  75. const SpeakerPosition& speakerPos = positions.getReference (speakerIdx);
  76. float fltDistance = distance (polarToCartesian (speakerPos.radius, speakerPos.phi), polarToCartesian (*radius, (*phi) * 2.0f * float_Pi));
  77. float gainInDb = kMaxDistanceGain * (fltDistance / 2.0f);
  78. float gain = std::pow (10.0f, (gainInDb / 20.0f));
  79. busArrangement.getBusBuffer(buffer, false, 0).copyFrom(speakerIdx, 0, inputBuffer, buffer.getNumSamples(), gain);
  80. }
  81. }
  82. //==============================================================================
  83. AudioProcessorEditor* createEditor() override { return new GenericEditor (*this); }
  84. bool hasEditor() const override { return true; }
  85. //==============================================================================
  86. const String getName() const override { return "Gain PlugIn"; }
  87. bool acceptsMidi() const override { return false; }
  88. bool producesMidi() const override { return false; }
  89. bool silenceInProducesSilenceOut() const override { return true; }
  90. double getTailLengthSeconds() const override { return 0; }
  91. //==============================================================================
  92. int getNumPrograms() override { return 1; }
  93. int getCurrentProgram() override { return 0; }
  94. void setCurrentProgram (int) override {}
  95. const String getProgramName (int) override { return String(); }
  96. void changeProgramName (int , const String& ) override { }
  97. //==============================================================================
  98. void getStateInformation (MemoryBlock& destData) override
  99. {
  100. MemoryOutputStream stream (destData, true);
  101. stream.writeFloat (*radius);
  102. stream.writeFloat (*phi);
  103. }
  104. void setStateInformation (const void* data, int sizeInBytes) override
  105. {
  106. MemoryInputStream stream (data, sizeInBytes, false);
  107. radius->setValueNotifyingHost (stream.readFloat());
  108. phi->setValueNotifyingHost (stream.readFloat());
  109. }
  110. private:
  111. //==============================================================================
  112. AudioParameterFloat* radius;
  113. AudioParameterFloat* phi;
  114. int currentSpeakerLayout;
  115. AudioSampleBuffer scratchBuffer;
  116. static Point<float> polarToCartesian (float r, float phi) noexcept
  117. {
  118. return Point<float> (r * std::cos (phi), r * std::sin (phi));
  119. }
  120. static float distance (Point<float> a, Point<float> b) noexcept
  121. {
  122. return std::sqrt (std::pow (a.x - b.x, 2.0f) + std::pow (a.y - b.y, 2.0f));
  123. }
  124. //==============================================================================
  125. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Spatializer)
  126. };
  127. // this needs at least c++11
  128. Array<Spatializer::SpeakerLayout> Spatializer::speakerPositions =
  129. {
  130. Spatializer::SpeakerLayout { AudioChannelSet::stereo(), { SpeakerPosition { 1.0f, -0.25f * float_Pi }, SpeakerPosition { 1.0f, 0.25f * float_Pi } }},
  131. Spatializer::SpeakerLayout { AudioChannelSet::quadraphonic(), { SpeakerPosition { 1.0f, -0.25f * float_Pi }, SpeakerPosition { 1.0f, 0.25f * float_Pi }, SpeakerPosition {1.0f, -0.75f * float_Pi}, SpeakerPosition {1.0f, 0.75f * float_Pi}}},
  132. Spatializer::SpeakerLayout { AudioChannelSet::create5point0(), {SpeakerPosition { 1.0f, 0.0f}, SpeakerPosition { 1.0f, -0.25f * float_Pi }, SpeakerPosition { 1.0f, 0.25f * float_Pi }, SpeakerPosition {1.0f, -0.75f * float_Pi}, SpeakerPosition {1.0f, 0.75f * float_Pi}}}
  133. };
  134. //==============================================================================
  135. // This creates new instances of the plugin..
  136. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  137. {
  138. return new Spatializer();
  139. }