1 /******************************************************************************
2 * src/SortSound.cpp
3 *
4 * This file contains the audio callback which generates all sound.
5 *
6 * The sound is created by mixing many many oscillators, whose freqencies are
7 * defined by the values compared. Each comparison adds two oscillators.
8 *
9 * In the final version the oscillators generate a triangular waveform and are
10 * enveloped using a ADSR function. The total time an oscillator yields sound
11 * is defined by the "Sound Sustain" user slider.
12 *
13 * The callback function also automatically scales the volume when many
14 * oscillators overlap. In effect, this is dynamic range compression.
15 *
16 ******************************************************************************
17 * Copyright (C) 2013-2014 Timo Bingmann <tb@panthema.net>
18 *
19 * This program is free software: you can redistribute it and/or modify it
20 * under the terms of the GNU General Public License as published by the Free
21 * Software Foundation, either version 3 of the License, or (at your option)
22 * any later version.
23 *
24 * This program is distributed in the hope that it will be useful, but WITHOUT
25 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
26 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
27 * more details.
28 *
29 * You should have received a copy of the GNU General Public License along with
30 * this program. If not, see <http://www.gnu.org/licenses/>.
31 *****************************************************************************/
32
33 #include "WSortView.h"
34 #include <SDL.h>
35 #include <limits>
36
37 // *** All time counters in the sound system are in sample units.
38
39 static const size_t s_samplerate = 44100;
40
41 /// global sound on/off switch
42 bool g_sound_on = false;
43
44 /// multiplying g_delay with this value yields the duration a sound is sustained
45 double g_sound_sustain = 2.0;
46
47 /// limit the number of oscillators to avoid overloading the callback
48 static const size_t s_max_oscillators = 512;
49
50 /// Oscillator generating sine or triangle waves
51 class Oscillator
52 {
53 protected:
54 /// frequency of generated wave
55 double m_freq;
56
57 /// start and end of wave in sample time
58 size_t m_tstart, m_tend;
59
60 /// duration of oscillation note
61 size_t m_duration;
62
63 public:
64 /// construct new oscillator
Oscillator(double freq,size_t tstart,size_t duration=44100/8)65 Oscillator(double freq, size_t tstart, size_t duration = 44100 / 8)
66 : m_freq(freq), m_tstart(tstart),
67 m_tend( m_tstart + duration ),
68 m_duration(duration)
69 {
70 }
71
72 // *** Wave Forms
73
74 /// simple sine wave
wave_sin(double x)75 static double wave_sin(double x)
76 {
77 return sin(x * 2*M_PI);
78 }
79
80 /// sin^3 wave
wave_sin3(double x)81 static double wave_sin3(double x)
82 {
83 double s = sin(x * 2*M_PI);
84 return s * s * s;
85 }
86
87 /// triangle wave
wave_triangle(double x)88 static double wave_triangle(double x)
89 {
90 x = fmod(x, 1.0);
91
92 if (x <= 0.25) return 4.0 * x;
93 if (x <= 0.75) return 2.0 - 4.0 * x;
94 return 4.0 * x - 4.0;
95 }
96
97 /// picking a waveform
wave(double x)98 static double wave(double x)
99 {
100 //return wave_sin(x);
101 //return wave_sin3(x);
102 return wave_triangle(x);
103 }
104
105 // *** Envelope
106
107 /// envelope applied to wave (uses ADSR)
envelope(size_t i) const108 double envelope(size_t i) const
109 {
110 double x = (double)i / m_duration;
111 if (x > 1.0) x = 1.0;
112
113 // simple envelope functions:
114
115 //return 1.0 - x;
116 //return cos(M_PI_2 * x);
117
118 // *** ADSR envelope
119
120 static const double attack = 0.025; // percentage of duration
121 static const double decay = 0.1; // percentage of duration
122 static const double sustain = 0.9; // percentage of amplitude
123 static const double release = 0.3; // percentage of duration
124
125 if (x < attack)
126 return 1.0 / attack * x;
127
128 if (x < attack + decay)
129 return 1.0 - (x - attack) / decay * (1.0 - sustain);
130
131 if (x < 1.0 - release)
132 return sustain;
133
134 return sustain / release * (1.0 - x);
135 }
136
137 // *** Generate Wave and Mix
138
139 /// mix in the output of this oscillator on the wave output
mix(double * data,int size,size_t p) const140 void mix(double* data, int size, size_t p) const
141 {
142 for (int i = 0; i < size; ++i)
143 {
144 if (p+i < m_tstart) continue;
145 if (p+i >= m_tend) break;
146
147 size_t trel = (p + i - m_tstart);
148
149 data[i] += envelope(trel) * wave((double)trel / s_samplerate * m_freq);
150 }
151 }
152
153 /// return start time
tstart() const154 size_t tstart() const
155 { return m_tstart; }
156
157 /// true if the oscillator is silent at time p
is_done(size_t p) const158 bool is_done(size_t p) const
159 {
160 return (p >= m_tend);
161 }
162 };
163
164 /// array of oscillators
165 static std::vector<Oscillator> s_osclist;
166
167 /// global timestamp of callback in sound sample units
168 static size_t s_pos = 0;
169
170 /// add an oscillator to the list (reuse finished ones)
add_oscillator(double freq,size_t p,size_t pstart,size_t duration)171 static void add_oscillator(double freq, size_t p, size_t pstart, size_t duration)
172 {
173 // find free oscillator
174 size_t oldest = 0, toldest = std::numeric_limits<size_t>::max();
175 for (size_t i = 0; i < s_osclist.size(); ++i)
176 {
177 if (s_osclist[i].is_done(p))
178 {
179 s_osclist[i] = Oscillator(freq, pstart, duration);
180 return;
181 }
182
183 if (s_osclist[i].tstart() < toldest) {
184 oldest = i;
185 toldest = s_osclist[i].tstart();
186 }
187 }
188
189 if (s_osclist.size() < s_max_oscillators)
190 {
191 // add new one
192 s_osclist.push_back( Oscillator(freq, pstart, duration) );
193 }
194 else
195 {
196 // replace oldest oscillator
197 s_osclist[oldest] = Oscillator(freq, pstart, duration);
198 }
199 }
200
201 /// list of array accesses since last callback
202 static std::vector<unsigned int> s_access_list;
203
204 // mutex to s_access_list
205 static wxMutex s_mutex_access_list;
206
207 /// "public" function to add a new array access
SoundAccess(size_t i)208 void SoundAccess(size_t i)
209 {
210 if (!g_sound_on) return;
211
212 wxMutexLocker lock(s_mutex_access_list);
213 ASSERT(lock.IsOk());
214
215 s_access_list.push_back(i);
216 }
217
218 /// function mapping array index (normalized to [0,1]) to frequency
arrayindex_to_frequency(double aindex)219 static double arrayindex_to_frequency(double aindex)
220 {
221 return 120 + 1200 * (aindex*aindex);
222 }
223
224 /// reset internal sound data (called from main thread)
SoundReset()225 void SoundReset()
226 {
227 wxMutexLocker lock(s_mutex_access_list);
228 ASSERT(lock.IsOk());
229
230 s_pos = 0;
231 s_osclist.clear();
232 }
233
234 /// sound generator callback run by SDL
SoundCallback(void * udata,Uint8 * stream,int len)235 void SoundCallback(void* udata, Uint8 *stream, int len)
236 {
237 if (!g_sound_on) {
238 memset(stream, 0, len);
239 return;
240 }
241
242 // current sample time (32-bit size_t wraps after 27 hours, 64-bit size_t
243 // wraps after 13 million years).
244 size_t& p = s_pos;
245
246 // reference to sortview
247 WSortView& sv = *reinterpret_cast<WSortView*>(udata);
248
249 // we use 16-bit mono output at 44.1 kHz
250 int16_t* data = (int16_t*)stream;
251 size_t size = len / sizeof(int16_t);
252
253 // fetch new access list and create oscillators
254 {
255 wxMutexLocker lock(s_mutex_access_list);
256 ASSERT(lock.IsOk());
257
258 // spread out access list over time of one callback
259 double pscale = (double)size / s_access_list.size();
260
261 for (size_t i = 0; i < s_access_list.size(); ++i)
262 {
263 double relindex = s_access_list[i] / (double)sv.m_array.array_max();
264 double freq = arrayindex_to_frequency(relindex);
265
266 add_oscillator( freq, p, p + i * pscale,
267 g_delay / 1000.0 * g_sound_sustain * s_samplerate );
268 }
269
270 s_access_list.clear();
271 }
272
273 // calculate waveform
274 std::vector<double> wave(size, 0.0);
275 size_t wavecount = 0;
276
277 for (std::vector<Oscillator>::const_iterator it = s_osclist.begin();
278 it != s_osclist.end(); ++it)
279 {
280 if (!it->is_done(p))
281 {
282 it->mix(wave.data(), size, p);
283 ++wavecount;
284 }
285 }
286
287 // scale output with number of waves mixed
288
289 if (wavecount == 0)
290 {
291 // set zero, the function below messes up when vol = 0.0
292 memset(stream, 0, len);
293 }
294 else
295 {
296 // count maximum wave amplitude
297 double vol = *std::max_element(wave.begin(), wave.end());
298
299 static double oldvol = 1.0;
300
301 if (vol > oldvol) {
302 // immediately ramp down volume
303 }
304 else {
305 // but slowly ramp up volume
306 vol = 0.9 * oldvol;
307 }
308
309 // convert waveform to samples, with ramping of volume
310
311 for (size_t i = 0; i < size; ++i)
312 {
313 int32_t v = 24000.0 * wave[i] / (oldvol + (vol - oldvol) * (i / (double)size));
314
315 if (v > 32200) {
316 //std::cout << "clip " << p << "\n";
317 v = 32200;
318 }
319 if (v < -32200) {
320 //std::cout << "clip " << p << "\n";
321 v = -32200;
322 }
323
324 data[i] = v;
325 }
326
327 oldvol = vol;
328 }
329
330 // advance sample timestamp
331 p += size;
332 }
333