ultimatepp/examples/Synth/Synth.cpp
cxl 376df036cd .examples
git-svn-id: svn://ultimatepp.org/upp/trunk@12698 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2019-01-22 09:33:28 +00:00

376 lines
7.7 KiB
C++

#include "Synth.h"
#include <Core/Core.h>
using namespace Upp;
enum {
WAVECOUNT = 2048,
WAVEMASK = 2047,
NOISECOUNT = 1024 * 1024 * 4,
NOISEMASK = NOISECOUNT - 1,
};
struct WaveForm {
float wave[2048];
};
float BrownNoise[NOISECOUNT];
INITBLOCK {
float p;
for(int i = 0; i < NOISECOUNT; i++) {
p = clamp(p + (0.02 * (Randomf() * 2 - 1)) / 1.02, -1/3.5, 1/3.5);
BrownNoise[i] = p;
}
}
void MakeWave(const char *s, WaveForm& h)
{
for(int i = 0; i < WAVECOUNT; i++)
h.wave[i] = 0;
try {
CParser p(s);
int harm = 1;
int i = 0;
double a = 1;
auto fn0 = [&]() -> double { return sin(M_2PI * i * harm / WAVECOUNT); };
Function<double ()> fn = fn0;
for(;;) {
int ii = (i * harm)/* & WAVEMASK*/;
if(p.Char('T'))
fn = [&]() -> double { return (1024.0 - abs(WAVECOUNT / 2 - ii)) / WAVECOUNT / 4 - 1; };
else
if(p.Char('t'))
fn = [&]() -> double { return -(1024.0 - abs(WAVECOUNT / 2 - ii)) / WAVECOUNT / 4 + 1; };
else
if(p.Char('Z'))
fn = [&]() -> double { return 2.0f * ii / (WAVECOUNT - 1) - 1; };
else
if(p.Char('z'))
fn = [&]() -> double { return -2.0f * ii / (WAVECOUNT - 1) + 1; };
else
if(p.Char('S'))
fn = fn0;
else
if(p.IsDouble()) {
double a = p.ReadDouble();
if(p.Char(':'))
harm = (int)a;
else {
for(i = 0; i < WAVECOUNT; i++)
h.wave[i] += (float)(a * fn());
harm++;
}
}
else
break;
}
}
catch(...) {}
}
float *GetWave(const String& h)
{
static Mutex _;
Mutex::Lock __(_);
static ArrayMap<String, WaveForm> cache;
int q = cache.Find(h);
if(q >= 0)
return cache[q].wave;
MakeWave(h, cache.Add(h));
return cache.Top().wave;
}
Instrument::Instrument()
{
delay = 0;
attack = 0;
decay = 0;
sustain = 1;
release = 0;
wave = "1";
mod_wave = "1";
mod_amplitude = 0;
mod_frequency = 1;
noise_kind = 0;
noise_amplitude = 1;
};
const int TABN = 4096;
static double table[TABN + 2];
double LOGVOL(double x)
{
return pow(10, x * 60 / 20) / pow(10, 60 / 20);
}
force_inline
double LogVol(double x)
{
double id = TABN * x;
int ii = (int)id;
if(ii < 0) return 0;
if(ii > TABN) return x;
double f = id - ii;
return (1 - f) * table[ii] + f * table[ii + 1];
}
INITBLOCK {
for(int i = 0; i <= TABN; i++) {
double q = i / (double)TABN;
table[i] = LOGVOL(q);
}
table[TABN + 1] = (TABN + 1) / 256.0;
table[0] = 0;
}
double MakeNoise(int kind)
{
static dword state = 1;
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
double white = 2.0 / 4294967295.0 * state - 1;
static double p;
static double b0, b1, b2;
switch(kind) {
case 1:
return white;
case 2:
b0 = 0.99765 * b0 + white * 0.0990460;
b1 = 0.96300 * b1 + white * 0.2965164;
b2 = 0.57000 * b2 + white * 1.0526913;
return b0 + b1 + b2 + white * 0.1848;
case 3:
p = clamp(p + (0.02 * (Randomf() * 2 - 1)) / 1.02, -1/3.5, 1/3.5);
return p * 3.5;
}
return 0;
}
struct SynthSound : SoundGenerator {
virtual bool Get(float *data, int len);
float volume;
float fdelta;
float sustain;
float mdelta;
float mod_amp;
int duration;
int delay;
int attack;
int decay;
int release;
float *wave;
float *mod_wave;
int noise_kind;
float noise_amplitude;
float release_from = 0;
float last = 0;
float frequency_mul;
bool has_noise;
float noise[8];
float q = 0;
float w = 0;
int t = 0;
void SetVolume(float vol) { volume = vol; }
void SetFrequency(float frequency) { fdelta = (WAVEMASK + 1) * frequency * frequency_mul / 44200; }
void Set(float volume, float frequency, float duration, const Instrument& m);
SynthSound(float volume, float frequency, float duration, const Instrument& m) {
Set(volume, frequency, duration, m);
}
};
void SynthSound::Set(float volume, float frequency, float duration_, const Instrument& m)
{
frequency_mul = m.frequency_mul;
SetVolume(volume);
SetFrequency(frequency);
sustain = m.sustain;
mdelta = (WAVEMASK + 1) * m.mod_frequency / 44200;
mod_amp = (WAVEMASK + 1) * m.mod_amplitude / 44200;
duration = int(44200 * duration_);
delay = int(44200 * m.delay);
attack = int(44200 * m.attack);
decay = int(44200 * m.decay);
release = int(44200 * m.release);
wave = GetWave(m.wave);
mod_wave = GetWave(m.mod_wave);
noise_kind = m.noise_kind;
noise_amplitude = m.noise_amplitude;
has_noise = m.has_noise;
for(int i = 0; i < 8; i++)
noise[i] = m.noise[i];
}
bool SynthSound::Get(float *b, int len)
{
bool plays = true;
float sustain_volume = sustain * volume;
for(int i = 0; i < len; i++) {
if(t < delay)
*b++ = 0;
else {
float envelope = 0;
if(t < delay + attack) {
envelope = (t * volume) / attack;
release_from = envelope;
last = t;
}
else
if(t < delay + attack + decay) {
envelope = volume - (volume - sustain_volume) * (t - delay - attack) / decay;
release_from = envelope;
last = t;
}
else
if(t < duration || duration < 0) {
envelope = sustain_volume;
release_from = envelope;
last = t;
}
else
if(release) {
envelope = release_from - release_from * (t - last) / release;
if(envelope <= 0) {
plays = false;
envelope = 0;
}
}
else
plays = false;
double a = wave[(int)q & WAVEMASK];
if(has_noise) {
a += BrownNoise[(int)q & NOISEMASK];
}
if(noise_kind)
a += noise_amplitude * MakeNoise(noise_kind);
*b++ = envelope * a;
w += mdelta;
q += fdelta + mod_amp * mod_wave[(int)w & WAVEMASK];
}
t++;
}
return plays;
}
int64 Play(float volume, float frequency, float duration, const Instrument& m)
{
return AddSound(new SynthSound(volume, frequency, duration, m));
}
int64 Play(float volume, float frequency, const Instrument& m)
{
return Play(volume, frequency, -1, m);
}
void SetVolume(int64 id, float volume)
{
AlterSound(id, [=](SoundGenerator *sg) {
SynthSound *ss = dynamic_cast<SynthSound *>(sg);
if(ss)
ss->SetVolume(volume);
});
}
void SetFrequency(int64 id, float frequency)
{
AlterSound(id, [=](SoundGenerator *sg) {
SynthSound *ss = dynamic_cast<SynthSound *>(sg);
if(ss)
ss->SetFrequency(frequency);
});
}
void StopSound(int64 id)
{
AlterSound(id, [=](SoundGenerator *sg) {
SynthSound *ss = dynamic_cast<SynthSound *>(sg);
if(ss)
ss->duration = 0;
});
}
void Instrument::Read(CParser& p)
{
while(!p.IsEof()) {
if(p.Char('{')) {
const char *s = p.GetPtr();
for(;;) {
if(p.IsChar('}') || p.IsEof()) {
wave = String(s, p.GetPtr());
break;
}
p.SkipTerm();
}
p.Char('}');
if(p.Char('@'))
frequency_mul = p.ReadDouble();
}
else
if(p.Char('[')) {
const char *s = p.GetPtr();
for(;;) {
if(p.IsChar(']') || p.IsEof()) {
mod_wave = String(s, p.GetPtr());
break;
}
p.SkipTerm();
}
p.Char(']');
for(;;) {
if(p.Char('@'))
mod_amplitude = p.ReadDouble();
else
if(p.Char('^'))
mod_frequency = p.ReadDouble();
else
break;
}
}
else
if(p.Char('<')) {
has_noise = true;
int ii = 0;
while(!p.Char('>') && ii < 8)
noise[ii++] = p.ReadDouble();
}
else
if(p.Char('a'))
attack = p.ReadDouble();
else
if(p.Char('d'))
decay = p.ReadDouble();
else
if(p.Char('s'))
sustain = p.ReadDouble();
else
if(p.Char('r'))
release = p.ReadDouble();
else
break;
}
}
void Instrument::Read(const char *s)
{
try {
CParser p(s);
Read(p);
}
catch(...) {}
}