6
6
7
7
namespace vae { namespace core {
8
8
/* *
9
- * @brief Struct providing SPCAP configurations hard coded
9
+ * @brief Struct containing SPCAP and preconfigured speaker setups
10
10
*/
11
11
struct SPCAP {
12
+ /* *
13
+ * @brief Class handling SPCAP for arbitrary speaker setups
14
+ * @tparam N Amount of speakers. Does not include LFE.
15
+ */
12
16
template <int N>
13
17
class SPCAPConfig {
14
18
struct Speaker {
15
- Vec3 dir;
16
- Sample effective;
19
+ Vec3 dir; // Position of the speaker on a unit sphere
20
+ Sample effective; //
17
21
};
18
22
Speaker mSpeakers [N];
19
23
public:
20
- SPCAPConfig (const std::initializer_list<Vec3>& directions) {
24
+ /* *
25
+ * @brief Construct a new SPCAPConfig object
26
+ * Implemented according to
27
+ * https://www.researchgate.net/publication/235080603_A_Novel_Multichannel_Panning_Method_for_Standard_and_Arbitrary_Loudspeaker_Configurations
28
+ * @param positions Speaker positions, will be normalized.
29
+ */
30
+ SPCAPConfig (const std::initializer_list<Vec3>& positions) {
21
31
int i = 0 ;
22
- for (const auto & speaker : directions ) {
32
+ for (const auto & speaker : positions ) {
23
33
mSpeakers [i] = { glm::normalize (speaker), Sample (0 ) };
24
34
i++;
25
35
}
26
36
for (i = 0 ; i < N; i++) {
27
37
for (int j = 0 ; j < N; j++) {
38
+ // Calculate the effective number of speakers according to (2)
39
+ // Can be done in adavnce and ideally at compiletime
40
+ // since the positions of the speakers will not change
28
41
mSpeakers [i].effective +=
29
42
Sample (0.5 ) * (Sample (1.0 ) + glm::dot (mSpeakers [i].dir , mSpeakers [j].dir ));
30
43
}
@@ -41,32 +54,38 @@ namespace vae { namespace core {
41
54
* @param attenuation Distance attenuation multiplied on the result
42
55
* @param spread Value from 0-1 controlling "wideness" of the sound
43
56
*/
44
- void pan (const Vec3& direction, Sample* result, Sample attenuation, Sample spread) const {
57
+ void pan (const Vec3& direction, Sample result[N] , Sample attenuation, Sample spread) const {
45
58
VAE_PROFILER_SCOPE
46
59
// TODO make spread change based on distance and use something like radius instead
47
- Sample sumSquaredGains = 0.0 ;
48
- const Sample tightness = (Sample (1 ) - spread) * Sample (10 ) + Sample (0.05 );
60
+ Sample sumGains = 0.0 ;
61
+ // const Sample tightness = (Sample(1) - spread) * Sample(10) + Sample(0.05);
62
+ const Sample tightness = 1.0 ;
49
63
std::fill_n (result, N, Sample (0 ));
50
64
for (int i = 0 ; i < N; i++) {
51
- Sample gain = Sample (0.5 ) * powf (Sample (1.0 ) + glm::dot (mSpeakers [i].dir , direction), tightness);
52
- gain /= mSpeakers [i].effective ;
53
- gain = gain * gain;
65
+ Sample gain = glm::dot (mSpeakers [i].dir , direction) + Sample (1.0 ); // (1)
66
+ gain = powf (gain, tightness); // (9)
67
+ gain *= Sample (0.5 ); // (1)
68
+ gain /= mSpeakers [i].effective ; // (3)
69
+ gain = gain * gain; // (4)
54
70
result[i] = gain;
55
- sumSquaredGains += gain;
71
+ sumGains += gain; // (4)
56
72
}
57
73
58
74
for (int i = 0 ; i < N; i++) {
59
- result[i] = sqrtf (result[i] / sumSquaredGains ) * attenuation;
75
+ result[i] = sqrtf (result[i] / sumGains ) * attenuation; // (8)
60
76
}
61
77
}
62
78
};
63
- // Initialized below because c++
79
+ // Initialized below because c++ 14
64
80
static const SPCAPConfig<2 > HeadphoneSPCAP;
65
81
static const SPCAPConfig<2 > StereroSPCAP;
66
82
static const SPCAPConfig<4 > QuadSPCAP;
67
83
static const SPCAPConfig<5 > SuroundSPCAP;
68
84
}; // SPCAP
69
85
86
+ /* *
87
+ * TODO there's probably a smart way to make this all constexpr
88
+ */
70
89
const SPCAP::SPCAPConfig<2 > SPCAP::HeadphoneSPCAP = {
71
90
{ -1 , 0 , 0 }, { +1 , 0 , 0 } // LR Side
72
91
};
0 commit comments