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