add signal generator modules to Dummy Backend.
authorRobin Gareus <robin@gareus.org>
Mon, 7 Jul 2014 14:23:23 +0000 (16:23 +0200)
committerRobin Gareus <robin@gareus.org>
Mon, 7 Jul 2014 14:31:58 +0000 (16:31 +0200)
libs/backends/dummy/dummy_audiobackend.cc
libs/backends/dummy/dummy_audiobackend.h

index 3a969aa479bc2cbf12512dc3453464b987267da3..69966f52fd6f13ebba8257a88e573d9c9f4bbe82 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <sys/time.h>
 #include <regex.h>
+#include <stdlib.h>
 
 #include <glibmm.h>
 
@@ -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<AudioBackend::DeviceStatus>
 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<PortFlags>(IsOutput | IsPhysical | IsTerminal));
                if (!p) return -1;
                set_latency_range (p, false, lr);
+               _system_inputs.push_back (static_cast<DummyAudioPort*>(p));
+               static_cast<DummyAudioPort*>(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<DummyAudioPort*>::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<const DummyAudioPort*>(*it);
+                       DummyAudioPort * source = static_cast<DummyAudioPort*>(*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<const DummyAudioPort*>(*it);
+                               source = static_cast<DummyAudioPort*>(*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;
 }
index 70aec34ecd91110dfa37a8b645712275ef39d53f..34807faa8f986993c49ce1c6e62974f2502be6db 100644 (file)
@@ -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<DummyAudioPort *> _system_inputs;
                std::vector<DummyPort *> _ports;