+DummyAudioPort::~DummyAudioPort () {
+ free(_wavetable);
+ _wavetable = 0;
+}
+
+static std::string format_hz (float freq) {
+ std::stringstream ss;
+ if (freq >= 10000) {
+ ss << std::setprecision (1) << std::fixed << freq / 1000 << "kHz";
+ } else if (freq >= 1000) {
+ ss << std::setprecision (2) << std::fixed << freq / 1000 << "kHz";
+ } else {
+ ss << std::setprecision (1) << std::fixed << freq << "Hz";
+ }
+ return ss.str ();
+}
+
+static size_t fit_wave (float freq, float rate, float precision = 0.001) {
+ const size_t max_mult = floor (freq * rate);
+ float minErr = 2;
+ size_t fact = 1;
+ for (size_t i = 1; i < max_mult; ++i) {
+ const float isc = rate * (float)i / freq; // ideal sample count
+ const float rsc = rintf (isc); // rounded sample count
+ const float err = fabsf (isc - rsc);
+ if (err < minErr) {
+ minErr = err;
+ fact = i;
+ }
+ if (err < precision) {
+ break;
+ }
+ }
+ //printf(" FIT %8.1f Hz / %8.1f Hz * %ld = %.0f (err: %e)\n", freq, rate, fact, fact * rate / freq, minErr);
+ return fact;
+}
+
+std::string
+DummyAudioPort::setup_generator (GeneratorType const g, float const samplerate, int c, int total)
+{
+ std::string name;
+ DummyPort::setup_random_number_generator();
+ _gen_type = g;
+
+ switch (_gen_type) {
+ case PinkNoise:
+ case PonyNoise:
+ case UniformWhiteNoise:
+ case GaussianWhiteNoise:
+ case DC05:
+ case Silence:
+ break;
+ case Demolition:
+ _gen_period = 3 * samplerate;
+ break;
+ case KronekerDelta:
+ _gen_period = (5 + randi() % (int)(samplerate / 20.f));
+ name = "Delta " + format_hz (samplerate / _gen_period);
+ break;
+ case SquareWave:
+ _gen_period = (5 + randi() % (int)(samplerate / 20.f)) & ~1;
+ name = "Square " + format_hz (samplerate / _gen_period);
+ break;
+ case SineWaveOctaves:
+ {
+ const int x = c - floor (((float)total / 2));
+ float f = powf (2.f, x / 3.f) * 1000.f;
+ f = std::max (10.f, std::min (samplerate *.5f, f));
+ const size_t mult = fit_wave (f, samplerate);
+ _gen_period = rintf ((float)mult * samplerate / f);
+ name = "Sine " + format_hz (samplerate * mult / (float)_gen_period);
+ _wavetable = (Sample*) malloc (_gen_period * sizeof(Sample));
+ for (uint32_t i = 0 ; i < _gen_period; ++i) {
+ _wavetable[i] = .12589f * sinf(2.0f * M_PI * (float)mult * (float)i / (float)(_gen_period)); // -18dBFS
+ }
+ }
+ break;
+ case SineWave:
+ _gen_period = 5 + randi() % (int)(samplerate / 20.f);
+ name = "Sine " + format_hz (samplerate / _gen_period);
+ _wavetable = (Sample*) malloc (_gen_period * sizeof(Sample));
+ for (uint32_t i = 0 ; i < _gen_period; ++i) {
+ _wavetable[i] = .12589f * sinf(2.0f * M_PI * (float)i / (float)_gen_period); // -18dBFS
+ }
+ break;
+ case SquareSweep:
+ case SquareSweepSwell:
+ case SineSweep:
+ case SineSweepSwell:
+ {
+ _gen_period = 5 * samplerate + randi() % (int)(samplerate * 10.f);
+ _gen_period &= ~1;
+ _gen_perio2 = 1 | (int)ceilf (_gen_period * .89f); // Volume Swell period
+ const double f_min = 20.;
+ const double f_max = samplerate * .5;
+ const double g_p2 = _gen_period * .5;
+#ifdef LINEAR_SWEEP
+ const double b = (f_max - f_min) / (2. * samplerate * g_p2);
+ const double a = f_min / samplerate;
+#else
+ const double b = log (f_max / f_min) / g_p2;
+ const double a = f_min / (b * samplerate);
+#endif
+ const uint32_t g_p2i = rint(g_p2);
+ _wavetable = (Sample*) malloc (_gen_period * sizeof(Sample));
+ for (uint32_t i = 0 ; i < g_p2i; ++i) {
+#ifdef LINEAR_SWEEP
+ const double phase = i * (a + b * i);
+#else
+ const double phase = a * exp (b * i) - a;
+#endif
+ _wavetable[i] = (float)sin (2. * M_PI * (phase - floor (phase)));
+ }
+ for (uint32_t i = g_p2i; i < _gen_period; ++i) {
+ const uint32_t j = _gen_period - i;
+#ifdef LINEAR_SWEEP
+ const double phase = j * (a + b * j);
+#else
+ const double phase = a * exp (b * j) - a;
+#endif
+ _wavetable[i] = -(float)sin (2. * M_PI * (phase - floor (phase)));
+ }
+ if (_gen_type == SquareSweep) {
+ for (uint32_t i = 0 ; i < _gen_period; ++i) {
+ _wavetable[i] = _wavetable[i] < 0 ? -.40709f : .40709f;
+ }
+ }
+ else if (_gen_type == SquareSweepSwell) {
+ for (uint32_t i = 0 ; i < _gen_period; ++i) {
+ _wavetable[i] = _wavetable[i] < 0 ? -1 : 1;
+ }
+ }
+ }
+ break;
+ case Loopback:
+ _wavetable = (Sample*) malloc (DummyAudioBackend::max_buffer_size() * sizeof(Sample));
+ break;
+ }
+ return name;
+}
+
+void DummyAudioPort::midi_to_wavetable (DummyMidiBuffer const * const src, size_t n_samples)
+{
+ memset(_wavetable, 0, n_samples * sizeof(float));
+ /* generate an audio spike for every midi message
+ * to verify layency-compensation alignment
+ * (here: midi-out playback-latency + audio-in capture-latency)
+ */
+ for (DummyMidiBuffer::const_iterator it = src->begin (); it != src->end (); ++it) {
+ const pframes_t t = (*it)->timestamp();
+ assert(t < n_samples);
+ // somewhat arbitrary mapping for quick visual feedback
+ float v = -.5f;
+ if ((*it)->size() == 3) {
+ const unsigned char *d = (*it)->const_data();
+ if ((d[0] & 0xf0) == 0x90) { // note on
+ v = .25f + d[2] / 512.f;
+ }
+ else if ((d[0] & 0xf0) == 0x80) { // note off
+ v = .3f - d[2] / 640.f;
+ }
+ else if ((d[0] & 0xf0) == 0xb0) { // CC
+ v = -.1f - d[2] / 256.f;
+ }
+ }
+ _wavetable[t] += v;
+ }
+}
+
+float DummyAudioPort::grandf ()
+{
+ // Gaussian White Noise
+ // http://www.musicdsp.org/archive.php?classid=0#109
+ float x1, x2, r;
+
+ if (_pass) {
+ _pass = false;
+ return _rn1;
+ }
+
+ do {
+ x1 = randf ();
+ x2 = randf ();
+ r = x1 * x1 + x2 * x2;
+ } while ((r >= 1.0f) || (r < 1e-22f));
+
+ r = sqrtf (-2.f * logf (r) / r);
+
+ _pass = true;
+ _rn1 = r * x2;
+ return r * x1;
+}
+
+/* inspired by jack-demolition by Steve Harris */
+static const float _demolition[] = {
+ 0.0f, /* special case - 0dbFS white noise */
+ 0.0f, /* zero, may cause denomrals following a signal */
+ 0.73 / 1e45, /* very small - should be denormal when floated */
+ 3.7f, /* arbitrary number > 0dBFS */
+ -4.3f, /* arbitrary negative number > 0dBFS */
+ 4294967395.0f, /* 2^16 + 100 */
+ -4294967395.0f,
+ HUGE, /* Big, non-inf number */
+ INFINITY, /* +inf */
+ -INFINITY, /* -inf */
+ -NAN, /* -nan */
+ NAN, /* nan */
+ 0.0f, /* some silence to check for recovery */
+};
+
+void DummyAudioPort::generate (const pframes_t n_samples)
+{
+ Glib::Threads::Mutex::Lock lm (generator_lock);
+ if (_gen_cycle) {
+ return;
+ }
+
+ switch (_gen_type) {
+ case Silence:
+ memset (_buffer, 0, n_samples * sizeof (Sample));
+ break;
+ case DC05:
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ _buffer[i] = 0.5f;
+ }
+ break;
+ case Demolition:
+ switch (_gen_count2) {
+ case 0: // noise
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ _buffer[i] = randf();
+ }
+ break;
+ default:
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ _buffer[i] = _demolition [_gen_count2];
+ }
+ break;
+ }
+ _gen_offset += n_samples;
+ if (_gen_offset > _gen_period) {
+ _gen_offset = 0;
+ _gen_count2 = (_gen_count2 + 1) % (sizeof (_demolition) / sizeof (float));
+ }
+ break;
+ case SquareWave:
+ assert(_gen_period > 0);
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ if (_gen_offset < _gen_period * .5f) {
+ _buffer[i] = .40709f; // -6dBFS
+ } else {
+ _buffer[i] = -.40709f;
+ }
+ _gen_offset = (_gen_offset + 1) % _gen_period;
+ }
+ break;
+ case KronekerDelta:
+ assert(_gen_period > 0);
+ memset (_buffer, 0, n_samples * sizeof (Sample));
+ for (pframes_t i = 0; i < n_samples; ++i) {
+ if (_gen_offset == 0) {
+ _buffer[i] = 1.0f;
+ }
+ _gen_offset = (_gen_offset + 1) % _gen_period;
+ }
+ break;
+ case SineSweepSwell:
+ case SquareSweepSwell:
+ assert(_wavetable && _gen_period > 0);
+ {
+ const float vols = 2.f / (float)_gen_perio2;
+ for (pframes_t i = 0; i < n_samples; ++i) {
+ const float g = fabsf (_gen_count2 * vols - 1.f);
+ _buffer[i] = g * _wavetable[_gen_offset];
+ _gen_offset = (_gen_offset + 1) % _gen_period;
+ _gen_count2 = (_gen_count2 + 1) % _gen_perio2;
+ }
+ }
+ break;
+ case Loopback:
+ _gen_period = n_samples; // XXX DummyBackend::_samples_per_period;
+ case SineWave:
+ case SineWaveOctaves:
+ case SineSweep:
+ case SquareSweep:
+ assert(_wavetable && _gen_period > 0);
+ {
+ pframes_t written = 0;
+ while (written < n_samples) {
+ const uint32_t remain = n_samples - written;
+ const uint32_t to_copy = std::min(remain, _gen_period - _gen_offset);
+ memcpy((void*)&_buffer[written],
+ (void*)&_wavetable[_gen_offset],
+ to_copy * sizeof(Sample));
+ written += to_copy;
+ _gen_offset = (_gen_offset + to_copy) % _gen_period;
+ }
+ }
+ break;
+ case UniformWhiteNoise:
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ _buffer[i] = .158489f * randf();
+ }
+ break;
+ case GaussianWhiteNoise:
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ _buffer[i] = .089125f * grandf();
+ }
+ break;
+ case PinkNoise:
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ // Paul Kellet's refined method
+ // http://www.musicdsp.org/files/pink.txt
+ // NB. If 'white' consists of uniform random numbers,
+ // the pink noise will have an almost gaussian distribution.
+ const float white = .0498f * randf ();
+ _b0 = .99886f * _b0 + white * .0555179f;
+ _b1 = .99332f * _b1 + white * .0750759f;
+ _b2 = .96900f * _b2 + white * .1538520f;
+ _b3 = .86650f * _b3 + white * .3104856f;
+ _b4 = .55000f * _b4 + white * .5329522f;
+ _b5 = -.7616f * _b5 - white * .0168980f;
+ _buffer[i] = _b0 + _b1 + _b2 + _b3 + _b4 + _b5 + _b6 + white * 0.5362f;
+ _b6 = white * 0.115926f;
+ }
+ break;
+ case PonyNoise:
+ for (pframes_t i = 0 ; i < n_samples; ++i) {
+ const float white = 0.0498f * randf ();
+ // Paul Kellet's economy method
+ // http://www.musicdsp.org/files/pink.txt
+ _b0 = 0.99765f * _b0 + white * 0.0990460f;
+ _b1 = 0.96300f * _b1 + white * 0.2965164f;
+ _b2 = 0.57000f * _b2 + white * 1.0526913f;
+ _buffer[i] = _b0 + _b1 + _b2 + white * 0.1848f;
+ }
+ break;
+ }
+ _gen_cycle = true;
+}