From 185f06e7f7272bec71c4f4b1603296c14802fa28 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 7 Jul 2014 16:23:23 +0200 Subject: [PATCH] add signal generator modules to Dummy Backend. --- libs/backends/dummy/dummy_audiobackend.cc | 171 +++++++++++++++++++++- libs/backends/dummy/dummy_audiobackend.h | 31 ++++ 2 files changed, 196 insertions(+), 6 deletions(-) diff --git a/libs/backends/dummy/dummy_audiobackend.cc b/libs/backends/dummy/dummy_audiobackend.cc index 3a969aa479..69966f52fd 100644 --- a/libs/backends/dummy/dummy_audiobackend.cc +++ b/libs/backends/dummy/dummy_audiobackend.cc @@ -19,6 +19,7 @@ #include #include +#include #include @@ -39,6 +40,7 @@ DummyAudioBackend::DummyAudioBackend (AudioEngine& e, AudioBackendInfo& info) : AudioBackend (e, info) , _running (false) , _freewheeling (false) + , _device ("") , _samplerate (48000) , _samples_per_period (1024) , _dsp_load (0) @@ -78,7 +80,11 @@ std::vector DummyAudioBackend::enumerate_devices () const { if (_device_status.empty()) { - _device_status.push_back (DeviceStatus (_("Dummy"), true)); + _device_status.push_back (DeviceStatus (_("Silence"), true)); + _device_status.push_back (DeviceStatus (_("Sine Wave"), true)); + _device_status.push_back (DeviceStatus (_("White Noise"), true)); + _device_status.push_back (DeviceStatus (_("Pink Noise"), true)); + _device_status.push_back (DeviceStatus (_("Pink Noise (low CPU)"), true)); } return _device_status; } @@ -143,8 +149,9 @@ DummyAudioBackend::can_change_buffer_size_when_running () const } int -DummyAudioBackend::set_device_name (const std::string&) +DummyAudioBackend::set_device_name (const std::string& d) { + _device = d; return 0; } @@ -207,7 +214,7 @@ DummyAudioBackend::set_systemic_output_latency (uint32_t sl) std::string DummyAudioBackend::device_name () const { - return _("Dummy Device"); + return _device; } float @@ -309,6 +316,7 @@ DummyAudioBackend::_start (bool /*for_latency_measurement*/) if (_ports.size()) { PBD::warning << _("DummyAudioBackend: recovering from unclean shutdown, port registry is not empty.") << endmsg; + _system_inputs.clear(); _ports.clear(); } @@ -637,6 +645,18 @@ int DummyAudioBackend::register_system_ports() { LatencyRange lr; + enum DummyAudioPort::GeneratorType gt; + if (_device == _("White Noise")) { + gt = DummyAudioPort::WhiteNoise; + } else if (_device == _("Pink Noise")) { + gt = DummyAudioPort::PinkNoise; + } else if (_device == _("Pink Noise (low CPU)")) { + gt = DummyAudioPort::PonyNoise; + } else if (_device == _("Sine Wave")) { + gt = DummyAudioPort::SineWave; + } else { + gt = DummyAudioPort::Silence; + } const int a_ins = _n_inputs > 0 ? _n_inputs : 8; const int a_out = _n_outputs > 0 ? _n_outputs : 8; @@ -651,6 +671,8 @@ DummyAudioBackend::register_system_ports() PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast(IsOutput | IsPhysical | IsTerminal)); if (!p) return -1; set_latency_range (p, false, lr); + _system_inputs.push_back (static_cast(p)); + static_cast(p)->setup_generator (gt, _samplerate); } lr.min = lr.max = _samples_per_period + _systemic_output_latency; @@ -687,6 +709,7 @@ void DummyAudioBackend::unregister_system_ports() { size_t i = 0; + _system_inputs.clear(); while (i < _ports.size ()) { DummyPort* port = _ports[i]; if (port->is_physical () && port->is_terminal ()) { @@ -1020,6 +1043,12 @@ DummyAudioBackend::main_process_thread () uint64_t clock1, clock2; clock1 = g_get_monotonic_time(); while (_running) { + + // re-set input buffers, generate on demand. + for (std::vector::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) { + (*it)->next_period(); + } + if (engine.process_callback (_samples_per_period)) { return 0; } @@ -1257,12 +1286,133 @@ bool DummyPort::is_physically_connected () const DummyAudioPort::DummyAudioPort (DummyAudioBackend &b, const std::string& name, PortFlags flags) : DummyPort (b, name, flags) + , _gen_type (Silence) + , _gen_cycle (false) + , _pass (false) { memset (_buffer, 0, sizeof (_buffer)); } DummyAudioPort::~DummyAudioPort () { } +void DummyAudioPort::setup_generator (GeneratorType const g, float const samplerate) +{ + _gen_type = g; + _rseed = g_get_monotonic_time() % UINT_MAX; + _pass = false; + + switch (_gen_type) { + case Silence: + break; + case SineWave: + _b1 = 0; + { +#ifdef COMPILER_MSVC + srand (_rseed); + const unsigned int k = rand () % 128; +#else + const unsigned int k = rand_r (&_rseed) % 128; +#endif + // midi note, chromatic scale + _b0 = (440.f / 32.f) * powf(2, (k - 9.0) / 12.0) / samplerate; + assert (_b0 < M_PI/2); // fine when samplerate >= 8K + } + break; + case PinkNoise: + case PonyNoise: + _b0 = _b1 = _b2 = _b3 = _b4 = _b5 = _b6 = 0.f; + // fall trhu, no break + case WhiteNoise: +#ifdef COMPILER_MSVC + srand (_rseed); +#endif + break; + } +} + +static inline float randf (unsigned int *seedp) { + static const float rmf = RAND_MAX / 2.0; + // TODO this should use a better uniform random generator +#ifdef COMPILER_MSVC + return ((float)rand () / rmf) - 1.f; +#else + return ((float)rand_r (seedp) / rmf) - 1.f; +#endif +} + +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 (&_rseed); + x2 = randf (&_rseed); + 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; +} + +void DummyAudioPort::generate (pframes_t n_samples) +{ + switch (_gen_type) { + case Silence: + memset (_buffer, 0, n_samples * sizeof (Sample)); + break; + case SineWave: + for (pframes_t i = 0 ; i < n_samples; ++i) { + _buffer[i] = .12589f * sinf(2.0 * M_PI * _b1); + _b1 += _b0; + if (_b1 > 1.0) _b1 -= 2.0; + } + break; + case WhiteNoise: + 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(&_rseed); + _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.5362; + _b6 = white * 0.115926; + } + break; + case PonyNoise: + for (pframes_t i = 0 ; i < n_samples; ++i) { + const float white = 0.0498f * randf(&_rseed); + // Paul Kellet's economy method + // http://www.musicdsp.org/files/pink.txt + _b0 = 0.99765 * _b0 + white * 0.0990460; + _b1 = 0.96300 * _b1 + white * 0.2965164; + _b2 = 0.57000 * _b2 + white * 1.0526913; + _buffer[i] = _b0 + _b1 + _b2 + white * 0.1848; + } + break; + } +} + void* DummyAudioPort::get_buffer (pframes_t n_samples) { if (is_input ()) { @@ -1270,13 +1420,19 @@ void* DummyAudioPort::get_buffer (pframes_t n_samples) if (it == get_connections ().end ()) { memset (_buffer, 0, n_samples * sizeof (Sample)); } else { - DummyAudioPort const * source = static_cast(*it); + DummyAudioPort * source = static_cast(*it); assert (source && source->is_output ()); + if (source->is_physical() && source->is_terminal()) { + source->get_buffer(n_samples); // generate signal. + } memcpy (_buffer, source->const_buffer (), n_samples * sizeof (Sample)); while (++it != get_connections ().end ()) { - source = static_cast(*it); + source = static_cast(*it); assert (source && source->is_output ()); Sample* dst = buffer (); + if (source->is_physical() && source->is_terminal()) { + source->get_buffer(n_samples); // generate signal. + } const Sample* src = source->const_buffer (); for (uint32_t s = 0; s < n_samples; ++s, ++dst, ++src) { *dst += *src; @@ -1284,7 +1440,10 @@ void* DummyAudioPort::get_buffer (pframes_t n_samples) } } } else if (is_output () && is_physical () && is_terminal()) { - memset (_buffer, 0, n_samples * sizeof (Sample)); + if (!_gen_cycle) { + _gen_cycle = true; + generate(n_samples); + } } return _buffer; } diff --git a/libs/backends/dummy/dummy_audiobackend.h b/libs/backends/dummy/dummy_audiobackend.h index 70aec34ecd..34807faa8f 100644 --- a/libs/backends/dummy/dummy_audiobackend.h +++ b/libs/backends/dummy/dummy_audiobackend.h @@ -125,8 +125,36 @@ class DummyAudioPort : public DummyPort { const Sample* const_buffer () const { return _buffer; } void* get_buffer (pframes_t nframes); + enum GeneratorType { + Silence, + WhiteNoise, + PinkNoise, + PonyNoise, + SineWave, + }; + void next_period () { _gen_cycle = false; } + void setup_generator (GeneratorType const, float const); + private: Sample _buffer[8192]; + + // signal generator ('fake' physical inputs) + void generate (pframes_t n_samples); + GeneratorType _gen_type; + bool _gen_cycle; + + // generator buffers + // (used for pink-noise filters and sine-phase) + float _b0, _b1, _b2, _b3, _b4, _b5, _b6; + + // (per thread) random seed + unsigned int _rseed; + + // gaussian noise generator + float grandf (); + bool _pass; + float _rn1; + }; // class DummyAudioPort class DummyMidiPort : public DummyPort { @@ -298,6 +326,8 @@ class DummyAudioBackend : public AudioBackend { bool _running; bool _freewheeling; + std::string _device; + float _samplerate; size_t _samples_per_period; float _dsp_load; @@ -334,6 +364,7 @@ class DummyAudioBackend : public AudioBackend { int register_system_ports (); void unregister_system_ports (); + std::vector _system_inputs; std::vector _ports;