├── Animation.cpp
├── Animation.h
├── BoundedValue.h
├── Box.cpp
├── Box.h
├── ConcurrentResource.h
├── Data.h
├── Doppler.cpp
├── Doppler.h
├── DrawInterpolator.h
├── DrewLib.h
├── Functions.h
├── GLUT.h
├── History.h
├── ImageEffect.cpp
├── ImageEffect.h
├── Interpolator.h
├── LICENSE
├── Multi.h
├── NonlinearInterpolator.h
├── OpenGL.h
├── OpenGLWindow.h
├── PluginEditor.cpp
├── PluginEditor.h
├── PluginProcessor.cpp
├── PluginProcessor.h
├── Points.h
├── PolyPtr.h
├── Polynomial.h
├── PolynomialSpline.h
├── PrintToFile.h
├── README.md
├── Resampler.cpp
├── Resampler.h
├── SelectionBox.cpp
├── SelectionBox.h
├── SoundSource.cpp
├── SoundSource.h
├── Spline.h
├── StackArray.h
├── StringFunctions.cpp
├── StringFunctions.h
├── TextUtils.cpp
├── TextUtils.h
├── View2D.cpp
└── View2D.h
/Animation.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Animation.cpp
3 | //
4 | // Created by Andrew Barker on 4/24/16.
5 | //
6 | //
7 |
8 | #include "Animation.h"
9 |
10 | Animation::Animation() noexcept
11 | : durationInSeconds(1), currentTimeInSeconds(1)
12 | {
13 | }
14 |
15 | Animation::Animation(const float durationInSeconds,
16 | const bool startImmediately) noexcept
17 | : durationInSeconds(durationInSeconds),
18 | currentTimeInSeconds(startImmediately ? 0 : durationInSeconds)
19 | {
20 | }
21 |
22 | void Animation::advance(const float frameRate) noexcept
23 | {
24 | currentTimeInSeconds += 1.0f / frameRate;
25 | }
26 |
27 | void Animation::restart() noexcept
28 | {
29 | currentTimeInSeconds = 0;
30 | }
31 |
32 | void Animation::restart(const float newDurationInSeconds) noexcept
33 | {
34 | currentTimeInSeconds = 0;
35 | durationInSeconds = newDurationInSeconds;
36 | }
37 |
38 | void Animation::finish() noexcept
39 | {
40 | currentTimeInSeconds = durationInSeconds;
41 | }
42 |
43 | float Animation::getProgress() const noexcept
44 | {
45 | if (currentTimeInSeconds < durationInSeconds)
46 | return currentTimeInSeconds / durationInSeconds;
47 | else
48 | return 1.0f;
49 | }
50 |
51 | bool Animation::isPlaying() const noexcept
52 | {
53 | return currentTimeInSeconds < durationInSeconds;
54 | }
55 |
56 | float Animation::getDuration() const noexcept
57 | {
58 | return durationInSeconds;
59 | }
60 |
--------------------------------------------------------------------------------
/Animation.h:
--------------------------------------------------------------------------------
1 | //
2 | // Animation.h
3 | //
4 | // Created by Andrew Barker on 4/24/16.
5 | //
6 | //
7 |
8 | #ifndef Animation_h
9 | #define Animation_h
10 |
11 | class Animation
12 | {
13 | public:
14 | Animation() noexcept;
15 | Animation(float durationInSeconds, bool startImmediately = false) noexcept;
16 | void advance(float frameRate) noexcept;
17 | void restart() noexcept;
18 | void restart(float durationInSeconds) noexcept;
19 | void finish() noexcept;
20 | float getProgress() const noexcept;
21 | bool isPlaying() const noexcept;
22 | float getDuration() const noexcept;
23 | private:
24 | float durationInSeconds, currentTimeInSeconds;
25 | };
26 |
27 | #endif /* Animation_h */
28 |
--------------------------------------------------------------------------------
/BoundedValue.h:
--------------------------------------------------------------------------------
1 | /*
2 | BoundedValue.h
3 |
4 | Abstracts the concept of an ordered value that must be in the range of [min, max]
5 |
6 | Copyright (C) 2016 Andrew Barker
7 |
8 | This program is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with this program. If not, see .
20 |
21 | The author can be contacted via email at andrew.barker.12345@gmail.com.
22 | */
23 |
24 | #ifndef BoundedValue_h
25 | #define BoundedValue_h
26 |
27 | #include
28 |
29 | template
30 | class BoundedValue
31 | {
32 | public:
33 | /** sets up a value of zero with a bounds of [0, 0] */
34 | BoundedValue () noexcept;
35 | /** sets up with a specified value and bounds of [min, max] */
36 | BoundedValue (T value, T min, T max) noexcept;
37 |
38 | /** returns the value */
39 | T getValue () const noexcept;
40 | /** sets the value and makes sure it is inside the specified bounds */
41 | void setValue (T value) noexcept;
42 |
43 | /** returns the minimum allowed value for the bounded value */
44 | T getMin () const noexcept;
45 | /** sets the minimum allowed value for the bounded value */
46 | void setMin (T min) noexcept;
47 |
48 | /** returns the maximum allowed value for the bounded value */
49 | T getMax () const noexcept;
50 | /** sets the maximum allowed value for the bounded value */
51 | void setMax (T max) noexcept;
52 |
53 | /** returns the difference between the min and max for the value */
54 | T range () const noexcept;
55 |
56 | /** returns the value normalized to it's range, between [0, 1] */
57 | T normalized () const noexcept;
58 |
59 | /** allows implicit conversion to the type of the bounded value */
60 | operator T () const noexcept;
61 |
62 | /** sets the value and makes sure it is inside the specified bounds */
63 | BoundedValue& operator = (T value) noexcept;
64 | /** adds the the specified amount to the value with bounds checking */
65 | BoundedValue& operator += (T addValue) noexcept;
66 | /** subtracts the specified amount from the value with bounds checking */
67 | BoundedValue& operator -= (T subValue) noexcept;
68 | /** multiplies the value by the specified amount with bounds checking */
69 | BoundedValue& operator *= (T multValue) noexcept;
70 | /** divides the value by the specified amount with bounds checking */
71 | BoundedValue& operator /= (T divValue) noexcept;
72 | /** sets the value to the remainder of value / divValue with bounds checking */
73 | BoundedValue& operator %= (T divValue) noexcept;
74 |
75 | private:
76 | T _value, _min, _max;
77 | };
78 |
79 | // implementation
80 |
81 | template
82 | BoundedValue::BoundedValue () noexcept
83 | : _value (0),
84 | _min (0),
85 | _max (0)
86 | {}
87 |
88 | template
89 | BoundedValue::BoundedValue (const T value,
90 | const T min,
91 | const T max) noexcept
92 | : _value (value),
93 | _min (min),
94 | _max (max)
95 | {
96 | if (_min > _max) {
97 | _min = max;
98 | _max = min;
99 | }
100 | }
101 |
102 | template
103 | T BoundedValue::getValue () const noexcept { return _value; }
104 |
105 | template
106 | void BoundedValue::setValue (const T value) noexcept
107 | {
108 | if (getMin() <= value && value <= getMax())
109 | _value = value;
110 | else if (value < getMin())
111 | _value = getMin();
112 | else
113 | _value = getMax();
114 | }
115 |
116 | template
117 | T BoundedValue::getMin () const noexcept { return _min; }
118 |
119 | template
120 | void BoundedValue::setMin (const T min) noexcept
121 | {
122 | if (min <= getMax()) {
123 | _min = min;
124 | setValue(getValue());
125 | } else {
126 | _min = min;
127 | _max = min;
128 | _value = min;
129 | }
130 | }
131 |
132 | template
133 | T BoundedValue::getMax () const noexcept { return _max; }
134 |
135 | template
136 | void BoundedValue::setMax (const T max) noexcept
137 | {
138 | if (max >= getMin()) {
139 | _max = max;
140 | setValue(getValue());
141 | } else {
142 | _min = max;
143 | _max = max;
144 | _value = max;
145 | }
146 | }
147 |
148 | template
149 | BoundedValue::operator T () const noexcept { return _value; }
150 |
151 | template
152 | BoundedValue& BoundedValue::operator = (const T value) noexcept
153 | {
154 | setValue(value);
155 | return *this;
156 | }
157 |
158 | template
159 | BoundedValue& BoundedValue::operator += (const T value) noexcept
160 | {
161 | setValue(getValue() + value);
162 | return *this;
163 | }
164 |
165 | template
166 | BoundedValue& BoundedValue::operator -= (const T value) noexcept
167 | {
168 | setValue(getValue() - value);
169 | return *this;
170 | }
171 |
172 | template
173 | BoundedValue& BoundedValue::operator *= (const T value) noexcept
174 | {
175 | setValue(getValue() * value);
176 | return *this;
177 | }
178 |
179 | template
180 | BoundedValue& BoundedValue::operator /= (T value) noexcept
181 | {
182 | setValue(getValue() / value);
183 | return *this;
184 | }
185 |
186 | template
187 | BoundedValue& BoundedValue::operator %= (T value) noexcept
188 | {
189 | setValue(std::fmod(getValue(), value));
190 | return *this;
191 | }
192 |
193 | template
194 | T BoundedValue::range () const noexcept
195 | { return getValue().getMax() - getValue().getMin(); }
196 |
197 | template
198 | T BoundedValue::normalized () const noexcept
199 | { return (getValue() - getMin()) * range(); }
200 |
201 | #endif /* BoundedValue_h */
--------------------------------------------------------------------------------
/Box.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Box.cpp
3 | //
4 | // Created by Andrew Barker on 9/19/16.
5 | //
6 | //
7 |
8 | #include "Box.h"
9 | #include "OpenGL.h"
10 | #include
11 |
12 |
13 | Box::Box() noexcept
14 | : top (0),
15 | bottom (0),
16 | left (0),
17 | right (0)
18 | {
19 | }
20 |
21 | Box::Box(const float top,
22 | const float bottom,
23 | const float left,
24 | const float right) noexcept
25 | : top (std::max(top, bottom)),
26 | bottom (std::min(top, bottom)),
27 | left (std::min(left, right)),
28 | right (std::max(left, right))
29 | {
30 | }
31 |
32 | Box::Box(const Point& pt,
33 | const float radius) noexcept
34 | : top (pt.y + radius),
35 | bottom (pt.y - radius),
36 | left (pt.x - radius),
37 | right (pt.x + radius)
38 | {
39 | }
40 |
41 | bool Box::operator== (const Box& box) const noexcept
42 | {
43 | return top == box.getTop() &&
44 | bottom == box.getBottom() &&
45 | left == box.getLeft() &&
46 | right == box.getRight();
47 | }
48 |
49 | bool Box::operator!= (const Box& box) const noexcept
50 | {
51 | return top != box.getTop() ||
52 | bottom != box.getBottom() ||
53 | left != box.getLeft() ||
54 | right != box.getRight();
55 | }
56 |
57 | float Box::getTop() const noexcept { return top; }
58 | void Box::setTop(const float newTop) noexcept
59 | {
60 | if (newTop > bottom)
61 | top = newTop;
62 | else {
63 | top = bottom;
64 | bottom = newTop;
65 | }
66 | }
67 |
68 | float Box::getBottom() const noexcept { return bottom; }
69 | void Box::setBottom(const float newBottom) noexcept
70 | {
71 | if (newBottom < top)
72 | bottom = newBottom;
73 | else {
74 | bottom = top;
75 | top = newBottom;
76 | }
77 | }
78 |
79 | float Box::getLeft() const noexcept { return left; }
80 | void Box::setLeft(const float newLeft) noexcept
81 | {
82 | if (newLeft < right)
83 | left = newLeft;
84 | else {
85 | left = right;
86 | right = newLeft;
87 | }
88 | }
89 |
90 | float Box::getRight() const noexcept { return right; }
91 | void Box::setRight(const float newRight) noexcept
92 | {
93 | if (newRight > left)
94 | right = newRight;
95 | else {
96 | right = left;
97 | left = newRight;
98 | }
99 | }
100 |
101 | float Box::width () const noexcept { return getRight() - getLeft(); }
102 | float Box::height () const noexcept { return getTop() - getBottom(); }
103 | float Box::area () const noexcept { return width() * height(); }
104 | float Box::centerX() const noexcept { return getLeft() + 0.5f * width(); }
105 | float Box::centerY() const noexcept { return getBottom() + 0.5f * height(); }
106 |
107 | void Box::drawVerticies () const
108 | {
109 | for (const auto& pt : boundaryPoints())
110 | glVertex2f(pt.x, pt.y);
111 | // glVertex2f(getLeft(), getTop());
112 | // glVertex2f(getRight(), getTop());
113 | // glVertex2f(getRight(), getBottom());
114 | // glVertex2f(getLeft(), getBottom());
115 | }
116 | void Box::drawOutline () const
117 | {
118 | glBegin(GL_LINE_LOOP);
119 | drawVerticies();
120 | glEnd();
121 | }
122 | void Box::drawFill () const
123 | {
124 | glBegin(GL_QUADS);
125 | drawVerticies();
126 | glEnd();
127 | }
128 |
129 | std::vector> Box::boundaryPoints () const noexcept
130 | {
131 | return {{getLeft(), getTop()},
132 | {getRight(), getTop()},
133 | {getRight(), getBottom()},
134 | {getLeft(), getBottom()}};
135 | }
136 |
137 | bool Box::contains (const Point& pt) const noexcept
138 | {
139 | return getLeft() < pt.x && pt.x < getRight() &&
140 | getBottom() < pt.y && pt.y < getTop();
141 | }
142 |
143 | bool Box::overlaps (const Box& other) const noexcept
144 | {
145 | return ! (getBottom() > other.getTop() || getTop() < other.getBottom() ||
146 | getLeft() > other.getRight() || getRight() < other.getLeft());
147 | }
148 |
149 | void Box::crop (const Box& crop) noexcept
150 | {
151 | if (getTop() > crop.getTop())
152 | setTop(crop.getTop());
153 | if (getBottom() < crop.getBottom())
154 | setBottom(crop.getBottom());
155 | if (getLeft() < crop.getLeft())
156 | setLeft(crop.getLeft());
157 | if (getRight() > crop.getRight())
158 | setRight(crop.getRight());
159 | }
160 |
161 | Box Box::combinedWith (const Box& other) const noexcept
162 | {
163 | return { std::max(getTop(), other.getTop()), std::min(getBottom(), other.getBottom()),
164 | std::min(getLeft(), other.getLeft()), std::max(getRight(), other.getRight()) };
165 | }
166 |
167 | Box Box::scaled (const float hScale,
168 | const float vScale) const noexcept
169 | {
170 | const float dWidth = width() * 0.5f * (1 - hScale);
171 | const float dHeight = height() * 0.5f * (1 - vScale);
172 | const float top = getTop() - dHeight;
173 | const float bottom = getBottom() + dHeight;
174 | const float left = getLeft() + dWidth;
175 | const float right = getRight() - dWidth;
176 | return {top, bottom, left, right};
177 | }
178 |
179 | void Box::move (const float dx,
180 | const float dy) noexcept
181 | {
182 | *this = {getTop() + dy, getBottom() + dy, getLeft() + dx, getRight() + dx};
183 | }
184 |
185 |
186 | //float width (const Box& b) noexcept
187 | //{
188 | // return b.getRight() - b.getLeft();
189 | //}
190 | //
191 | //float height (const Box& b) noexcept
192 | //{
193 | // return b.getTop() - b.getBottom();
194 | //}
195 | //
196 | //float area (const Box& b) noexcept
197 | //{
198 | // return width(b) * height(b);
199 | //}
200 | //
201 | //void drawVerticies (const Box& b)
202 | //{
203 | // glVertex2f(b.getLeft(), b.getTop());
204 | // glVertex2f(b.getRight(), b.getTop());
205 | // glVertex2f(b.getRight(), b.getBottom());
206 | // glVertex2f(b.getLeft(), b.getBottom());
207 | //}
208 | //
209 | //void drawOutline (const Box& b)
210 | //{
211 | // glBegin(GL_LINE_LOOP);
212 | // drawVerticies(b);
213 | // glEnd();
214 | //}
215 | //
216 | //void drawFill (const Box& b)
217 | //{
218 | // glBegin(GL_QUADS);
219 | // drawVerticies(b);
220 | // glEnd();
221 | //}
222 | //
223 | //std::vector> boundaryPoints (const Box& b) noexcept
224 | //{
225 | // return {{b.getLeft(), b.getTop()},
226 | // {b.getRight(), b.getTop()},
227 | // {b.getRight(), b.getBottom()},
228 | // {b.getLeft(), b.getBottom()}};
229 | //}
230 | //
231 | //bool contain (const Box& b,
232 | // const Point& pt) noexcept
233 | //{
234 | // return b.getLeft() < pt.x && pt.x < b.getRight() &&
235 | // b.getBottom() < pt.y && pt.y < b.getTop();
236 | //}
237 | //
238 | //bool overlap (const Box& b1,
239 | // const Box& b2) noexcept
240 | //{
241 | // return ! (b1.getBottom() > b2.getTop() || b1.getTop() < b2.getBottom() ||
242 | // b1.getLeft() > b2.getRight() || b1.getRight() < b2.getLeft());
243 | //}
244 | //
245 | //void crop (Box& b,
246 | // const Box& crop) noexcept
247 | //{
248 | // if (b.getTop() > crop.getTop())
249 | // b.setTop(crop.getTop());
250 | // if (b.getBottom() < crop.getBottom())
251 | // b.setBottom(crop.getBottom());
252 | // if (b.getLeft() < crop.getLeft())
253 | // b.setLeft(crop.getLeft());
254 | // if (b.getRight() > crop.getRight())
255 | // b.setRight(crop.getRight());
256 | //}
257 | //
258 | //Box combined (const Box& b1,
259 | // const Box& b2) noexcept
260 | //{
261 | // return { std::max(b1.getTop(), b2.getTop()), std::min(b1.getBottom(), b2.getBottom()),
262 | // std::min(b1.getLeft(), b2.getLeft()), std::max(b1.getRight(), b2.getRight()) };
263 | //}
264 | //
265 | //Box getScaled (const Box& b,
266 | // const float hScale,
267 | // const float vScale) noexcept
268 | //{
269 | // const float dWidth = width(b) * 0.5f * (1 - hScale);
270 | // const float dHeight = height(b) * 0.5f * (1 - vScale);
271 | // const float top = b.getTop() - dHeight;
272 | // const float bottom = b.getBottom() + dHeight;
273 | // const float left = b.getLeft() + dWidth;
274 | // const float right = b.getRight() - dWidth;
275 | // return {top, bottom, left, right};
276 | //}
277 | //
278 | //void move(Box& b,
279 | // const float dx,
280 | // const float dy) noexcept
281 | //{
282 | // b.top += dy;
283 | // b.bottom += dy;
284 | // b.left += dx;
285 | // b.right += dx;
286 | //}
287 | //
288 | ////void placeWithin(Box& b,
289 | //// const Box& placeWithin,
290 | //// const std::vector& noOverlap) noexcept
291 | ////{
292 | //// int dirCount = 0;
293 | //// for (const auto & x : noOverlap) {
294 | //// if (overlap(b, x)) {
295 | //// move(b,
296 | //// }
297 | //// }
298 | ////}
299 |
--------------------------------------------------------------------------------
/Box.h:
--------------------------------------------------------------------------------
1 | //
2 | // Box.h
3 | //
4 | // Created by Andrew Barker on 9/19/16.
5 | //
6 | //
7 |
8 | #ifndef Box_h
9 | #define Box_h
10 |
11 | #include "../JuceLibraryCode/JuceHeader.h"
12 | #include
13 |
14 | //template
15 | //struct Point { T x, y; };
16 |
17 | /** A 2D rectangle enforcing that top > bottom and left < right */
18 | class Box
19 | {
20 | public:
21 | /** creates an empty box */
22 | Box () noexcept;
23 | /** creates a box from the specified coordinates of its edges */
24 | Box (float top, float bottom, float left, float right) noexcept;
25 | /** creates a square box centered around a point and width and height of 2 * radius */
26 | Box (const Point& pt, float radius) noexcept;
27 |
28 | /** returns true if both boxes have the same edge coordinates */
29 | bool operator== (const Box& box) const noexcept;
30 | /** returns true if both boxes don't have the same edge coordinates */
31 | bool operator!= (const Box& box) const noexcept;
32 |
33 | /** returns the y coordinate of the box's top edge */
34 | float getTop () const noexcept;
35 | /** sets the y coordinate of the box's bottom edge */
36 | void setTop (float top) noexcept;
37 | /** returns the y coordinate of the box's bottom edge */
38 | float getBottom () const noexcept;
39 | /** sets the Y coordinate of the box's bottom edge */
40 | void setBottom (float bottom) noexcept;
41 | /** returns the x coordinate of the box's left edge */
42 | float getLeft () const noexcept;
43 | /** sets the x coordinate of the box's left edge */
44 | void setLeft (float left) noexcept;
45 | /** returns the x coordinate of the box's right edge */
46 | float getRight () const noexcept;
47 | /** sets the x coordinate of the box's right edge */
48 | void setRight (float right) noexcept;
49 |
50 | /** returns the width of the box */
51 | float width () const noexcept;
52 | /** returns the height of the box */
53 | float height () const noexcept;
54 | /** returns the area of the box */
55 | float area () const noexcept;
56 | /** returns the x coordinate horizontal center of the box */
57 | float centerX() const noexcept;
58 | /** returns the y coordinate vertical center of the box */
59 | float centerY() const noexcept;
60 |
61 | /** calls glVertex2f() for all the corner points of the box */
62 | void drawVerticies () const;
63 | /** draws the outline of the box using OpenGL */
64 | void drawOutline () const;
65 | /** draws a fill of the box using OpenGL */
66 | void drawFill () const;
67 |
68 | /** returns all the corner points of the box */
69 | std::vector> boundaryPoints () const noexcept;
70 |
71 | /** returns true if the specified point is inside the box */
72 | bool contains (const Point& pt) const noexcept;
73 | /** returns true if this box overlaps at all with another */
74 | bool overlaps (const Box& other) const noexcept;
75 | /** crops this box by another */
76 | void crop (const Box& crop) noexcept;
77 | /** returns a box that is the minimum needed to contain this box and another */
78 | Box combinedWith (const Box& other) const noexcept;
79 | /** returns a box that is this box horizontally and vertically scaled as specified */
80 | Box scaled (float hScale, float vScale) const noexcept;
81 | /** moves this box by a given x/y distance */
82 | void move (float dx, float dy) noexcept;
83 | // friend void move(Box& b,
84 | // float dx,
85 | // float dy) noexcept;
86 |
87 | private:
88 | float top, bottom, left, right;
89 | };
90 |
91 | //float width (const Box& b) noexcept;
92 | //float height (const Box& b) noexcept;
93 | //float area (const Box& b) noexcept;
94 | //void drawVerticies (const Box& b);
95 | //void drawOutline (const Box& b);
96 | //void drawFill (const Box& b);
97 | //std::vector> boundaryPoints(const Box& b) noexcept;
98 | //bool contain (const Box& b,
99 | // const Point& pt) noexcept;
100 | //bool overlap (const Box& b1,
101 | // const Box& b2) noexcept;
102 | //void crop (Box& b,
103 | // const Box& crop) noexcept;
104 | //Box combined (const Box& b1,
105 | // const Box& b2) noexcept;
106 | //Box getScaled (const Box& b,
107 | // float hScale,
108 | // float vScale) noexcept;
109 | //void move(Box& b,
110 | // float dx,
111 | // float dy) noexcept;
112 | ////void placeWithin(Box& b,
113 | //// const Box& placeWithin,
114 | //// const std::vector& noOverlap) noexcept;
115 |
116 | #endif /* Box_h */
--------------------------------------------------------------------------------
/Data.h:
--------------------------------------------------------------------------------
1 | //
2 | // Data.h
3 | //
4 | // Created by Andrew Barker on 6/15/14.
5 | //
6 | //
7 | /*
8 | 3DAudio: simulates surround sound audio for headphones
9 | Copyright (C) 2016 Andrew Barker
10 |
11 | This program is free software: you can redistribute it and/or modify
12 | it under the terms of the GNU General Public License as published by
13 | the Free Software Foundation, either version 3 of the License, or
14 | (at your option) any later version.
15 |
16 | This program is distributed in the hope that it will be useful,
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | GNU General Public License for more details.
20 |
21 | You should have received a copy of the GNU General Public License
22 | along with this program. If not, see .
23 |
24 | The author can be contacted via email at andrew.barker.12345@gmail.com.
25 | */
26 |
27 | #ifndef Data_h
28 | #define Data_h
29 |
30 | #define numAzimuthSteps 180
31 | #define numElevationSteps 90
32 | #define numTimeSteps 128
33 | #define numDistanceSteps 20
34 | #define distanceBegin 0.1
35 | #define distanceEnd 3.25
36 | #define sampleRate_HRTF 44100.0
37 | #define sphereRad 0.09
38 | #define threshold 0.00000001
39 | #define earElevation 90
40 | #define earAzimuth 85
41 |
42 |
43 | //#include
44 | //#include
45 | //
46 | //// note: these dimensions determine the size of the dvf/hrir data. if these are
47 | //// too large then the application may run out of RAM.
48 | //// (especially for 32-bit @ about 2GB)
49 | //#define numAzimuthSteps 180
50 | //#define numElevationSteps 90
51 | //#define numTimeSteps 128
52 | //#define numDistanceSteps 20
53 | //#define distanceBegin 0.1
54 | //#define distanceEnd 3.25
55 | //#define sampleRate_HRTF 44100
56 | //#define sphereRad 0.09
57 | //#define threshold 0.00000001
58 | //#define earElevation 90
59 | //#define earAzimuth 85
60 | //
61 | //// a singleton class to hold the hrir and dvf data so that multiple plugin instances may run without having to consume unecessary RAM
62 | //class Data
63 | //{
64 | //public:
65 | // static Data& getSingleton() //Return our instance
66 | // {
67 | // return mInstance;
68 | // }
69 | //
70 | // void load()
71 | // {
72 | //
73 | // }
74 | //
75 | // void clear()
76 | // {
77 | //
78 | // }
79 | //
80 | // float***** HRIR;
81 | // //float HRIR[numDistanceSteps][numAzimuthSteps][numElevationSteps][2][numTimeSteps];
82 | //
83 | //private:
84 | // static Data mInstance;
85 | //
86 | // Data() // read in data on construction
87 | // {
88 | ////// // binary hrtf file name
89 | ////// File f;
90 | ////// f.getSpecialLocation(File::currentApplicationFile);
91 | ////// //juce::File f = juce::File::getSpecialLocation(juce::File::currentApplicationFile);
92 | ////// String path = f.getFullPathName();
93 | //////// probably not necessary...
94 | ////////#ifdef _WIN32
95 | //////// path += "\";
96 | ////////#else
97 | //////// path += "/";
98 | ////////#endif
99 | ////// path += "/Contents/3dAudioData.bin";
100 | //////// char fullPath[300];
101 | //////// strcpy(fullPath, path.toRawUTF8());
102 | //// char* fileName = (char*)"/Users/AndrewBarker/Documents/Programming/3dAudio/SphericalHRIR2.bin"/*"SphericalHRIR.bin"*/;
103 | //// // basic read (should be cross platform)
104 | //// // open the stream
105 | //// std::ifstream is(/*path.getCharPointer()*/fileName);
106 | //// if (is.good()) {
107 | //// // determine the file length
108 | //// is.seekg(0, is.end);
109 | //// size_t size = is.tellg();
110 | //// is.seekg(0, is.beg);
111 | //// // create a temp array to store the data
112 | //// char* dataBuffer = new char[size];
113 | //// // load the data
114 | //// is.read(dataBuffer, size);
115 | //// // close the file
116 | //// is.close();
117 | //// // pack data into HRIR array
118 | //// int index = 0;
119 | //// for (int d = 0; d < numDistanceSteps; d++) {
120 | //// for (int a = 0; a < numAzimuthSteps; a++) {
121 | //// for (int e = 0; e < numElevationSteps; e++) {
122 | //// for (int ch = 0; ch < 2; ch++) {
123 | //// for (int t = 0; t < numTimeSteps; t++) {
124 | //// HRIR[d][a][e][ch][t] = *((float*) &dataBuffer[index*sizeof(float)]);
125 | //// //HRIR[d][a][e][ch][t] = 0;
126 | //// index++;
127 | //// }
128 | //// }
129 | //// }
130 | //// }
131 | //// }
132 | //// // cleanup the temp array
133 | //// delete[] dataBuffer;
134 | //// } else {
135 | //// // failed to open hrtf binary file, load up zeros
136 | //// int index = 0;
137 | //// for (int d = 0; d < numDistanceSteps; d++) {
138 | //// for (int a = 0; a < numAzimuthSteps; a++) {
139 | //// for (int e = 0; e < numElevationSteps; e++) {
140 | //// for (int ch = 0; ch < 2; ch++) {
141 | //// for (int t = 0; t < numTimeSteps; t++) {
142 | //// HRIR[d][a][e][ch][t] = 0;
143 | //// index++;
144 | //// }
145 | //// }
146 | //// }
147 | //// }
148 | //// }
149 | //// }
150 | // }
151 | //
152 | // ~Data() {}
153 | //
154 | // // Dont forget to declare these two. You want to make sure they
155 | // // are unaccessable otherwise you may accidently get copies of
156 | // // your singleton appearing.
157 | // Data(Data const&); // Don't Implement
158 | // void operator=(Data const&); // Don't implement
159 | //};
160 | //
161 | //Data Data::mInstance;
162 |
163 | #endif
164 |
--------------------------------------------------------------------------------
/Doppler.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Doppler.cpp
3 | // ThreeDAudio
4 | //
5 | // Created by Andrew Barker on 4/16/15.
6 | //
7 | //
8 | /*
9 | 3DAudio: simulates surround sound audio for headphones
10 | Copyright (C) 2016 Andrew Barker
11 |
12 | This program is free software: you can redistribute it and/or modify
13 | it under the terms of the GNU General Public License as published by
14 | the Free Software Foundation, either version 3 of the License, or
15 | (at your option) any later version.
16 |
17 | This program is distributed in the hope that it will be useful,
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | GNU General Public License for more details.
21 |
22 | You should have received a copy of the GNU General Public License
23 | along with this program. If not, see .
24 |
25 | The author can be contacted via email at andrew.barker.12345@gmail.com.
26 | */
27 |
28 | #include "Doppler.h"
29 |
30 | void Doppler::process(const float distance, const int bufferSize, const float* input, float* output) noexcept
31 | {
32 | const auto delay = distance / speedOfSound * sampleRate;
33 | const auto cBufSize = buffer.size();
34 | if (delayPrev == -1) {
35 | delayPrev = delay;
36 | prevSampleDelayedIdx = delay;
37 | }
38 | const auto slope = (delay - delayPrev) / bufferSize;
39 | const auto a0 = delayPrev;
40 | const auto a1 = slopePrev;
41 | float _a2, _a3;
42 | if ((slope > 0 && slopePrev < 0) || (slope < 0 && slopePrev > 0)) { // use 3rd degree polynomial interpolation for sample delay
43 | _a2 = 2 * (slope - slopePrev) / bufferSize;
44 | _a3 = (slopePrev - slope) / (bufferSize * bufferSize);
45 | }
46 | else { // use 2nd degree polynomial interpolation for sample delay
47 | _a2 = (slope - slopePrev) / (2 * bufferSize);
48 | _a3 = 0;
49 | }
50 | const auto a2 = _a2, a3 = _a3;
51 | const auto denom = a1*bufferSize + a2*bufferSize*bufferSize + a3*bufferSize*bufferSize*bufferSize;
52 | const auto delayScale = denom == 0 ? 0 : (delay - delayPrev) / denom; // check for div by 0
53 | for (int n = 0; n < bufferSize; ++n) {
54 | const auto delayedIdx = a0 + (a1*n + a2*n*n + a3*n*n*n) * delayScale;
55 | auto fidx = bufferInIdx + delayedIdx;
56 | while (fidx >= cBufSize)
57 | fidx -= cBufSize;
58 | while (fidx < 0)
59 | fidx += cBufSize;
60 | const int idxP1 = int(fidx) + 1 == cBufSize ? 0 : int(fidx) + 1;
61 | const int prevIdxP1 = int(prevSampleDelayedIdx) + 1 == cBufSize ?
62 | 0 : int(prevSampleDelayedIdx) + 1;
63 | bool forwards = true;
64 | if (idxP1 < prevIdxP1 && !(idxP1 + cBufSize - prevIdxP1 < prevIdxP1 - idxP1))
65 | forwards = false;
66 | if (forwards) {
67 | for (int i = prevIdxP1; i != idxP1; i = i + 1 == cBufSize ? 0 : i + 1) {
68 | const auto blend = (i - prevSampleDelayedIdx < 0 ?
69 | cBufSize - prevSampleDelayedIdx + i : i - prevSampleDelayedIdx)
70 | /
71 | (fidx - prevSampleDelayedIdx < 0 ?
72 | cBufSize - prevSampleDelayedIdx + fidx : fidx - prevSampleDelayedIdx);
73 | buffer[i] += prevSample + (input[n] - prevSample) * blend;
74 | }
75 | }
76 | else {
77 | for (int i = idxP1; i != prevIdxP1; i = i + 1 == cBufSize ? 0 : i + 1) {
78 | const auto blend = (i - fidx < 0 ?
79 | cBufSize - fidx + i : i - fidx)
80 | /
81 | (prevSampleDelayedIdx - fidx < 0 ?
82 | cBufSize + prevSampleDelayedIdx - fidx : prevSampleDelayedIdx - fidx);
83 | buffer[i] += prevSample + (input[n] - prevSample) * (1 - blend);
84 | }
85 | }
86 | prevSample = input[n];
87 | prevSampleDelayedIdx = fidx;
88 | bufferInIdx = bufferInIdx + 1 == cBufSize ? 0 : bufferInIdx + 1;
89 | }
90 | slopePrev = slope;
91 | delayPrev = delay;
92 | for (int n = 0; n < bufferSize; ++n) {
93 | output[n] = buffer[bufferOutIdx];
94 | buffer[bufferOutIdx] = 0;
95 | bufferOutIdx = bufferOutIdx + 1 == cBufSize ? 0 : bufferOutIdx + 1;
96 | }
97 | }
98 |
99 | void Doppler::allocate(const float maxDistance, const int maxBufferSize, const float speedOfSoundToPlanAllocationSize)
100 | {
101 | const auto maxDelayInSamples = maxDistance / speedOfSoundToPlanAllocationSize * sampleRate;
102 | buffer.resize(maxBufferSize + maxDelayInSamples);
103 | reset();
104 | }
105 |
106 | void Doppler::free() noexcept
107 | {
108 | buffer.clear();
109 | }
110 |
111 | void Doppler::reset() noexcept
112 | {
113 | for (auto& x : buffer)
114 | x = 0;
115 | delayPrev = -1;
116 | slopePrev = 0;
117 | bufferInIdx = bufferOutIdx = 0;
118 | prevSample = 0;
119 | }
120 |
121 | void Doppler::setSampleRate(const float _sampleRate) noexcept
122 | {
123 | sampleRate = _sampleRate;
124 | }
125 |
126 | void Doppler::setSpeedOfSound(const float _speedOfSound) noexcept
127 | {
128 | speedOfSound = _speedOfSound;
129 | }
130 |
131 | //#include
132 | //
133 | //void Doppler::process(const float dist, const int N, const float* x, float* y) noexcept
134 | //{
135 | // // time delay in samples, measured from the first sample of the next buffer to the beginning of the next buffer's delayed waveform
136 | //// float d; // the delay actually used for this buffer
137 | //// const float dDesired = dist*ONE_OVER_SoW*Fs; // the desired delay (the one passed in this buffer)
138 | //// // the following shit is just to smooth the distance changes when they are comming erraticly from the user input thread as opposed to the nice consistent changes that the interpolators generate on playback
139 | //// const float deviance = maxChange*((float)N); // how much the delay changes from the previous buffer for this buffer
140 | //// const float dif = std::abs(dDesired-dPrev); // how much distance change is there to still be made up
141 | //// const float totalDif = std::abs(dDesired-dDesiredPrev); // total distance change between each "step"
142 | //// if (dif > INIT_MAX_CHANGE*N)
143 | //// {
144 | //// if (dDesired < dPrev)
145 | //// d = dPrev - deviance;
146 | //// else
147 | //// d = dPrev + deviance;
148 | //// if (/*std::abs(dDesired-d)*/dif > totalDif*0.5) // less than half way there
149 | //// maxChange *= 1.05;//2.0;
150 | //// else // more than half way there
151 | //// maxChange /= 1.05;//0.5;
152 | //// }
153 | //// else
154 | //// {
155 | //// dDesiredPrev = dDesired;
156 | //// d = dDesired;
157 | //// maxChange = INIT_MAX_CHANGE;
158 | //// }
159 | // const float d = dist/speedOfSound*Fs;
160 | // //const float d = dist*ONE_OVER_SoW*Fs;
161 | // const int numInputs = inputs.size();
162 | // // load the new input
163 | // //if (inputs.size() == 0)
164 | // if (oldest == newest)
165 | // { // the first input cannot be stretched yet since we have only been passed one delay value so far
166 | // //inputs.emplace_back(DelayedInput(x, N, d, d, (float)N, N));
167 | // inputs[newest].load(x, N, d, d, (float)N, N);
168 | // //inputs[newest].load(x, N, d, d, (float)N, N, N, N);
169 | // }
170 | // else // all following inputs can be stretched with potentially different begin + end delays
171 | // { // for the 2nd buffer, N should equal NPrev, if not then the audio stretching will be slightly off for just that buffer
172 | // //int NPrev = inputs.back().input.size();
173 | // //float NstrPrev = inputs.back().dEnd - inputs.back().dBegin;
174 | // //inputs.emplace_back(DelayedInput(x, N, dPrev, d, NstrPrev, NPrev));
175 | // int prev = newest-1;
176 | // if (prev < 0)
177 | // prev = numInputs-1;
178 | // const int NPrev = inputs[prev].inputLength;
179 | // const float NstrPrev = inputs[prev].dEnd - inputs[prev].dBegin;
180 | // inputs[newest].load(x, N, dPrev, d, NstrPrev, NPrev);
181 | // }
182 | // newest = (newest+1) % numInputs;
183 | // // zero output array
184 | // for (int n = 0; n < N; ++n)
185 | // y[n] = 0;
186 | // // loop though inputs and compute the samples for this buffer
187 | // //for (int i = 0; i < inputs.size(); ++i)
188 | // int next, begin, end;
189 | // for (int i = oldest; i != newest;)
190 | // {
191 | // next = (i+1) % numInputs;
192 | // // if the input is needed for the current buffer
193 | // if (inputs[i].dBegin <= N-1)
194 | // {
195 | // begin = std::max(0, /*((int)inputs[i].dBegin+1)*/ (int)std::ceil(inputs[i].dBegin));
196 | // end = std::min(N, /*((int)inputs[i].dEnd+1)*/ (int)std::ceil(inputs[i].dEnd));
197 | //// if (i < inputs.size()-1)
198 | //// {
199 | // for (int n = begin; n < (const int)end; ++n)
200 | // y[n] += inputs[i].valueAt(n, inputs[next/*i+1*/].input[0]);
201 | //// }
202 | //// else
203 | //// {
204 | //// for (int n = begin; n < end; ++n)
205 | //// y[n] += inputs[i].valueAt(n, 0);
206 | //// }
207 | // }
208 | // inputs[i].dBegin -= N;
209 | // inputs[i].dEnd -= N;
210 | // inputs[i].shift += N;
211 | // if (inputs[i].dEnd < 0)
212 | // {
213 | // //inputs.erase(inputs.begin()+i);
214 | // //--i;
215 | // oldest = next;
216 | // }
217 | // i = next;
218 | // }
219 | // dPrev = d;
220 | //}
221 | //
222 | ////void Doppler::process(const float* dists, int N, const float* x, float* y) noexcept
223 | ////{
224 | ////
225 | ////}
226 | //
227 | //void Doppler::reset() noexcept
228 | //{
229 | // //inputs.clear();
230 | // oldest = 0;
231 | // newest = 0;
232 | // dPrev = 0;
233 | //}
234 | //
235 | //void Doppler::setSampleRate(const float sampleRate) noexcept
236 | //{
237 | // Fs = sampleRate;
238 | //}
239 | //
240 | ////void Doppler::setSpeedOfSound(const float newSpeedOfSound)
241 | ////{
242 | //// if (speedOfSound != newSpeedOfSound)
243 | //// {
244 | //// allocate();
245 | //// }
246 | //// speedOfSound = newSpeedOfSound;
247 | ////}
248 | //
249 | //float Doppler::getSpeedOfSound() const noexcept
250 | //{
251 | // return speedOfSound;
252 | //}
253 | //
254 | //void Doppler::allocate(const float max_Dist, const int max_N, const float newSpeedOfSound)
255 | //{
256 | // // maximum time delay in samples that must be supported
257 | // maxDistance = max_Dist;
258 | // maxN = max_N;
259 | // speedOfSound = newSpeedOfSound;
260 | // allocate();
261 | //// const float maxDelayInSamples = maxDistance/speedOfSound*Fs;
262 | //// inputs.resize(std::ceil(maxDelayInSamples/maxN));
263 | //// for (auto& input : inputs)
264 | //// input.allocate(maxN);
265 | //// reset();
266 | //}
267 | //
268 | //void Doppler::allocate()
269 | //{
270 | // const float maxDelayInSamples = maxDistance/speedOfSound*Fs;
271 | // inputs.resize(std::max(3, (int)std::ceil(maxDelayInSamples/maxN))); // need at least 3 inputs otherwise stuff gets weird...
272 | // for (auto& input : inputs)
273 | // input.allocate(maxN);
274 | // inputs.shrink_to_fit();
275 | // reset();
276 | //}
277 | //
278 | //void Doppler::free()
279 | //{
280 | // inputs.clear();
281 | // inputs.shrink_to_fit();
282 | //}
283 | //
284 | //
285 | //Doppler::Doppler() noexcept
286 | //{
287 | //}
288 | //
289 | //Doppler::~Doppler()
290 | //{
291 | //}
292 |
--------------------------------------------------------------------------------
/Doppler.h:
--------------------------------------------------------------------------------
1 | //
2 | // Doppler.h
3 | // ThreeDAudio
4 | //
5 | // Created by Andrew Barker on 4/2/18.
6 | //
7 | //
8 | /*
9 | 3DAudio: simulates surround sound audio for headphones
10 | Copyright (C) 2016 Andrew Barker
11 |
12 | This program is free software: you can redistribute it and/or modify
13 | it under the terms of the GNU General Public License as published by
14 | the Free Software Foundation, either version 3 of the License, or
15 | (at your option) any later version.
16 |
17 | This program is distributed in the hope that it will be useful,
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | GNU General Public License for more details.
21 |
22 | You should have received a copy of the GNU General Public License
23 | along with this program. If not, see .
24 |
25 | The author can be contacted via email at andrew.barker.12345@gmail.com.
26 | */
27 |
28 | #ifndef __Doppler__
29 | #define __Doppler__
30 |
31 | #include
32 |
33 | static constexpr float defaultSpeedOfSound = 343.0f; // in meters/sec
34 |
35 | class Doppler
36 | {
37 | public:
38 | /*Doppler() noexcept;
39 | ~Doppler();*/
40 | /** process an input audio buffer at certain distance from the listener such that the doppler effect is applied to output */
41 | void process(float distance, int bufferSize, const float* input, float* output) noexcept;
42 | /** allocate enough memory for the doppler effect given a maximum sound source distance (in meters), maximum buffer size, and minimum speed of sound (in m/s) */
43 | void allocate(float maxDistance, int maxBufferSize, float speedOfSoundToPlanAllocationSize);
44 | /** free all memory */
45 | void free() noexcept;
46 | /** reset the doppler effect state */
47 | void reset() noexcept;
48 | /** specify the sample rate of the audio being processed */
49 | void setSampleRate(float sampleRate) noexcept;
50 | /** set the speed of sound for the doppler effect */
51 | void setSpeedOfSound(float speedOfSound) noexcept;
52 | private:
53 | // circular buffer for holding delayed input
54 | std::vector buffer;
55 | // next index to insert input in circular buffer
56 | float bufferInIdx = 0;
57 | // next index to output from circular buffer
58 | float bufferOutIdx = 0;
59 | // sample rate (in Hz)
60 | float sampleRate = 44100;
61 | // in meters / sec
62 | float speedOfSound = defaultSpeedOfSound;
63 | // previous buffer's delay at end (in samples)
64 | float delayPrev = -1;
65 | // distance delay over input sample slope at begining of current input buffer / end of last input buffer
66 | float slopePrev = 0;
67 | // previous sample put into circular buffer
68 | float prevSample = 0;
69 | // previous sample's delay compensated index
70 | float prevSampleDelayedIdx = 0;
71 | };
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | // OLD DOPPLER EFFECT
94 | //#include "PolynomialSpline.h"
95 | //
96 | //static constexpr float defaultSpeedOfSound = 343.0f; // in meters/sec
97 | ////#define SPEED_OF_WAVE 343 //60 //100 // in meters/sec
98 | ////#define ONE_OVER_SoW 0.16//0.00291545189// 0.016 // in sec/meters
99 | ////#define INIT_MAX_CHANGE 0.01
100 | //
101 | //// structure to hold previous input buffers with their relative delays and stretching
102 | //typedef struct _DelayedInput
103 | //{
104 | // std::vector input; // an input buffer
105 | // int inputLength = 0; // the length of the buffer actually used
106 | // float dBegin = 0; // delay in samples from the current buffer's start to the beginning of input
107 | // float dEnd = 0; // delay in samples from the current buffer's start to the end of input (beginning sample of next input)
108 | // int shift = 0;
109 | // int prevSign = 1;
110 | // //CubicFunctionalSpline spline;
111 | // LightweightCubicFunctionalSpline spline; // a 3rd order 2D polynomial curve for generating smoothly stretched output buffers
112 | // //_DelayedInput
113 | // void load(const float* buf, int N,
114 | // float delayBegin, float delayEnd,
115 | // float prevNstr, int prevN) noexcept
116 | // {
117 | // //input = std::vector(buf, buf+N);
118 | // for (int i = 0; i < N; ++i)
119 | // input[i] = buf[i];
120 | // const int inputSize = input.size();
121 | // for (int i = N; i < inputSize; ++i)
122 | // input[i] = 0;
123 | // inputLength = N;
124 | // shift = 0;
125 | // float yBegin, yEnd;
126 | // if (delayBegin < delayEnd+N)
127 | // {
128 | // dBegin = delayBegin;
129 | // dEnd = delayEnd+N;
130 | // yBegin = 0;
131 | // yEnd = N;
132 | // const float splinePts[4][2] =
133 | // { {dBegin-prevNstr, yBegin-prevSign*prevN},
134 | // {dBegin, yBegin},
135 | // {dEnd, yEnd},
136 | // {dEnd+(dEnd-dBegin), yEnd+N} }; // problem here is that if we switch sign the next buffer, the end of this spline's slope will not be perfectly continuous with the begining of the next spline, we're not set up to know the future here... however this seems preferable to forcing the audio to lag one buffer in order to compute a perfectly continuous spline segment, plus the discontinuity should be rather small if the sound barrier is approached gradually.
137 | // spline.calc(splinePts);
138 | //// spline = CubicFunctionalSpline
139 | //// ({dBegin-prevNstr, yBegin-prevSign*prevN},
140 | //// {dBegin,yBegin}, {dEnd,yEnd},
141 | //// {dEnd+(dEnd-dBegin), yEnd+N});
142 | // prevSign = 1;
143 | // }
144 | // else // averaged faster than SPEED_OF_WAVE for change in distance btw buffers, so audio gets flipped backwards
145 | // {
146 | // dBegin = delayEnd+N;
147 | // dEnd = delayBegin;
148 | // yBegin = N;
149 | // yEnd = 0;
150 | // const float splinePts[4][2] =
151 | // { {dBegin-prevNstr, yBegin-prevSign*prevN},
152 | // {dBegin, yBegin},
153 | // {dEnd, yEnd},
154 | // {dEnd+(dEnd-dBegin), yEnd-N} };
155 | // spline.calc(splinePts);
156 | //// spline = CubicFunctionalSpline
157 | //// ({dBegin-prevNstr, yBegin-prevSign*prevN},
158 | //// {dBegin,yBegin}, {dEnd,yEnd},
159 | //// {dEnd+(dEnd-dBegin), yEnd-N});
160 | // prevSign = -1;
161 | // }
162 | //// spline = CubicFunctionalSpline
163 | //// ({dBegin-prevNstr, yBegin-prevN},
164 | //// {dBegin,yBegin}, {dEnd,yEnd},
165 | //// {dEnd+(dEnd-dBegin), yEnd+N});
166 | // }
167 | // void allocate(int maxN)
168 | // {
169 | // input.resize(maxN, 0);
170 | // input.shrink_to_fit(); // not tested thoroughly
171 | // spline.allocate();
172 | // }
173 | // float valueAt(float s, float nextInputSample) noexcept
174 | // {
175 | // //float index = spline.pointAt(s+shift)[0];
176 | // float index;
177 | // spline.pointAt(s+shift, &index);
178 | // int low = index; //floor(index);
179 | // int high = low + 1;
180 | // float k = high - index;
181 | // float value = k*input[low];
182 | // if (high < inputLength/*input.size()*/)
183 | // value += (1-k)*input[high];
184 | // else
185 | // value += (1-k)*nextInputSample;
186 | // return value;
187 | // //return k*input[low] + (1-k)*input[high];
188 | // }
189 | //} DelayedInput;
190 | //
191 | //class Doppler
192 | //{
193 | //public:
194 | // Doppler() {};
195 | // ~Doppler() {};
196 | // // output N processed samples in y given a current distance dist (in meters), and N input samples in x
197 | // void process(float dist, int N, const float* x, float* y) noexcept;
198 | // // processes with an array of N distances, one for each sample
199 | // //void process(const float* dists, int N, const float* x, float* y) noexcept;
200 | // // reset the processing state
201 | // void reset() noexcept;
202 | // void setSampleRate(float sampleRate) noexcept;
203 | // //void setSpeedOfSound(float newSpeedOfSound);
204 | // float getSpeedOfSound() const noexcept;
205 | // // pre-allocate all internal storage prior to realtime audio processing
206 | // void allocate(float maxDist, int max_N, float newSpeedOfSound);
207 | // void free();
208 | //private:
209 | // void allocate();
210 | // std::vector inputs; // storage for delayed and stretched input audio
211 | // float Fs = 44100; // sample rate (in Hz)
212 | // float dPrev = 0; // previous buffer's delay (in samples)
213 | // //float maxChange = INIT_MAX_CHANGE; // in percent of input buffer samples
214 | // //float dDesiredPrev = 0;
215 | // // indecies for the range of inputs yet to be played back
216 | // int oldest = 0;
217 | // int newest = 0;
218 | // float maxN = 0;
219 | // float maxDistance = 0;
220 | // float speedOfSound = defaultSpeedOfSound; // in meters / sec
221 | //};
222 |
223 | #endif /* defined(__Doppler__) */
224 |
--------------------------------------------------------------------------------
/DrawInterpolator.h:
--------------------------------------------------------------------------------
1 | //
2 | // DrawInterpolator.h
3 | //
4 | // Created by Andrew Barker on 9/13/16.
5 | //
6 | //
7 |
8 | #ifndef DrawInterpolator_h
9 | #define DrawInterpolator_h
10 |
11 | #include "../JuceLibraryCode/JuceHeader.h"
12 |
13 | #include
14 | #include
15 |
16 | #include "OpenGL.h"
17 | #include "Interpolator.h"
18 | #include "StackArray.h"
19 | #include "Box.h"
20 | #include "Multi.h"
21 |
22 |
23 | class InterpolatorLook
24 | {
25 | public:
26 | enum Dimensionality { TWO_D, THREE_D };
27 | enum LineType { CONTINUOUS, DASHED, DOTTED };
28 | template
29 | InterpolatorLook (const Interpolator* interp, Dimensionality dim) noexcept;
30 | float begin;
31 | float end;
32 | Dimensionality drawingMode;
33 | std::vector dimensionsToDraw;
34 | int numVertices;
35 | LineType lineType;
36 | float lineSize;
37 | Colour beginColor;
38 | Colour endColor;
39 | float numColorCycles;
40 | float colorCyclePhase; // 0 to 1
41 | };
42 |
43 | template
44 | InterpolatorLook::InterpolatorLook (const Interpolator* interp,
45 | const Dimensionality drawingMode) noexcept
46 | : begin (interp->getInputRange()[0]),
47 | end (interp->getInputRange()[1]),
48 | drawingMode (drawingMode),
49 | dimensionsToDraw (((drawingMode == TWO_D) ? std::vector{0, 1}
50 | : std::vector{0, 1, 2})),
51 | numVertices (100),
52 | lineType (CONTINUOUS),
53 | lineSize (2),
54 | beginColor (Colour::fromFloatRGBA(1, 0, 0, 1)),
55 | endColor (Colour::fromFloatRGBA(0, 0, 1, 1)),
56 | numColorCycles (1.5),
57 | colorCyclePhase (0)
58 | {
59 | }
60 |
61 | static GLenum getGLMode(const InterpolatorLook& look) noexcept
62 | {
63 | switch (look.lineType) {
64 | case InterpolatorLook::CONTINUOUS:
65 | return GL_LINE_STRIP;
66 | case InterpolatorLook::DASHED:
67 | return GL_LINES;
68 | case InterpolatorLook::DOTTED:
69 | return GL_POINTS;
70 | };
71 | }
72 |
73 | static Colour getInterpolatedColor(const Colour beginColor,
74 | const Colour endColor,
75 | const int numCycles,
76 | const float phase,
77 | const float position) noexcept
78 | {
79 | cauto dColor = std::fmod(2 * ((numCycles - 0.5f) * position + phase), 2.0f);
80 | cauto color = dColor < 1 ? beginColor.interpolatedWith(endColor, dColor)
81 | : endColor.interpolatedWith(beginColor, dColor - 1);
82 | return color;
83 | }
84 |
85 | static void glInterpolatedColor(const InterpolatorLook& look,
86 | const float position)
87 | {
88 | glColour(getInterpolatedColor(look.beginColor, look.endColor, look.numColorCycles, look.colorCyclePhase, position));
89 | }
90 |
91 | template
92 | void draw(const ParametricInterpolator* interp,
93 | const InterpolatorLook& look)
94 | {
95 | cauto inputRange = interp->getInputRange();
96 | cauto begin = std::max(inputRange[0], look.begin);
97 | cauto percentOfEnd = (interp->getType() == InterpolatorType::CLOSED_PARAMETRIC && interp->getNumPoints() == 2) ? 0.5f : 0.9999999f;
98 | cauto end = std::min(inputRange[1] * percentOfEnd, look.end);
99 | cauto length = end - begin;
100 | cauto interval = length / look.numVertices;
101 |
102 | GLboolean prevAntiAliasing;
103 | glGetBooleanv(GL_LINE_SMOOTH, &prevAntiAliasing);
104 | glEnable(GL_LINE_SMOOTH); // enable antialiasing
105 | glInterpolatedColor(look, 0);
106 | GLfloat prevGLSize;
107 | if (look.lineType == InterpolatorLook::DOTTED) {
108 | glGetFloatv(GL_POINT_SIZE, &prevGLSize);
109 | glPointSize(look.lineSize);
110 | } else {
111 | glGetFloatv(GL_LINE_WIDTH, &prevGLSize);
112 | glLineWidth(look.lineSize);
113 | }
114 | cauto glMode = getGLMode(look);
115 |
116 | glBegin(glMode);
117 | cauto numDimensions = interp->getNumDimensions();
118 | STACK_ARRAY(float, pt, numDimensions - 1);
119 | for (auto t = begin; t < end; t += interval) {
120 | if (interp->pointAt(t, pt)) {
121 | if (look.drawingMode == InterpolatorLook::TWO_D)
122 | glVertex2f(pt[0], pt[1]);
123 | else
124 | glVertex3f(pt[0], pt[1], pt[2]);
125 | }
126 | glInterpolatedColor(look, (t - begin) / length);
127 | }
128 | glEnd();
129 |
130 | if (look.lineType == InterpolatorLook::DOTTED)
131 | glPointSize(prevGLSize);
132 | else
133 | glLineWidth(prevGLSize);
134 | if (prevAntiAliasing == GL_FALSE)
135 | glDisable(GL_LINE_SMOOTH); // disable antialiasing
136 | }
137 |
138 | template
139 | void draw(const FunctionalInterpolator* interp,
140 | const InterpolatorLook& look)
141 | {
142 | const auto pts = interp->getPoints();
143 | const auto inputRange = interp->getInputRange();
144 | const auto begin = std::max(inputRange[0], look.begin);
145 | const auto end = std::min(inputRange[1], look.end);
146 | const float interval = (end - begin) / look.numVertices;
147 | if (interval <= 0.0000001f)
148 | return; // avoid inf loop below
149 | const int numDimensions = interp->getNumDimensions();
150 | STACK_ARRAY(float, pt, numDimensions-1);
151 | float x = begin;
152 | int splineIndex = 0;
153 | int prevSplineIndex = 0;
154 | bool dontReset = false;
155 | bool doAgain = true;
156 | GLboolean prevAntiAliasing;
157 | glGetBooleanv(GL_LINE_SMOOTH, &prevAntiAliasing);
158 | glEnable(GL_LINE_SMOOTH); // enable antialiasing
159 | glInterpolatedColor(look, 0);
160 | GLfloat prevGLSize;
161 | if (look.lineType == InterpolatorLook::DOTTED) {
162 | glGetFloatv(GL_POINT_SIZE, &prevGLSize);
163 | glPointSize(look.lineSize);
164 | } else {
165 | glGetFloatv(GL_LINE_WIDTH, &prevGLSize);
166 | glLineWidth(look.lineSize);
167 | }
168 | const auto glMode = getGLMode(look);
169 | glBegin(glMode);
170 | AGAIN: // man this got complicated...
171 | int count = 0;
172 | while (x <= end) {
173 | if (++count > look.numVertices) // trying to avoid inf loop at all costs
174 | break;
175 | // only draw the dotted line over the portions that are not open/empty segments,
176 | if (interp->pointAtSmart(x, pt, splineIndex)) {
177 | if (prevSplineIndex+2 <= splineIndex ? pts[prevSplineIndex+1][0] != pts[splineIndex][0] : true) {
178 | if (look.drawingMode == InterpolatorLook::TWO_D)
179 | glVertex2f(x, pt[look.dimensionsToDraw[1]-1]);
180 | else
181 | glVertex3f(x, pt[look.dimensionsToDraw[1]-1], pt[look.dimensionsToDraw[2]-1]);
182 | } else if (!dontReset) { // avoiding drawing anything between 2 or more pts with the same x val
183 | glEnd();
184 | glBegin(glMode);
185 | dontReset = false;
186 | }
187 | x += interval;
188 | // draw verticies at any points that would otherwise get skipped over
189 | int lastSplineIndex = -1;
190 | for (int i = 1; splineIndex+i < pts.size() && x > pts[splineIndex+i][0]; ++i) {
191 | lastSplineIndex = splineIndex + i;
192 | if (pts[lastSplineIndex][0] != pts[lastSplineIndex-1][0]
193 | && interp->getSplineShape(lastSplineIndex-1) != SplineShape::EMPTY) {
194 | if (look.drawingMode == InterpolatorLook::TWO_D)
195 | glVertex2f(pts[lastSplineIndex][0],
196 | pts[lastSplineIndex][look.dimensionsToDraw[1]]);
197 | else
198 | glVertex3f(pts[lastSplineIndex][0],
199 | pts[lastSplineIndex][look.dimensionsToDraw[1]],
200 | pts[lastSplineIndex][look.dimensionsToDraw[2]]);
201 | } else {
202 | glEnd();
203 | glBegin(glMode);
204 | }
205 | }
206 | if (lastSplineIndex >= 0) {
207 | if (look.drawingMode == InterpolatorLook::TWO_D)
208 | glVertex2f(pts[lastSplineIndex][0],
209 | pts[lastSplineIndex][look.dimensionsToDraw[1]]);
210 | else
211 | glVertex3f(pts[lastSplineIndex][0],
212 | pts[lastSplineIndex][look.dimensionsToDraw[1]],
213 | pts[lastSplineIndex][look.dimensionsToDraw[2]]);
214 | dontReset = true;
215 | }
216 | } else {
217 | // finish drawing any unfinished GL_LINE segments
218 | if (look.drawingMode == InterpolatorLook::TWO_D)
219 | glVertex2f(pts[splineIndex][0],
220 | pts[splineIndex][look.dimensionsToDraw[1]]);
221 | else
222 | glVertex3f(pts[splineIndex][0],
223 | pts[splineIndex][look.dimensionsToDraw[1]],
224 | pts[splineIndex][look.dimensionsToDraw[2]]);
225 | // gotta reset the gl line drawing state so we don't possibly draw a single line segment across an open segment
226 | glEnd();
227 | glBegin(glMode);
228 | if (splineIndex + 1 < pts.size())
229 | x = pts[splineIndex + 1][0]; // skip the loop to the begining of the next segment since this one is either open or spatially nonexistant in x
230 | else
231 | x = end; // goto last iteration of while loop
232 | }
233 | glInterpolatedColor(look, (x - begin) / (end - begin));
234 | prevSplineIndex = splineIndex;
235 | }
236 | if (doAgain && x - interval < end // make sure the last x point is the end of specified range
237 | && (pts.back()[0] <= look.end ? pts[pts.size()-2][0] < pts.back()[0] : true)) {
238 | x = end;
239 | doAgain = false;
240 | goto AGAIN;
241 | }
242 | glEnd();
243 | if (look.lineType == InterpolatorLook::DOTTED)
244 | glPointSize(prevGLSize);
245 | else
246 | glLineWidth(prevGLSize);
247 | if (prevAntiAliasing == GL_FALSE)
248 | glDisable(GL_LINE_SMOOTH); // disable antialiasing
249 | }
250 |
251 | class PointLook
252 | {
253 | public:
254 | float radius;
255 | float mouseOverRadius;
256 | Colour color;
257 | Colour selectedColor;
258 | float animationDuration;
259 | };
260 |
261 | class DrawablePointState
262 | {
263 | public:
264 | // need copy of these for each interp...
265 | Multi mouseOverAnimations;
266 | Multi selectAnimations;
267 | std::vector prevMouseOvers;
268 | std::vector prevSelecteds;
269 | };
270 |
271 | static void drawCircleOutline(const Point position,
272 | const float radius,
273 | const int segments,
274 | const float windowWidth,
275 | const float windowHeight)
276 | {
277 | // GLboolean prevAntiAliasing;
278 | // glGetBooleanv(GL_LINE_SMOOTH, &prevAntiAliasing);
279 | // glEnable(GL_LINE_SMOOTH); // enable antialiasing
280 | glBegin(GL_LINE_LOOP);
281 | cauto aspect = windowHeight / windowWidth;
282 | for (int n = 0; n <= segments; ++n) {
283 | cauto t = 2 * M_PI * (float)n / segments;
284 | glVertex2f(position.x + aspect * radius * std::sin(t),
285 | position.y + radius * std::cos(t));
286 | }
287 | glEnd();
288 | // if (prevAntiAliasing == GL_FALSE)
289 | // glDisable(GL_LINE_SMOOTH); // disable antialiasing
290 | }
291 |
292 | static void drawCircle(const Point position,
293 | const float radius,
294 | const int segments,
295 | const float windowWidth,
296 | const float windowHeight)
297 | {
298 | glBegin(GL_TRIANGLE_FAN);
299 | glVertex2f(position.x, position.y);
300 | cauto aspect = windowHeight / windowWidth;
301 | for (int n = 0; n <= segments; ++n) {
302 | cauto t = 2 * M_PI * (float)n / segments;
303 | glVertex2f(position.x + aspect * radius * std::sin(t),
304 | position.y + radius * std::cos(t));
305 | }
306 | glEnd();
307 | GLboolean antiAliasing;
308 | glGetBooleanv(GL_LINE_SMOOTH, &antiAliasing);
309 | if (antiAliasing) {
310 | GLfloat prevLineWidth;
311 | glGetFloatv(GL_LINE_WIDTH, &prevLineWidth);
312 | glLineWidth(1);
313 | drawCircleOutline(position, radius, segments, windowWidth, windowHeight);
314 | glLineWidth(prevLineWidth);
315 | }
316 | }
317 |
318 | template
319 | std::vector> convertPoints(const std::vector>& points,
320 | const int dimX = 0,
321 | const int dimY = 1)
322 | {
323 | std::vector> pts (points.size());
324 | for (int i = 0; i < points.size(); ++i) {
325 | pts[i].x = points[i][dimX];
326 | pts[i].y = points[i][dimY];
327 | }
328 | return pts;
329 | }
330 |
331 | template
332 | void drawPoints2D(const std::vector>& points,
333 | const PointLook& look,
334 | DrawablePointState& state,
335 | const Point& mousePosition,
336 | const OpenGLWindow& window,
337 | const Box& selectBox,
338 | const Box& antiSelectBox,
339 | int& mouseOverPointIndex,
340 | bool mouseOverEnabled = true,
341 | const float viewWidth = 1.0,
342 | const std::array& range = {0, 0})
343 | {
344 | GLfloat prevLineWidth;
345 | glGetFloatv(GL_LINE_WIDTH, &prevLineWidth);
346 | GLboolean prevAntiAliasing;
347 | glGetBooleanv(GL_LINE_SMOOTH, &prevAntiAliasing);
348 | glEnable(GL_LINE_SMOOTH); // enable antialiasing
349 | mouseOverPointIndex = -1;
350 | auto resized = false;
351 | if (state.prevMouseOvers.size() != points.size()) {
352 | state.prevMouseOvers.resize(points.size(), false);
353 | resized = true;
354 | }
355 | if (state.prevSelecteds.size() != points.size()) {
356 | state.prevSelecteds.resize(points.size(), false);
357 | resized = true;
358 | }
359 | Colour color;
360 | float radius;
361 | for (int i = 0; i < points.size(); ++i) {
362 | cauto x = points[i].point[0];
363 | cauto y = points[i].point[1];
364 | if (range[0] <= x && x <= range[1]) {
365 | radius = look.radius;
366 | cauto yRadius = pixelsToNormalized(look.mouseOverRadius, window.height);
367 | cauto xRadius = pixelsToNormalized(look.mouseOverRadius, window.width) * viewWidth;// radius * viewWidth * window.getAspect();
368 | const Box b { y + yRadius, y - yRadius,
369 | x - xRadius, x + xRadius };
370 | cauto mouseOver = mouseOverEnabled &&
371 | ((mouseOverPointIndex == -1 && b.contains(mousePosition)) || selectBox.contains({x, y})) &&
372 | !antiSelectBox.contains({x, y});
373 | if (mouseOver) {
374 | auto animation = state.mouseOverAnimations.get(i);
375 | if (state.prevMouseOvers[i] && animation) {
376 | animation->advance(window.frameRate);
377 | radius = look.radius + (look.mouseOverRadius - look.radius) * animation->getProgress();
378 | color = look.color.interpolatedWith(look.selectedColor, animation->getProgress());
379 | glColour(color);
380 | if (!animation->isPlaying())
381 | state.mouseOverAnimations.remove(i);
382 | } else if (!state.prevMouseOvers[i]){
383 | state.mouseOverAnimations.add(i, look.animationDuration, true);
384 | color = look.color;
385 | radius = look.radius;
386 | } else {
387 | color = look.selectedColor;
388 | radius = look.mouseOverRadius;
389 | }
390 | state.prevMouseOvers[i] = true;
391 | mouseOverPointIndex = i;
392 | } else {
393 | if (state.prevMouseOvers[i] && !resized)
394 | state.mouseOverAnimations.add(i, look.animationDuration, true);
395 | auto animation = state.mouseOverAnimations.get(i);
396 | if (animation) {
397 | animation->advance(window.frameRate);
398 | radius = look.mouseOverRadius + (look.radius - look.mouseOverRadius) * animation->getProgress();
399 | color = look.selectedColor.interpolatedWith(look.color, animation->getProgress());
400 | if (!animation->isPlaying())
401 | state.mouseOverAnimations.remove(i);
402 | } else {
403 | radius = look.radius;
404 | color = look.color;
405 | }
406 | }
407 | if (points[i].selected) {
408 | if (!state.prevSelecteds[i] /*&& !resized*/)
409 | state.selectAnimations.add(i, look.animationDuration, true);
410 | state.prevSelecteds[i] = true;
411 | auto rad = 2 * radius;
412 | auto lineWidth = 1.5f;
413 | auto animation = state.selectAnimations.get(i);
414 | if (animation) {
415 | cauto progress = animation->getProgress();
416 | lineWidth = 10 - (10 - lineWidth) * progress;// 15 - 13.5f * progress;
417 | color = look.color.interpolatedWith(look.selectedColor, progress).withAlpha(progress);
418 | glColour(color);
419 | rad = (2 - progress) * radius;
420 | animation->getProgress();
421 | animation->advance(window.frameRate);
422 | if (!animation->isPlaying())
423 | state.selectAnimations.remove(i);
424 | } else {
425 | radius -= 1;//look.radius - 1;
426 | rad = radius + 1.5f;
427 | color = look.selectedColor;
428 | glColour(color);
429 | }
430 | glLineWidth(lineWidth);
431 | drawCircleOutline({x, y}, pixelsToNormalized(rad, window.height), 12, window.width / viewWidth, window.height);
432 | } else {
433 | if (state.prevSelecteds[i] && !resized)
434 | state.selectAnimations.add(i, look.animationDuration, true);
435 | state.prevSelecteds[i] = false;
436 | auto rad = 2 * radius;
437 | auto lineWidth = 1.5f;
438 | auto animation = state.selectAnimations.get(i);
439 | if (animation) {
440 | cauto progress = animation->getProgress();
441 | color = look.selectedColor.interpolatedWith(look.color, progress).withAlpha(1 - progress);
442 | glColour(color);
443 | rad = (1 + progress) * radius;
444 | animation->getProgress();
445 | animation->advance(window.frameRate);
446 | if (!animation->isPlaying())
447 | state.selectAnimations.remove(i);
448 | glLineWidth(lineWidth + (10 - lineWidth) * progress);
449 | drawCircleOutline({x, y}, pixelsToNormalized(rad, window.height), 12, window.width / viewWidth, window.height);
450 | }
451 | }
452 | if (!mouseOver)
453 | state.prevMouseOvers[i] = false;
454 | glColour(color.withAlpha(1.0f));
455 | drawCircle({x, y}, pixelsToNormalized(radius, window.height), 12, window.width / viewWidth, window.height);
456 | }
457 | }
458 | if (prevAntiAliasing == GL_FALSE)
459 | glDisable(GL_LINE_SMOOTH); // disable antialiasing
460 |
461 | glDisable(GL_POLYGON_SMOOTH);
462 | glDisable(GL_POINT_SMOOTH);
463 | glDisable(GL_LINE_SMOOTH);
464 | glLineWidth(prevLineWidth);
465 | }
466 |
467 | template
468 | void draw(const Interpolator* interp,
469 | const InterpolatorLook& look)
470 | {
471 | if (!interp)
472 | return;
473 | if (interp->getPoints().size() < 2) // no path to draw
474 | return;
475 | if (interp->getType() == InterpolatorType::FUNCTIONAL) {
476 | draw(dynamic_cast* const>(interp), look);
477 | } else { // parametric
478 | draw(dynamic_cast* const>(interp), look);
479 | }
480 | }
481 |
482 | template
483 | void draw(const Interpolator* interp,
484 | const InterpolatorLook& look,
485 | GLuint& displayList)
486 | {
487 | if (displayList != 0) {
488 | glCallList(displayList);
489 | return;
490 | } else {
491 | glDeleteLists(displayList, 1);
492 | displayList = glGenLists(1);
493 | glNewList(displayList, GL_COMPILE_AND_EXECUTE);
494 | draw(interp, look);
495 | glEndList();
496 | }
497 | }
498 |
499 | #endif /* DrawInterpolator_h */
500 |
--------------------------------------------------------------------------------
/DrewLib.h:
--------------------------------------------------------------------------------
1 | //
2 | // DrewLib.h
3 | // Andrew Barker's C++ convenience library.
4 | //
5 | // Created by Andrew Barker on 11/13/16.
6 | //
7 | //
8 |
9 | #ifndef DrewLib_h
10 | #define DrewLib_h
11 |
12 | #define cauto const auto
13 | #define cint const int
14 | #define cfloat const float
15 |
16 | #ifdef WIN32
17 | static constexpr float M_PI = 3.14159265358979323846264338327950288;
18 | static constexpr float M_PI_2 = 1.57079632679489661923132169163975144;
19 | #endif
20 |
21 | #include "Functions.h"
22 |
23 | #endif /* DrewLib_h */
24 |
--------------------------------------------------------------------------------
/Functions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Functions.h
3 | //
4 | // Created by Andrew Barker on 4/26/14.
5 | //
6 | //
7 | /*
8 | 3DAudio: simulates surround sound audio for headphones
9 | Copyright (C) 2016 Andrew Barker
10 |
11 | This program is free software: you can redistribute it and/or modify
12 | it under the terms of the GNU General Public License as published by
13 | the Free Software Foundation, either version 3 of the License, or
14 | (at your option) any later version.
15 |
16 | This program is distributed in the hope that it will be useful,
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | GNU General Public License for more details.
20 |
21 | You should have received a copy of the GNU General Public License
22 | along with this program. If not, see .
23 |
24 | The author can be contacted via email at andrew.barker.12345@gmail.com.
25 | */
26 |
27 | #ifndef Functions_h
28 | #define Functions_h
29 |
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include "DrewLib.h"
35 |
36 | //inline float lagrangeInterpolate(const float y1, const float y2, const float y3, const float y4, const float x) noexcept
37 | //{
38 | // const float xm1 = x-1.0;
39 | // const float xm2 = x-2.0;
40 | // const float xm3 = x-3.0;
41 | // const float xm4 = x-4.0;
42 | //
43 | //// float a0 = (xm2)*(xm3)*(xm4)/(-6.0)*y1;
44 | //// float a1 = (xm1)*(xm3)*(xm4)/(2.0)*y2;
45 | //// float a2 = (xm1)*(xm2)*(xm4)/(-2.0)*y3;
46 | //// float a3 = (xm1)*(xm2)*(xm3)/(6.0)*y4;
47 | //
48 | // // the need for speed ...
49 | // const float a0 = (xm2)*(xm3)*(xm4)*-0.1666666666666666667*y1;
50 | // const float a1 = (xm1)*(xm3)*(xm4)*0.5*y2;
51 | // const float a2 = (xm1)*(xm2)*(xm4)*-0.5*y3;
52 | // const float a3 = (xm1)*(xm2)*(xm3)*0.1666666666666666667*y4;
53 | //
54 | // return a0 + a1 + a2 + a3;
55 | //// return ((x-2.0)*(x-3.0)*(x-4.0)/(-6.0)*y1 + (x-1.0)*(x-3.0)*(x-4.0)/(2.0)*y2
56 | //// + (x-1.0)*(x-2.0)*(x-4.0)/(-2.0)*y3 + (x-1.0)*(x-2.0)*(x-3.0)/(6.0)*y4);
57 | //}
58 | //
59 | //// the vectorized version of the one above
60 | //inline void lagrangeInterpolate(const float* y1, const float* y2, const float* y3, const float* y4, const int N, const float x, float* output) noexcept
61 | //{
62 | // const float xm1 = x-1.0;
63 | // const float xm2 = x-2.0;
64 | // const float xm3 = x-3.0;
65 | // const float xm4 = x-4.0;
66 | // const float a1 = (xm2)*(xm3)*(xm4)*-0.1666666666666666667;
67 | // const float a2 = (xm1)*(xm3)*(xm4)*0.5;
68 | // const float a3 = (xm1)*(xm2)*(xm4)*-0.5;
69 | // const float a4 = (xm1)*(xm2)*(xm3)*0.1666666666666666667;
70 | // for (int n = 0; n < N; ++n)
71 | // output[n] = a1*y1[n] + a2*y2[n] + a3*y3[n] + a4*y4[n];
72 | //}
73 |
74 | /** true if Set s, contains value v */
75 | template
76 | bool contains(const Set& s, const Value& v) noexcept
77 | {
78 | return std::find(s.begin(), s.end(), v) != s.end();
79 | }
80 |
81 | /** rotates the specified indices (must be sorted) by delta. the non-rotated indices may be displaced in the same direction but their relative ordering is preserved. */
82 | template
83 | void partial_rotate(std::vector& v, const std::vector& indicesToRotate, const int delta) {
84 | cauto copy = v;
85 | std::vector> other (copy.size() - indicesToRotate.size());
86 | std::vector inPlace (copy.size(), false);
87 | for (int i = 0, j = 0, k = 0; i < copy.size(); ++i) {
88 | if (k == indicesToRotate.size() || i != indicesToRotate[k])
89 | other[j++] = {i, copy[i]};
90 | else
91 | ++k;
92 | }
93 | for (int i = 0, j; i < indicesToRotate.size(); ++i) {
94 | j = indicesToRotate[i] + delta;
95 | while (j < 0)
96 | j += copy.size();
97 | while (j >= copy.size())
98 | j -= copy.size();
99 | v[j] = copy[indicesToRotate[i]];
100 | inPlace[j] = true;
101 | }
102 | for (int i = 0, j; i < other.size(); ++i) {
103 | j = other[i].first;
104 | while (inPlace[j])
105 | j = (j+1) % copy.size();
106 | v[j] = other[i].second;
107 | inPlace[j] = true;
108 | }
109 | }
110 |
111 | // should be in C++17
112 | namespace std {
113 | template
114 | constexpr T hypot(const T x, const T y, const T z) noexcept
115 | {
116 | return std::sqrt(x*x + y*y + z*z);
117 | }
118 |
119 | template
120 | constexpr const T& clamp( const T& v, const T& lo, const T& hi, Compare comp )
121 | {
122 | return assert( !comp(hi, lo) ),
123 | comp(v, lo) ? lo : comp(hi, v) ? hi : v;
124 | }
125 |
126 | template
127 | constexpr const T& clamp( const T& v, const T& lo, const T& hi )
128 | {
129 | return clamp( v, lo, hi, std::less<>() );
130 | }
131 | }
132 |
133 | template
134 | T boundsCheck(const T value, const T min, const T max)
135 | {
136 | return std::max(std::min(value, max), min);
137 | }
138 |
139 | // circular input buffer convolution
140 | inline void convolve(const float *cBuf, const int cBufIdx, const int cBufN,
141 | const float *h, const int Nh, const float hScale,
142 | float* output, const int N) noexcept
143 | {
144 | // for each output sample
145 | for (int n = 0; n < N; ++n) {
146 | // for each overlaping sample of the two signals
147 | float sum = 0;
148 | int i = (cBufIdx + n) % cBufN;
149 | for (int j = 0; j < Nh; ++j) {
150 | sum += cBuf[i] * h[j];
151 | if (--i < 0)
152 | i = cBufN - 1;
153 | }
154 | output[n] = sum * hScale;
155 | }
156 | }
157 |
158 | inline void convolve(const float *cBuf, const int cBufIdx, const int cBufN,
159 | const float *hs, const int Nh, const int numHs, const float *hScales, const int ch,
160 | float *output, const int N) noexcept
161 | {
162 | // for each output sample
163 | for (int n = 0; n < N; ++n) {
164 | const float L = N / float(numHs - 1);
165 | const float ndL = n / L;
166 | const int hi = ndL;
167 | const int hIdx1 = 2 * hi + ch;
168 | const int hIdx2 = 2 * (hi + 1) + ch;
169 | const float *h1 = &hs[hIdx1 * Nh],
170 | *h2 = &hs[hIdx2 * Nh];
171 | int i = (cBufIdx + n) % cBufN;
172 | float sum1 = 0, sum2 = 0;
173 | // for each overlaping sample of the two signals
174 | for (int j = 0; j < Nh; ++j) {
175 | sum1 += cBuf[i] * h1[j];
176 | sum2 += cBuf[i] * h2[j];
177 | if (--i < 0)
178 | i = cBufN - 1;
179 | }
180 | const float hBlend = ndL - hi; //std::fmod(n, L);
181 | output[n] = sum1 * hScales[hIdx1] * (1-hBlend) + sum2 * hScales[hIdx2] * hBlend;
182 | }
183 | }
184 |
185 | // time domain convolution
186 | /*static*/
187 | inline void convolve(const float *x, int Nx, const float *h, int Nh, float *output) noexcept
188 | {
189 | const int Nxm1 = Nx-1;
190 | const int Nhm1 = Nh-1;
191 | const int L = Nx+Nhm1;//Nx+Nh-1;
192 | int xi = 0;
193 | int xf = 0;
194 | int hi = 0;
195 |
196 | // for each output sample
197 | for (int i = 0; i < L; ++i)
198 | {
199 | // for each overlaping sample of two signals
200 | const int M = xf-xi+1;
201 | float sum = 0;
202 | for (int j = 0; j < M; ++j)
203 | sum += x[xf-j] * h[hi+j];
204 |
205 | // shifting
206 | if (xf < Nxm1/*Nx-1*/)
207 | ++xf;
208 | else if (hi < Nhm1/*Nh-1*/)
209 | ++hi;
210 |
211 | if (i > Nhm1/*Nh-1*/)
212 | ++xi;
213 |
214 | output[i] = sum;
215 | }
216 | }
217 |
218 | // only compute convolution for the output samples from beginIndex to endIndex inclusive
219 | /*static*/
220 | inline void convolve(const float *x, const int Nx, const float *h, const int Nh, float *output, const int beginIndex, const int endIndex) noexcept
221 | {
222 | const int Nxm1 = Nx-1;
223 | const int Nhm1 = Nh-1;
224 | //const int L = Nx+Nhm1;//Nx+Nh-1;
225 | int xi, xf, hi;
226 |
227 | // find xi xf and hi starting at begin index
228 | if (beginIndex > Nhm1/*Nh-1*/)
229 | xi = beginIndex - Nh;
230 | else
231 | xi = 0;
232 |
233 | if (beginIndex <= Nxm1/*Nx-1*/)
234 | {
235 | xf = beginIndex;
236 | hi = 0;
237 | }
238 | else
239 | {
240 | xf = Nxm1/*Nx-1*/;
241 | hi = beginIndex - Nxm1/*(Nx-1)*/;
242 | if (hi > Nhm1/*Nh-1*/)
243 | hi = Nhm1/*Nh-1*/;
244 | }
245 |
246 | // for each output sample
247 | for (int i = beginIndex; i <= endIndex; ++i)
248 | {
249 | // for each overlaping sample of two signals
250 | const int M = xf-xi+1;
251 | float sum = 0;
252 | for (int j = 0; j < M; ++j)
253 | sum += x[xf-j] * h[hi+j];
254 |
255 | // shifting
256 | if (xf < Nxm1/*Nx-1*/)
257 | ++xf;
258 | else if (hi < Nhm1/*Nh-1*/)
259 | ++hi;
260 |
261 | if (i > Nhm1/*Nh-1*/)
262 | ++xi;
263 |
264 | output[i] = sum;
265 | }
266 | }
267 |
268 | //template
269 | //T inner_product(InputIt1 first1, InputIt1 last1,
270 | // InputIt2 first2, T value)
271 | //{
272 | // while (first1 != last1) {
273 | // value += (*first1) * (*first2);
274 | // --first1;
275 | // ++first2;
276 | // }
277 | // return value;
278 | //}
279 | //
280 | //inline void convolve2(float *x, const int Nx, float *h, const int Nh, float *output, const int beginIndex, const int endIndex)
281 | //{
282 | // const int L = Nx+Nh-1;
283 | // int xi, xf, hi;
284 | //
285 | // // find xi xf and hi starting at begin index
286 | // if (beginIndex > Nh-1)
287 | // xi = beginIndex - Nh;
288 | // else
289 | // xi = 0;
290 | //
291 | // if (beginIndex <= Nx-1)
292 | // {
293 | // xf = beginIndex;
294 | // hi = 0;
295 | // }
296 | // else
297 | // {
298 | // xf = Nx-1;
299 | // hi = beginIndex - (Nx-1);
300 | // if (hi > Nh-1)
301 | // hi = Nh-1;
302 | // }
303 | //
304 | // // for each output sample
305 | // for (int i = beginIndex; i <= endIndex; ++i)
306 | // {
307 | // // for each overlaping sample of two signals
308 | // const int M = xf-xi+1;
309 | // output[i] = inner_product(&x[xf], &x[xf-M], &h[hi], 0);
310 | //
311 | // // shifting
312 | // if (xf < Nx-1)
313 | // ++xf;
314 | // else if (hi < Nh-1)
315 | // ++hi;
316 | //
317 | // if (i > Nh-1)
318 | // ++xi;
319 | // }
320 | //}
321 |
322 | inline float toDegrees(float radians) noexcept
323 | {
324 | return radians * 180 / M_PI;
325 | }
326 |
327 | inline float toRadians(float degrees) noexcept
328 | {
329 | return degrees * M_PI / 180;
330 | }
331 |
332 | inline void XYZtoRAE(const float* xyz, float* rae) noexcept
333 | {
334 | rae[0] = std::sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2]);
335 | if (rae[0] != 0)
336 | rae[2] = std::acos(xyz[1]/rae[0]);
337 | else // avoid divide by zero
338 | rae[2] = 0;
339 |
340 | if (xyz[0] < 0)
341 | rae[1] = std::atan(xyz[2]/xyz[0]) + M_PI;
342 | else if (xyz[0] > 0)
343 | rae[1] = std::atan(xyz[2]/xyz[0]);
344 | else // avoid divide by zero
345 | if (xyz[2] < 0)
346 | rae[1] = -M_PI_2;
347 | else
348 | rae[1] = M_PI_2;
349 |
350 | while (rae[1] > 2.0*M_PI)
351 | rae[1] -= 2.0*M_PI;
352 |
353 | while (rae[1] < 0)
354 | rae[1] += 2.0*M_PI;
355 | }
356 |
357 | inline void RAEtoXYZ(const float* rae, float* xyz) noexcept
358 | {
359 | xyz[0] = rae[0]*std::sin(rae[2])*std::cos(rae[1]);
360 | xyz[1] = rae[0]*std::cos(rae[2]);
361 | xyz[2] = rae[0]*std::sin(rae[2])*std::sin(rae[1]);
362 | }
363 |
364 | inline float angleBetween(const std::vector& a,
365 | const std::vector& b) noexcept
366 | {
367 | if (a.size() != b.size())
368 | return -1;
369 | float dotProduct = 0, magA = 0, magB = 0;
370 | for (int i = 0; i < a.size(); ++i) {
371 | dotProduct += a[i] * b[i];
372 | magA += a[i] * a[i];
373 | magB += b[i] * b[i];
374 | }
375 | return std::acos(dotProduct / (std::sqrt(magA) * std::sqrt(magB)));
376 | }
377 |
378 | template
379 | std::vector toVector(const float* array, const std::size_t N)
380 | {
381 | std::vector vector (N);
382 | for (int i = 0; i < N; ++i)
383 | vector[i] = array[i];
384 | return vector;
385 | }
386 |
387 | //inline void XYZtoRAE(const float (&xyz)[3], float (&rae)[3])
388 | //{
389 | // rae[0] = std::sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2]);
390 | // if (rae[0] != 0)
391 | // rae[2] = std::acos(xyz[1]/rae[0]);
392 | // else // avoid divide by zero
393 | // rae[2] = 0;
394 | //
395 | // if (xyz[0] < 0)
396 | // rae[1] = std::atan(xyz[2]/xyz[0]) + M_PI;
397 | // else if (xyz[0] > 0)
398 | // rae[1] = std::atan(xyz[2]/xyz[0]);
399 | // else // avoid divide by zero
400 | // rae[1] = 0;
401 | //
402 | // while (rae[1] > 2.0*M_PI)
403 | // rae[1] -= 2.0*M_PI;
404 | //
405 | // while (rae[1] < 0)
406 | // rae[1] += 2.0*M_PI;
407 | //}
408 | //
409 | //inline void RAEtoXYZ(const float (&rae)[3], float (&xyz)[3])
410 | //{
411 | // xyz[0] = rae[0]*std::sin(rae[2])*std::cos(rae[1]);
412 | // xyz[1] = rae[0]*std::cos(rae[2]);
413 | // xyz[2] = rae[0]*std::sin(rae[2])*std::sin(rae[1]);
414 | //}
415 | //
416 | //// more C++ er
417 | //inline void XYZtoRAE(const std::array& xyz, std::array& rae)
418 | //{
419 | // rae[0] = std::sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1] + xyz[2]*xyz[2]);
420 | // if (rae[0] != 0)
421 | // rae[2] = std::acos(xyz[1]/rae[0]);
422 | // else // avoid divide by zero
423 | // rae[2] = 0;
424 | //
425 | // if (xyz[0] < 0)
426 | // rae[1] = std::atan(xyz[2]/xyz[0]) + M_PI;
427 | // else if (xyz[0] > 0)
428 | // rae[1] = std::atan(xyz[2]/xyz[0]);
429 | // else // avoid divide by zero
430 | // rae[1] = 0;
431 | //
432 | // while (rae[1] > 2.0*M_PI)
433 | // rae[1] -= 2.0*M_PI;
434 | //
435 | // while (rae[1] < 0)
436 | // rae[1] += 2.0*M_PI;
437 | //}
438 | //
439 | //inline void RAEtoXYZ(const std::array& rae, std::array& xyz)
440 | //{
441 | // xyz[0] = rae[0]*std::sin(rae[2])*std::cos(rae[1]);
442 | // xyz[1] = rae[0]*std::cos(rae[2]);
443 | // xyz[2] = rae[0]*std::sin(rae[2])*std::sin(rae[1]);
444 | //}
445 |
446 |
447 |
448 | //// x assumed to be between -1.0 and +1.0
449 | //static double raisedCosine(double x)
450 | //{
451 | // double y;
452 | //
453 | // // B between 0 (square pulse) and 1 (sinc-like pulse)
454 | // // double B = 0.3;
455 | //
456 | // //length = 1/T = 1.0 -> T = 1.0
457 | //
458 | // // if (fabs(x) <= (1-B)/2.0) {
459 | // // y = 1;
460 | // // } if (fabs(x) <= (1+B)/2.0) {
461 | // // y = 0.5*(1.0 + cos(M_PI/B * (fabs(x) - (1.0-B)/2.0)));
462 | // // } else {
463 | // // y = 0;
464 | // // }
465 | //
466 | // // y = cos(M_PI/2.0 * x);
467 | // y = 0.5*(1.0 + cos(M_PI * x));
468 | //
469 | // return y;
470 | //}
471 |
472 | //// cubic interpolation of 4 equally spaced data points
473 | //// mu is the parametric variable between 0 to 1 and gives the interpolated value between values y1 and y2
474 | //static float cubicInterpolate(float y0, float y1, float y2, float y3, float mu)
475 | //{
476 | // float a0,a1,a2,a3,mu2;
477 | //
478 | // mu2 = mu*mu;
479 | // a0 = y3 - y2 - y0 + y1;
480 | // a1 = y0 - y1 - a0;
481 | // a2 = y2 - y0;
482 | // a3 = y1;
483 | //
484 | // return (a0*mu*mu2 + a1*mu2 + a2*mu + a3);
485 | //}
486 |
487 | #endif // Functions_h
488 |
--------------------------------------------------------------------------------
/GLUT.h:
--------------------------------------------------------------------------------
1 | //
2 | // GLUT.h
3 | //
4 | // Created by Andrew Barker on 9/20/16.
5 | //
6 | //
7 |
8 | #ifndef GLUT_h
9 | #define GLUT_h
10 |
11 | //#ifdef __APPLE__
12 | // #include
13 | //#else // windows or linux
14 | // #define FREEGLUT_STATIC 1
15 | // #include
16 | //#endif
17 |
18 | //#include
19 | #include "Points.h"
20 |
21 | // screw linking to other libraries when doing cross-platform dev...
22 | namespace glpp {
23 |
24 | static void lookAt (const PointXYZ& eye,
25 | const PointXYZ& lookAt,
26 | const PointXYZ& up)
27 | {
28 | cauto forward = normalized((lookAt - eye));
29 | cauto right = normalized(crossProduct(forward, up));
30 | cauto nUp = crossProduct(right, forward);
31 |
32 | GLfloat m[] = {
33 | right.x, nUp.x, -forward.x, 0,
34 | right.y, nUp.y, -forward.y, 0,
35 | right.z, nUp.z, -forward.z, 0,
36 | 0, 0, 0, 1
37 | };
38 |
39 | glMultMatrixf(m);
40 | glTranslatef(-eye.x, -eye.y, -eye.z);
41 | }
42 |
43 | // Replaces gluPerspective. Sets the frustum to perspective mode.
44 | // fovY - Field of vision in degrees in the y direction
45 | // aspect - Aspect ratio of the viewport (w/h)
46 | // zNear - The near clipping distance
47 | // zFar - The far clipping distance
48 | static void perspective (const GLdouble fovY, const GLdouble aspect,
49 | const GLdouble zNear, const GLdouble zFar)
50 | {
51 | //const GLdouble pi = 3.1415926535897932384626433832795;
52 | cauto fH = std::tan(0.5 * toRadians(fovY)) * zNear;
53 | cauto fW = fH * aspect;
54 | glFrustum(-fW, fW, -fH, fH, zNear, zFar);
55 | }
56 |
57 | static void pickMatrix(const GLdouble x, const GLdouble y,
58 | const GLdouble width, const GLdouble height,
59 | const GLint viewport[4])
60 | {
61 | GLfloat m[16];
62 | GLfloat sx, sy;
63 | GLfloat tx, ty;
64 |
65 | sx = viewport[2] / width;
66 | sy = viewport[3] / height;
67 | tx = (viewport[2] + 2.0 * (viewport[0] - x)) / width;
68 | ty = (viewport[3] + 2.0 * (viewport[1] - y)) / height;
69 |
70 | #define M(row, col) m[col*4+row]
71 | M(0, 0) = sx;
72 | M(0, 1) = 0.0;
73 | M(0, 2) = 0.0;
74 | M(0, 3) = tx;
75 | M(1, 0) = 0.0;
76 | M(1, 1) = sy;
77 | M(1, 2) = 0.0;
78 | M(1, 3) = ty;
79 | M(2, 0) = 0.0;
80 | M(2, 1) = 0.0;
81 | M(2, 2) = 1.0;
82 | M(2, 3) = 0.0;
83 | M(3, 0) = 0.0;
84 | M(3, 1) = 0.0;
85 | M(3, 2) = 0.0;
86 | M(3, 3) = 1.0;
87 | #undef M
88 |
89 | glMultMatrixf(m);
90 | }
91 |
92 | // thank you datenwolf! http://stackoverflow.com/questions/5988686/creating-a-3d-sphere-in-opengl-using-visual-c
93 | class SolidSphere
94 | {
95 | private:
96 | std::vector vertices;
97 | std::vector normals;
98 | std::vector texcoords;
99 | std::vector indices;
100 |
101 | public:
102 | SolidSphere(float radius, unsigned int sectors, unsigned int rings)
103 | {
104 | cauto R = 1.0f / (float)(rings-1);
105 | cauto S = 1.0f / (float)(sectors-1);
106 | int r, s;
107 |
108 | vertices.resize(rings * sectors * 3);
109 | normals.resize(rings * sectors * 3);
110 | texcoords.resize(rings * sectors * 2);
111 | auto v = vertices.begin();
112 | auto n = normals.begin();
113 | auto t = texcoords.begin();
114 | for (r = 0; r < rings; r++) {
115 | for (s = 0; s < sectors; s++) {
116 | cfloat y = std::sin(-M_PI_2 + M_PI * r * R);
117 | cfloat x = std::cos(2*M_PI * s * S) * std::sin(M_PI * r * R);
118 | cfloat z = std::sin(2*M_PI * s * S) * std::sin(M_PI * r * R);
119 |
120 | *t++ = s * S;
121 | *t++ = r * R;
122 |
123 | *v++ = x * radius;
124 | *v++ = y * radius;
125 | *v++ = z * radius;
126 |
127 | *n++ = x;
128 | *n++ = y;
129 | *n++ = z;
130 | }
131 | }
132 |
133 | indices.resize(rings * sectors * 4);
134 | auto i = indices.begin();
135 | for (r = 0; r < rings-1; r++) {
136 | for (s = 0; s < sectors-1; s++) {
137 | *i++ = r * sectors + s;
138 | *i++ = r * sectors + (s+1);
139 | *i++ = (r+1) * sectors + (s+1);
140 | *i++ = (r+1) * sectors + s;
141 | }
142 | }
143 | }
144 |
145 | void draw(GLfloat x, GLfloat y, GLfloat z) const
146 | {
147 | glMatrixMode(GL_MODELVIEW);
148 | glPushMatrix();
149 | glTranslatef(x, y, z);
150 |
151 | glEnableClientState(GL_VERTEX_ARRAY);
152 | glEnableClientState(GL_NORMAL_ARRAY);
153 | glEnableClientState(GL_TEXTURE_COORD_ARRAY);
154 |
155 | glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
156 | glNormalPointer(GL_FLOAT, 0, &normals[0]);
157 | glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
158 | glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
159 |
160 | glPopMatrix();
161 | }
162 | };
163 |
164 | //template
165 | //void perspective(const T fovy, const T aspect, const T zNear, const T zFar)
166 | //{
167 | // assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0));
168 | //
169 | // const T tanHalfFovy = std::tan(fovy / static_cast(2));
170 | //
171 | // //tmat4x4 Result(static_cast(0));
172 | // GLfloat m[4][4] = {0};
173 | // m[0][0] = static_cast(1) / (aspect * tanHalfFovy);
174 | // m[1][1] = static_cast(1) / (tanHalfFovy);
175 | // m[2][3] = - static_cast(1);
176 | //
177 | ////# if GLM_DEPTH_CLIP_SPACE == GLM_DEPTH_ZERO_TO_ONE
178 | //// Result[2][2] = zFar / (zNear - zFar);
179 | //// Result[3][2] = -(zFar * zNear) / (zFar - zNear);
180 | ////# else
181 | // m[2][2] = - (zFar + zNear) / (zFar - zNear);
182 | // m[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear);
183 | ////# endif
184 | //// return Result;
185 | // glMultMatrixf(&m[0][0]);
186 | //}
187 |
188 | }
189 | #endif /* GLUT_h */
190 |
--------------------------------------------------------------------------------
/History.h:
--------------------------------------------------------------------------------
1 | //
2 | // History.h
3 | //
4 | // Created by Andrew Barker on 8/25/16.
5 | //
6 | //
7 |
8 | #ifndef History_h
9 | #define History_h
10 |
11 | #include
12 | #include
13 |
14 | // this class keeps track of a history of undo/redoable states. it also has timer so that you can group smaller transactions into a single one based on the timeout length
15 | template
16 | class History {
17 |
18 | private:
19 | std::vector history;
20 | int current = 0;
21 | float undoTimer = 0; // in sec
22 | float timeoutLength = 3; // in sec
23 |
24 | public:
25 | void pushBack(const State& state)
26 | {
27 | int i = 1;
28 | if (history.size() > 0 && history[current] == state)
29 | --i; // overwrite the current history with the state passed in
30 | if (current + i < history.size())
31 | history.erase(history.begin() + current + i, history.end());
32 | history.emplace_back(state);
33 | current = history.size() - 1;
34 | }
35 |
36 | const State* const undo() noexcept
37 | {
38 | if (history.size() > 0) {
39 | current = std::max(0, current - 1);
40 | return &history[current];
41 | } else
42 | return nullptr;
43 | }
44 |
45 | const State* const redo() noexcept
46 | {
47 | if (history.size() > 0) {
48 | current = std::min((int)history.size() - 1, current + 1);
49 | return &history[current];
50 | } else
51 | return nullptr;
52 | }
53 |
54 | void clear()
55 | {
56 | current = 0;
57 | history.clear();
58 | }
59 |
60 | const State* const getCurrent() const noexcept
61 | {
62 | if (history.size() > 0)
63 | return &history[current];
64 | else
65 | return nullptr;
66 | }
67 |
68 | void resetTimer(const float newTimeoutLength = -1) noexcept
69 | {
70 | undoTimer = 0;
71 | if (newTimeoutLength > 0)
72 | timeoutLength = newTimeoutLength;
73 | }
74 |
75 | void advanceTimer(const float timeToAdvance) noexcept
76 | {
77 | if (! timerExpired())
78 | undoTimer += timeToAdvance;
79 | }
80 |
81 | bool timerExpired() const noexcept
82 | {
83 | return undoTimer >= timeoutLength;
84 | }
85 |
86 | int getSize() const noexcept
87 | {
88 | return history.size();
89 | }
90 | };
91 |
92 | #endif /* History_h */
93 |
--------------------------------------------------------------------------------
/ImageEffect.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // ImageEffect.cpp
3 | //
4 | // Created by Andrew Barker on 5/16/16.
5 | //
6 | //
7 |
8 | #include "ImageEffect.h"
9 |
10 |
11 | MyGlowEffect::MyGlowEffect (Colour color,
12 | float radius,
13 | float originalAlpha) noexcept
14 | : color(color), radius(radius), originalAlpha(originalAlpha)
15 | {}
16 |
17 | void MyGlowEffect::applyEffect (Image& sourceImage,
18 | Graphics& destContext,
19 | const float scaleFactor,
20 | const float alpha) const
21 | {
22 | Image temp (sourceImage.getFormat(), sourceImage.getWidth(), sourceImage.getHeight(), true);
23 |
24 | ImageConvolutionKernel blurKernel (roundToInt (radius * scaleFactor * 2.0f));
25 |
26 | blurKernel.createGaussianBlur (radius);
27 | blurKernel.rescaleAllValues (radius);
28 |
29 | blurKernel.applyToImage (temp, sourceImage, sourceImage.getBounds());
30 |
31 | // need to duplicate source image in order to redraw it on top of the glow b/c the Graphics context might be set to draw onto the source image
32 | Image source = sourceImage;
33 | source.duplicateIfShared();
34 |
35 | destContext.setColour (color.withMultipliedAlpha (alpha));
36 | destContext.drawImageAt (temp, 0, 0, true);
37 |
38 | destContext.setOpacity (alpha * originalAlpha);
39 | destContext.drawImageAt (source, 0, 0);
40 | }
41 |
42 | std::unique_ptr MyGlowEffect::blendedTo (const MyImageEffectFilter* end,
43 | const float alpha) const
44 | {
45 | if (end == nullptr) {
46 | return std::make_unique
47 | (color.withMultipliedAlpha(1-alpha), radius * (1-alpha), originalAlpha);
48 | } else {
49 | auto temp = dynamic_cast(end);
50 | return std::make_unique
51 | (color.interpolatedWith(temp->color, alpha),
52 | radius + alpha * (temp->radius - radius),
53 | originalAlpha + alpha * (temp->originalAlpha - originalAlpha));
54 | }
55 | }
56 |
57 | bool MyGlowEffect::operator == (const MyImageEffectFilter& other) const noexcept
58 | {
59 | if (getType() == (&other)->getType()) {
60 | auto x = dynamic_cast(other);
61 | return color == x.color && radius == x.radius && originalAlpha == x.originalAlpha;
62 | }
63 | return false;
64 | }
65 |
66 | bool MyGlowEffect::operator != (const MyImageEffectFilter& other) const noexcept
67 | {
68 | if (getType() == (&other)->getType()) {
69 | auto x = dynamic_cast(other);
70 | return color != x.color || radius != x.radius || originalAlpha != x.originalAlpha;
71 | }
72 | return false;
73 | }
74 |
75 | MyImageEffectFilterType MyGlowEffect::getType() const noexcept
76 | {
77 | return MyImageEffectFilterType::GLOW_EFFECT;
78 | }
79 |
80 |
81 |
82 | std::vector blend (const std::vector& begin,
83 | const std::vector& end,
84 | const float alpha)
85 | {
86 | std::vector blendeds;// (std::max(begin.size(), end.size()));
87 | blendeds.reserve(std::max(begin.size(), end.size()));
88 | int i = 0;
89 | for (; i < std::min(begin.size(), end.size()); ++i)
90 | blendeds.emplace_back(begin[i].blendedTo(end[i], alpha));
91 | if (begin.size() > end.size()) {
92 | for (; i < begin.size(); ++i)
93 | blendeds.emplace_back(begin[i].blendedTo(ImageEffect(), alpha));
94 | } else {
95 | for (; i < end.size(); ++i)
96 | blendeds.emplace_back(end[i].blendedTo(ImageEffect(), 1-alpha));
97 | }
98 | return blendeds;
99 | }
100 |
--------------------------------------------------------------------------------
/ImageEffect.h:
--------------------------------------------------------------------------------
1 | //
2 | // ImageEffect.h
3 | //
4 | // Created by Andrew Barker on 5/16/16.
5 | //
6 | //
7 |
8 | #ifndef ImageEffect_h
9 | #define ImageEffect_h
10 |
11 | #include "../JuceLibraryCode/JuceHeader.h"
12 | #include
13 |
14 | enum class MyImageEffectFilterType { GLOW_EFFECT };
15 |
16 | class MyImageEffectFilter
17 | {
18 | public:
19 | virtual void applyEffect (Image& sourceImage,
20 | Graphics& destContext,
21 | float scaleFactor,
22 | float alpha) const = 0;
23 |
24 | virtual std::unique_ptr blendedTo (const MyImageEffectFilter* end,
25 | float alpha) const = 0;
26 |
27 | virtual ~MyImageEffectFilter() = default;
28 |
29 | virtual bool operator == (const MyImageEffectFilter& other) const noexcept = 0;
30 | virtual bool operator != (const MyImageEffectFilter& other) const noexcept = 0;
31 | virtual MyImageEffectFilterType getType() const noexcept = 0;
32 | };
33 |
34 | class MyGlowEffect : public MyImageEffectFilter
35 | {
36 | public:
37 | MyGlowEffect (Colour color = Colours::white,
38 | float radius = 2,
39 | float originalAlpha = 1) noexcept;
40 |
41 | void applyEffect (Image& sourceImage,
42 | Graphics& destContext,
43 | float scaleFactor,
44 | float alpha) const override;
45 |
46 | std::unique_ptr blendedTo (const MyImageEffectFilter* end,
47 | float alpha) const override;
48 |
49 | bool operator == (const MyImageEffectFilter& other) const noexcept override;
50 | bool operator != (const MyImageEffectFilter& other) const noexcept override;
51 | MyImageEffectFilterType getType() const noexcept override;
52 |
53 | Colour color;
54 | float radius;
55 | float originalAlpha;
56 | };
57 |
58 | class ImageEffect
59 | {
60 | public:
61 | ImageEffect() : self_(nullptr) {}
62 |
63 | template
64 | ImageEffect(T x) : self_(std::make_shared>(std::move(x))) {}
65 |
66 | void apply(Image& source, Graphics& dest, float scale, float alpha) const
67 | { if (self_) self_->applyEffect(source, dest, scale, alpha); }
68 |
69 | ImageEffect blendedTo(const ImageEffect& end, float alpha) const
70 | { return self_ ? self_->blendedTo(end.self_.get(), alpha) : ImageEffect(); }
71 |
72 | bool operator == (const ImageEffect& other) const noexcept
73 | { return *self_ == *other.self_; }
74 |
75 | bool operator != (const ImageEffect& other) const noexcept
76 | { return *self_ != *other.self_; }
77 |
78 | private:
79 | ImageEffect(std::unique_ptr&& x) : self_(std::move(x)) {}
80 |
81 | template
82 | struct model : MyImageEffectFilter {
83 | model(T x) : data_(std::move(x)) {}
84 |
85 | void applyEffect(Image& source, Graphics& dest, float scale, float alpha) const override
86 | { (&data_)->applyEffect(source, dest, scale, alpha); }
87 |
88 | std::unique_ptr blendedTo(const MyImageEffectFilter* end, float alpha) const override
89 | { return (&data_)->blendedTo(end ? &dynamic_cast*>(end)->data_ : nullptr, alpha); }
90 |
91 | bool operator == (const MyImageEffectFilter& other) const noexcept override
92 | { return (&data_)->operator==(other); }
93 |
94 | bool operator != (const MyImageEffectFilter& other) const noexcept override
95 | { return (&data_)->operator!=(other); }
96 |
97 | MyImageEffectFilterType getType() const noexcept override
98 | { return (&data_)->getType(); }
99 |
100 | T data_;
101 | };
102 |
103 | std::shared_ptr self_;
104 | };
105 |
106 | // the two effects vectors better have the same ordering of ImageEffect subclasses for the effects that both share, otherwise not good stuff may happen
107 | std::vector blend (const std::vector& begin,
108 | const std::vector& end,
109 | const float alpha);
110 |
111 | #endif /* ImageEffect_h */
112 |
--------------------------------------------------------------------------------
/Multi.h:
--------------------------------------------------------------------------------
1 | //
2 | // Multi.h
3 | //
4 | // Created by Andrew Barker on 4/15/16.
5 | //
6 | //
7 |
8 | #ifndef Multi_h
9 | #define Multi_h
10 |
11 | #include
12 |
13 | // holds multiple, uniquely-IDed things (like a std::map, but underlying container is a vector)
14 | template
15 | class Multi
16 | {
17 | private:
18 | // can't use POD's with inheritance
19 | // class IDedThing : public Thing
20 | // {
21 | // public:
22 | // template
23 | // IDedThing(std::size_t idNumber, Args&&... args) : Thing(std::forward(args)...), idNum(idNumber) {}
24 | // Thing thing;
25 | // std::size_t idNum = 0;
26 | // };
27 | class IDedThing
28 | {
29 | public:
30 | //IDedThing(std::size_t idNumber, Thing thing) : thing(thing), idNum(idNumber) {}
31 | template
32 | IDedThing(const std::size_t idNumber, Args&&... args) : thing(std::forward(args)...), idNum(idNumber) {}
33 | Thing thing;
34 | std::size_t idNum = 0;
35 | };
36 | std::vector things;
37 |
38 | public:
39 | Thing* get(const std::size_t idNumber) noexcept
40 | {
41 | auto index = std::find_if(std::begin(things), std::end(things),
42 | [&](const auto& thing){return thing.idNum == idNumber;})
43 | - std::begin(things);
44 | if (index < things.size())
45 | return &things[index].thing;
46 | else
47 | return nullptr;
48 | }
49 |
50 | const Thing* get(const std::size_t idNumber) const noexcept
51 | {
52 | return get(idNumber);
53 | }
54 |
55 | std::vector& getVector() noexcept
56 | {
57 | // std::vector justThings;
58 | // justThings.reserve(things.size());
59 | // for (const auto& thing : things)
60 | // justThings.emplace_back(thing);
61 | // return justThings;
62 | return things;
63 | }
64 |
65 | const std::vector& getVector() const noexcept
66 | {
67 | return things;
68 | }
69 |
70 | template
71 | void add(const std::size_t idNumber, Args&&... args)
72 | {
73 | remove(idNumber);
74 | things.emplace_back(idNumber, std::forward(args)...);
75 | }
76 |
77 | void remove(const std::size_t idNumber)
78 | {
79 | things.erase(std::remove_if(std::begin(things), std::end(things),
80 | [&](const auto& thing){return thing.idNum == idNumber;}),
81 | std::end(things));
82 | }
83 |
84 | void clear() noexcept
85 | {
86 | things.clear();
87 | }
88 |
89 | std::size_t size()
90 | {
91 | return things.size();
92 | }
93 | };
94 |
95 | #endif /* Multi_h */
96 |
--------------------------------------------------------------------------------
/NonlinearInterpolator.h:
--------------------------------------------------------------------------------
1 | //
2 | // NonlinearInterpolator.h
3 | //
4 | // Created by Andrew Barker on 9/8/16.
5 | //
6 | //
7 |
8 | #ifndef NonlinearInterpolator_h
9 | #define NonlinearInterpolator_h
10 |
11 | #include
12 |
13 | template
14 | class NonlinearInterpolator
15 | {
16 | public:
17 | NonlinearInterpolator() {}
18 | virtual ~NonlinearInterpolator() {}
19 | virtual NonlinearInterpolator* clone() const = 0;
20 | // given a linear value between min and max, return a nonlinearly interpolated value between min and max
21 | virtual FloatingPointType getValue(FloatingPointType linearValue,
22 | FloatingPointType min,
23 | FloatingPointType max) const = 0;
24 | virtual FloatingPointType getInverseValue(FloatingPointType linearValue,
25 | FloatingPointType min,
26 | FloatingPointType max) const = 0;
27 | };
28 |
29 | template
30 | class LogarithmicInterpolator : public NonlinearInterpolator
31 | {
32 | public:
33 | LogarithmicInterpolator(const FloatingPointType base = 2) noexcept : base(base) {}
34 |
35 | NonlinearInterpolator* clone() const override
36 | {
37 | return new LogarithmicInterpolator(*this);
38 | }
39 |
40 | FloatingPointType getValue(const FloatingPointType linearValue,
41 | const FloatingPointType min,
42 | const FloatingPointType max) const noexcept override
43 | {
44 | const auto range = max - min;
45 | // if (base > 1)
46 | const auto normalizedValue = (linearValue - min) / range * (base - 1) + 1; // [1, base]
47 | return min + std::log(normalizedValue) / std::log(base) * range; // [min, max]
48 | }
49 |
50 | FloatingPointType getInverseValue(const FloatingPointType linearValue,
51 | const FloatingPointType min,
52 | const FloatingPointType max) const noexcept override
53 | {
54 | const auto range = max - min;
55 | const auto normalizedValue = (linearValue - min) / range; // [0, 1]
56 | return min + (std::pow(base, normalizedValue) - 1) / (base - 1) * range; // [min, max]
57 | }
58 |
59 | private:
60 | FloatingPointType base;
61 | };
62 | #endif /* NonlinearInterpolator_h */
--------------------------------------------------------------------------------
/OpenGL.h:
--------------------------------------------------------------------------------
1 | //
2 | // OpenGL.h
3 | //
4 | // Created by Andrew Barker on 9/19/16.
5 | //
6 | //
7 |
8 | #ifndef OpenGL_h
9 | #define OpenGL_h
10 |
11 | #ifdef __APPLE__
12 | #include
13 | #else // windows or linux
14 | #include
15 | #endif
16 |
17 | //#include "../JuceLibraryCode/JuceHeader.h"
18 | //
19 | //void glColour(const Colour& color)
20 | //{
21 | // glColor4f(color.getFloatRed(), color.getFloatGreen(), color.getFloatBlue(), color.getFloatAlpha());
22 | //}
23 |
24 | #endif /* OpenGL_h */
25 |
--------------------------------------------------------------------------------
/OpenGLWindow.h:
--------------------------------------------------------------------------------
1 | //
2 | // OpenGLWindow.h
3 | //
4 | // Created by Andrew Barker on 9/20/16.
5 | //
6 | //
7 |
8 | #ifndef OpenGLWindow_h
9 | #define OpenGLWindow_h
10 |
11 | #include "../JuceLibraryCode/JuceHeader.h"
12 |
13 | class OpenGLWindow
14 | {
15 | public:
16 | OpenGLWindow(OpenGLContext* context) noexcept : context(context) {}
17 | void checkResized(int w, int h) noexcept
18 | {
19 | if (w != width || h != height) {
20 | resized = true;
21 | width = w;
22 | height = h;
23 | }
24 | }
25 | void saveResized() noexcept
26 | {
27 | resized = false;
28 | }
29 | float getAspect() const noexcept
30 | {
31 | return ((float)height) / width;
32 | }
33 | OpenGLContext* context = nullptr;
34 | int width = 0, height = 0;
35 | float frameRate = 30; // lets be artsy and get that "film" look, haha
36 | bool resized = true;
37 | };
38 |
39 | #endif /* OpenGLWindow_h */
40 |
--------------------------------------------------------------------------------
/PluginProcessor.h:
--------------------------------------------------------------------------------
1 | //
2 | // PluginProcessor.h
3 | //
4 | // Created by Andrew Barker on 4/26/14.
5 | //
6 | //
7 | /*
8 | 3DAudio: simulates surround sound audio for headphones
9 | Copyright (C) 2016 Andrew Barker
10 |
11 | This program is free software: you can redistribute it and/or modify
12 | it under the terms of the GNU General Public License as published by
13 | the Free Software Foundation, either version 3 of the License, or
14 | (at your option) any later version.
15 |
16 | This program is distributed in the hope that it will be useful,
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | GNU General Public License for more details.
20 |
21 | You should have received a copy of the GNU General Public License
22 | along with this program. If not, see .
23 |
24 | The author can be contacted via email at andrew.barker.12345@gmail.com.
25 | */
26 |
27 | #ifndef __PluginProcessor__
28 | #define __PluginProcessor__
29 |
30 | // uncomment to build demo version instead of full version
31 | //#define DEMO 1
32 |
33 | #include "../JuceLibraryCode/JuceHeader.h"
34 | #include "DrewLib.h"
35 |
36 | #include "SoundSource.h"
37 | #include "Resampler.h"
38 | #include "ConcurrentResource.h"
39 |
40 | // keeps track of the number of plugin instances so we can only use one copy of the HRIR data
41 | static int numRefs = 0;
42 | // possible states for GUI display
43 | enum class DisplayState { MAIN, PATH_AUTOMATION, SETTINGS, NUM_DISPLAY_STATES };
44 | // realtime is lightest on cpu and will not glitch, offline is expensive on cpu and may glitch, auto-detect assumes the processing mode from the host
45 | enum class ProcessingMode { REALTIME, OFFLINE, AUTO_DETECT };
46 | // max number of sound sources
47 | static constexpr auto maxNumSources = 8;
48 | // making life easier
49 | using Sources = std::vector;
50 | using Locker = std::lock_guard;
51 |
52 | class ThreeDAudioProcessor : public AudioProcessor, public UndoManager
53 | #ifdef DEMO // demo version only
54 | , public Timer
55 | #endif
56 | {
57 | public:
58 | ThreeDAudioProcessor();
59 | ~ThreeDAudioProcessor();
60 | #ifdef DEMO // demo version only
61 | void timerCallback() override;
62 | #endif
63 | // the methods to for JUCE's AudioProcessor interface
64 | void prepareToPlay (double sampleRate, int samplesPerBlock) override;
65 | void releaseResources() override;
66 | void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override;
67 | AudioProcessorEditor* createEditor() override;
68 | bool hasEditor() const override;
69 | const String getName() const override;
70 | // no need to override the ones in AudioProcessor
71 | // int getNumParameters();
72 | // float getParameter (int index);
73 | // void setParameter (int index, float newValue);
74 | // const String getParameterName (int index);
75 | // const String getParameterText (int index);
76 | const String getInputChannelName (int channelIndex) const override;
77 | const String getOutputChannelName (int channelIndex) const override;
78 | bool isInputChannelStereoPair (int index) const override;
79 | bool isOutputChannelStereoPair (int index) const override;
80 | bool acceptsMidi() const override;
81 | bool producesMidi() const override;
82 | bool silenceInProducesSilenceOut() const override;
83 | double getTailLengthSeconds() const override;
84 | int getNumPrograms() override;
85 | int getCurrentProgram() override;
86 | void setCurrentProgram (int index) override;
87 | const String getProgramName (int index) override;
88 | void changeProgramName (int index, const String& newName) override;
89 | void getStateInformation (MemoryBlock& destData) override;
90 | void setStateInformation (const void* data, int sizeInBytes) override;
91 | // methods for source interaction
92 | void saveCurrentState(int beforeOrAfter);
93 | void getSourcePosXYZ(int sourceIndex, float (&xyz)[3]) const;
94 | bool addSourceAtXYZ(const float (&xyz)[3]);
95 | void copySelectedSources();
96 | bool getSourceSelected(std::size_t sourceIndex) const;
97 | void setSourceSelected(int sourceIndex, bool newSelectedState);
98 | void selectAllSources(bool newSelectedState);
99 | void deleteSelectedSources();
100 | void toggleLockSourcesToPaths();
101 | bool getLockSourcesToPaths() const;
102 | void toggleDoppler();
103 | int moveSelectedSourcesXYZ(float dx, float dy, float dz, bool moveSource = false);
104 | int moveSelectedSourcesRAE(float dr, float da, float de, bool moveSource = false);
105 | void toggleSelectedSourcesPathType();
106 | Array* getSources();
107 | //void setSources(const Lockable& newSources);
108 | void setSources(const Sources& newSources);
109 | // path point interaction
110 | void dropPathPoint();
111 | bool dropPathPoint(const float (&xyz)[3]);
112 | void togglePathPointSelected(int sourceIndex, int ptIndex);
113 | void setPathPointSelectedState(int sourceIndex, int ptIndex, bool newSelectedState);
114 | //void markPathAsUpdated(int sourceIndex);
115 | std::vector> getPathPoints(int sourceIndex) const;
116 | std::vector getPathPointsSelected(int sourceIndex) const;
117 | bool getPathPointSelected(std::size_t sourceIndex,
118 | std::size_t pathPointIndex) const;
119 | void setSelectedPathPointIndecies(int sourceIndex,
120 | int pathPointIndex,
121 | int newIndex);
122 | // path automation point interaction
123 | void togglePathAutomationPointSelected(int sourceIndex, int ptIndex);
124 | void selectAllPathAutomationView(bool newSelectedState);
125 | void setPathAutomationPointSelectedState(int sourceIndex, int ptIndex, bool newSelectedState);
126 | void deselectAllPathAutomationPoints();
127 | int moveSelectedPathAutomationPoints(float dx, float dy);
128 | //std::vector moveSelectedPathAutomationPointsWithReorderingInfo(float dx, float dy, int sourceIndexOfInterest);
129 | void moveSelectedPathAutomationPointsTo(int referencePtSourceIndex,
130 | int& referencePtIndex,
131 | int referencePtIndexAmongSelecteds,
132 | float x, float y);
133 | void addPathAutomationPtAtXY(const float (&xy)[2]);
134 | void deleteSelectedAutomationPoints();
135 | void setSelectedPathAutomationPointsSegmentType(int newSegType);
136 | //void markPathPosAsUpdated(int sourceIndex);
137 | void copySelectedPathAutomationPoints();
138 | std::vector> getPathAutomationPoints(int sourceIndex) const;
139 | //std::vector> getSelectedPathAutomationPoints(int sourceIndex);
140 | //std::vector getPathAutomationPointsSelected(int sourceIndex);
141 | int getPathAutomationPointIndexAmongSelectedPoints(int sourceIndex, int pointIndex) const;
142 | bool areAnySelectedSourcesPathAutomationPointsSelected() const;
143 | void makeSourcesVisibleForPathAutomationView();
144 | // resets the playing state if processBlock() has not been called in a while, needed because of the logic for moving selected sources
145 | void resetPlaying(float frameRate) noexcept;
146 | // for looping
147 | void toggleLooping(float defaultBegin, float defaultEnd);
148 | void defineLoopingRegionUsingSelectedPathAutomationPoints();
149 |
150 | std::atomic presetJustLoaded {true}; // to communicate to the editor when preset is loaded, editor resets to false after doing what it needs
151 | std::atomic loopRegionBegin {-1};
152 | std::atomic loopRegionEnd {-1};
153 | std::atomic loopingEnabled {false};
154 | // for the doppler effect
155 | void setSpeedOfSound(float newSpeedOfSound);
156 | bool dopplerOn = false;
157 | float speedOfSound = defaultSpeedOfSound;
158 | float maxSpeedOfSound = 500.0f;
159 | float minSpeedOfSound = 0.1f;
160 | // plugin window size
161 | int lastUIWidth = 700;
162 | int lastUIHeight = 600;
163 | // current time position, buffer size, sample rate, bpm, and time signature
164 | std::atomic posSEC {0};
165 | std::atomic N;
166 | std::atomic fs;
167 | std::atomic bpm {120};
168 | std::atomic timeSigNum {4};
169 | std::atomic timeSigDen {4};
170 | std::string getCurrentTimeString(int opt) const;
171 | std::tuple getMeasuresBeatsFrac(float sec) const;
172 | // eye position
173 | float upDir = 1.0f; // y component of eyeUp
174 | float eyePos[3]; // x,y,z
175 | float eyeUp[3] = {0.0f, 1.0f, 0.0f};
176 | float eyeRad = 3.3f;
177 | float eyeAzi = 9*M_PI/8;
178 | float eyeEle = M_PI/2.2f;
179 | // layout for the path automation view
180 | float automationViewWidth = 60.0f;
181 | float automationViewOffset = automationViewWidth/2.0f;
182 | // which view is displayed in the plugin window
183 | std::atomic displayState {DisplayState::MAIN};
184 | // determines audio rendering quality and realtime processing performance
185 | void setProcessingMode(ProcessingMode newMode) noexcept;
186 | std::atomic processingMode {ProcessingMode::AUTO_DETECT};
187 | std::atomic realTime {true};
188 | std::atomic isHostRealTime {false};
189 | // show the controls for that view
190 | //bool showHelp = false;
191 | // for letting the GL know when its display lists for drawing the path and pathPos interps for each source are updated
192 | std::atomic pathChanged {false};
193 | std::atomic pathPosChanged {false};
194 | //std::array, maxNumSources> pathChangeds;
195 | //std::array, maxNumSources> pathPosChangeds;
196 | // the visual representation of sound sources along with temporary copies to support undo/redos
197 | RealtimeConcurrent sources;
198 | //AudioPlayHead::CurrentPositionInfo gPositionInfo;
199 | std::array, maxNumSources> sourcePathPositionsFromDAW; // for source position automation from DAW
200 | std::atomic wetOutputVolume {1.0f};
201 | std::atomic dryOutputVolume {0.0f};
202 | float savedMixValue = wetOutputVolume / (wetOutputVolume + dryOutputVolume);
203 |
204 | private:
205 | #ifdef DEMO // Demo version only
206 | DialogWindow::LaunchOptions buyMeWindowLauncher;
207 | DialogWindow* buyMeWindow = nullptr;
208 | #endif
209 | float prevWetOutputVolume = wetOutputVolume;
210 | float prevDryOutputVolume = dryOutputVolume;
211 | int maxBufferSizePreparedFor = -1;
212 | // version of sources that can be used to process audio, only updated in processBlock() and is therefore thread-safe to use for processing
213 | std::vector playableSources;
214 | int prevSourcesSize = 0; // see processBlock() for useage
215 | // temporary SoundSource copies to support undo/redos
216 | Sources beforeUndo;
217 | Sources currentUndo;
218 | // Lockable beforeUndo;
219 | // Lockable currentUndo;
220 | // objects for sample rate conversion
221 | Resampler resampler;
222 | Resampler unsamplerCh1;
223 | Resampler unsamplerCh2;
224 | // previous buffer's time position from this plugin's perspective
225 | float posSECprev = 0;
226 | // prev buf time position from host's perspective
227 | float posSECPrevHost = 0;
228 | float prevBufferDuration = 0;
229 | // are we ready to process audio with sound sources?
230 | std::atomic inited {false};
231 | // during playback sources can be locked to move on their paths, or moved about freely by the user
232 | std::atomic lockSourcesToPaths {true};
233 | // are we playing back audio now?
234 | std::atomic playing {false};
235 | std::atomic resetPlayingCount {0};
236 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreeDAudioProcessor)
237 | };
238 |
239 | class EditSources : public UndoableAction
240 | {
241 | public:
242 | // EditSources(const Lockable& prevSourcesIn, const Lockable& nextSourcesIn, ThreeDAudioProcessor* ownerIn)
243 | // {
244 | // const Locker lockPrev (prevSources.getLock());
245 | // const Locker lockPrevIn (prevSourcesIn.getLock());
246 | // const Locker lockNext (nextSources.getLock());
247 | // const Locker lockNextIn (nextSourcesIn.getLock());
248 | // Sources& prev = prevSources.getResource();
249 | // for (const auto& source : prevSourcesIn.getResource())
250 | // prev.emplace_back(source);
251 | // Sources& next = nextSources.getResource();
252 | // for (const auto& source : nextSourcesIn.getResource())
253 | // next.emplace_back(source);
254 | // owner = ownerIn;
255 | // }
256 | EditSources(const Sources& prevSourcesIn, const Sources& nextSourcesIn, ThreeDAudioProcessor* ownerIn)
257 | {
258 | for (const auto& source : prevSourcesIn)
259 | prevSources.emplace_back(source);
260 | for (const auto& source : nextSourcesIn)
261 | nextSources.emplace_back(source);
262 | owner = ownerIn;
263 | }
264 | bool perform() override
265 | {
266 | owner->setSources(nextSources);
267 | return true;
268 | }
269 | bool undo() override
270 | {
271 | owner->setSources(prevSources);
272 | return true;
273 | }
274 | int getSizeInUnits() override
275 | {
276 | return 10;
277 | }
278 | UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
279 | {
280 | UndoableAction* coalescedAction = new EditSources(prevSources, ((EditSources*)nextAction)->nextSources, ((EditSources*)nextAction)->owner);
281 | return coalescedAction;
282 | }
283 | private:
284 | // Lockable prevSources;
285 | // Lockable nextSources;
286 | Sources prevSources;
287 | Sources nextSources;
288 | ThreeDAudioProcessor* owner;
289 | };
290 |
291 | #endif /* defined(__PluginProcessor__) */
292 |
--------------------------------------------------------------------------------
/Points.h:
--------------------------------------------------------------------------------
1 | //
2 | // Points.h
3 | // Represents all sorts of point types and associated convenience methods.
4 | //
5 | // Created by Andrew Barker on 11/13/16.
6 | //
7 | //
8 |
9 | #ifndef Points_h
10 | #define Points_h
11 |
12 | #include "OpenGL.h"
13 |
14 | template
15 | class PointXYZ {
16 | public:
17 | T x, y, z;
18 |
19 | PointXYZ addX (const T add) const noexcept { return {x + add, y, z}; }
20 | PointXYZ addY (const T add) const noexcept { return {x, y + add, z}; }
21 | PointXYZ addZ (const T add) const noexcept { return {x, y, z + add}; }
22 | PointXYZ subX (const T sub) const noexcept { return {x - sub, y, z}; }
23 | PointXYZ subY (const T sub) const noexcept { return {x, y - sub, z}; }
24 | PointXYZ subZ (const T sub) const noexcept { return {x, y, z - sub}; }
25 | //void normalize() noexcept {
26 |
27 | };
28 |
29 | template
30 | PointXYZ operator+ (const PointXYZ& l, const PointXYZ& r) noexcept { return {l.x + r.x, l.y + r.y, l.z + r.z}; }
31 |
32 | template
33 | PointXYZ operator- (const PointXYZ& l, const PointXYZ& r) noexcept { return {l.x - r.x, l.y - r.y, l.z - r.z}; }
34 |
35 | template