1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 namespace dsp
29 {
30 
31 enum class PannerRule
32 {
33     linear,          // regular 6 dB or linear panning rule, allows the panned sound to be
34                      // perceived as having a constant level when summed to mono
35     balanced,        // both left and right are 1 when pan value is 0, with left decreasing
36                      // to 0 above this value and right decreasing to 0 below it
37     sin3dB,          // alternate version of the regular 3 dB panning rule with a sine curve
38     sin4p5dB,        // alternate version of the regular 4.5 dB panning rule with a sine curve
39     sin6dB,          // alternate version of the regular 6 dB panning rule with a sine curve
40     squareRoot3dB,   // regular 3 dB or constant power panning rule, allows the panned sound
41                      // to be perceived as having a constant level regardless of the pan position
42     squareRoot4p5dB  // regular 4.5 dB panning rule, a compromise option between 3 dB and 6 dB panning rules
43 };
44 
45 /**
46     A processor to perform panning operations on stereo buffers.
47 
48     @tags{DSP}
49 */
50 template <typename SampleType>
51 class Panner
52 {
53 public:
54     //==============================================================================
55     using Rule = PannerRule;
56 
57     //==============================================================================
58     /** Constructor. */
59     Panner();
60 
61     //==============================================================================
62     /** Sets the panning rule. */
63     void setRule (Rule newRule);
64 
65     /** Sets the current panning value, between -1 (full left) and 1 (full right). */
66     void setPan (SampleType newPan);
67 
68     //==============================================================================
69     /** Initialises the processor. */
70     void prepare (const ProcessSpec& spec);
71 
72     /** Resets the internal state variables of the processor. */
73     void reset();
74 
75     //==============================================================================
76     /** Processes the input and output samples supplied in the processing context. */
77     template <typename ProcessContext>
process(const ProcessContext & context)78     void process (const ProcessContext& context) noexcept
79     {
80         const auto& inputBlock = context.getInputBlock();
81         auto& outputBlock      = context.getOutputBlock();
82 
83         const auto numInputChannels  = inputBlock.getNumChannels();
84         const auto numOutputChannels = outputBlock.getNumChannels();
85         const auto numSamples        = outputBlock.getNumSamples();
86 
87         jassert (inputBlock.getNumSamples() == numSamples);
88         ignoreUnused (numSamples);
89 
90         if (numOutputChannels != 2 || numInputChannels == 0 || numInputChannels > 2)
91             return;
92 
93         if (numInputChannels == 2)
94         {
95             outputBlock.copyFrom (inputBlock);
96         }
97         else
98         {
99             outputBlock.getSingleChannelBlock (0).copyFrom (inputBlock);
100             outputBlock.getSingleChannelBlock (1).copyFrom (inputBlock);
101         }
102 
103         if (context.isBypassed)
104             return;
105 
106         outputBlock.getSingleChannelBlock (0).multiplyBy (leftVolume);
107         outputBlock.getSingleChannelBlock (1).multiplyBy (rightVolume);
108     }
109 
110 private:
111     //==============================================================================
112     void update();
113 
114     //==============================================================================
115     Rule currentRule = Rule::balanced;
116     SampleType pan = 0.0;
117     SmoothedValue<SampleType> leftVolume, rightVolume;
118     double sampleRate = 44100.0;
119 };
120 
121 } // namespace dsp
122 } // namespace juce
123