<tb@panthema.net>
<http://www.gnu.org/licenses/>
#include "WSortView.h"
#include <SDL.h>
#include <limits>
static const size_t s_samplerate = 44100;
bool g_sound_on = false;
double g_sound_sustain = 2.0;
static const size_t s_max_oscillators = 512;
class Oscillator
{
protected:
double m_freq;
size_t m_tstart, m_tend;
size_t m_duration;
public:
Oscillator(double freq, size_t tstart, size_t duration = 44100 / 8)
: m_freq(freq), m_tstart(tstart),
m_tend( m_tstart + duration ),
m_duration(duration)
{
}
static double wave_sin(double x)
{
return sin(x * 2*M_PI);
}
static double wave_sin3(double x)
{
double s = sin(x * 2*M_PI);
return s * s * s;
}
static double wave_triangle(double x)
{
x = fmod(x, 1.0);
if (x <= 0.25) return 4.0 * x;
if (x <= 0.75) return 2.0 - 4.0 * x;
return 4.0 * x - 4.0;
}
static double wave(double x)
{
return wave_triangle(x);
}
double envelope(size_t i) const
{
double x = (double)i / m_duration;
if (x > 1.0) x = 1.0;
static const double attack = 0.025;
static const double decay = 0.1;
static const double sustain = 0.9;
static const double release = 0.3;
if (x < attack)
return 1.0 / attack * x;
if (x < attack + decay)
return 1.0 - (x - attack) / decay * (1.0 - sustain);
if (x < 1.0 - release)
return sustain;
return sustain / release * (1.0 - x);
}
void mix(double* data, int size, size_t p) const
{
for (int i = 0; i < size; ++i)
{
if (p+i < m_tstart) continue;
if (p+i >= m_tend) break;
size_t trel = (p + i - m_tstart);
data[i] += envelope(trel) * wave((double)trel / s_samplerate * m_freq);
}
}
size_t tstart() const
{ return m_tstart; }
bool is_done(size_t p) const
{
return (p >= m_tend);
}
};
static std::vector<Oscillator> s_osclist;
static size_t s_pos = 0;
static void add_oscillator(double freq, size_t p, size_t pstart, size_t duration)
{
size_t oldest = 0, toldest = std::numeric_limits<size_t>::max();
for (size_t i = 0; i < s_osclist.size(); ++i)
{
if (s_osclist[i].is_done(p))
{
s_osclist[i] = Oscillator(freq, pstart, duration);
return;
}
if (s_osclist[i].tstart() < toldest) {
oldest = i;
toldest = s_osclist[i].tstart();
}
}
if (s_osclist.size() < s_max_oscillators)
{
s_osclist.push_back( Oscillator(freq, pstart, duration) );
}
else
{
s_osclist[oldest] = Oscillator(freq, pstart, duration);
}
}
static std::vector<unsigned int> s_access_list;
static wxMutex s_mutex_access_list;
void SoundAccess(size_t i)
{
if (!g_sound_on) return;
wxMutexLocker lock(s_mutex_access_list);
ASSERT(lock.IsOk());
s_access_list.push_back(i);
}
static double arrayindex_to_frequency(double aindex)
{
return 120 + 1200 * (aindex*aindex);
}
void SoundReset()
{
wxMutexLocker lock(s_mutex_access_list);
ASSERT(lock.IsOk());
s_pos = 0;
s_osclist.clear();
}
void SoundCallback(void* udata, Uint8 *stream, int len)
{
if (!g_sound_on) {
memset(stream, 0, len);
return;
}
size_t& p = s_pos;
WSortView& sv = *reinterpret_cast<WSortView*>(udata);
int16_t* data = (int16_t*)stream;
size_t size = len / sizeof(int16_t);
{
wxMutexLocker lock(s_mutex_access_list);
ASSERT(lock.IsOk());
double pscale = (double)size / s_access_list.size();
for (size_t i = 0; i < s_access_list.size(); ++i)
{
double relindex = s_access_list[i] / (double)sv.array_max();
double freq = arrayindex_to_frequency(relindex);
add_oscillator( freq, p, p + i * pscale,
g_delay / 1000.0 * g_sound_sustain * s_samplerate );
}
s_access_list.clear();
}
std::vector<double> wave(size, 0.0);
size_t wavecount = 0;
for (std::vector<Oscillator>::const_iterator it = s_osclist.begin();
it != s_osclist.end(); ++it)
{
if (!it->is_done(p))
{
it->mix(wave.data(), size, p);
++wavecount;
}
}
if (wavecount == 0)
{
memset(stream, 0, len);
}
else
{
double vol = *std::max_element(wave.begin(), wave.end());
static double oldvol = 1.0;
if (vol > oldvol) {
}
else {
vol = 0.9 * oldvol;
}
for (size_t i = 0; i < size; ++i)
{
int32_t v = 24000.0 * wave[i] / (oldvol + (vol - oldvol) * (i / (double)size));
if (v > 32200) {
v = 32200;
}
if (v < -32200) {
v = -32200;
}
data[i] = v;
}
oldvol = vol;
}
p += size;
}