]> git.sesse.net Git - bmusb/commitdiff
Make FakeCapture capable of outputting audio (a simple sine tone).
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 29 Jul 2016 10:32:16 +0000 (12:32 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 29 Jul 2016 10:32:16 +0000 (12:32 +0200)
bmusb/fake_capture.h
fake_capture.cpp

index 48dca063ae7d6b18e21e3554478b4aa1532f1000..17982ea50e6d0ff3071f6d01f9240ede4e9ed993 100644 (file)
@@ -12,7 +12,7 @@ namespace bmusb {
 class FakeCapture : public CaptureInterface
 {
 public:
-       FakeCapture(unsigned width, unsigned height, unsigned fps, unsigned audio_frequency, int card_index);
+       FakeCapture(unsigned width, unsigned height, unsigned fps, unsigned audio_sample_frequency, int card_index, bool has_audio = false);
        ~FakeCapture();
 
        // CaptureInterface.
@@ -79,10 +79,17 @@ public:
 
 private:
        void producer_thread_func();
+       void make_tone(int32_t *out, unsigned num_stereo_samples);
 
-       unsigned width, height, fps, audio_frequency;
+       unsigned width, height, fps, audio_sample_frequency;
        uint8_t y, cb, cr;
 
+       // sin(2 * pi * f / F) and similar for cos. Used for fast sine generation.
+       // Zero when no audio.
+       float audio_sin = 0.0f, audio_cos = 0.0f;
+       float audio_real = 0.0f, audio_imag = 0.0f;  // Current state of the audio phaser.
+       float audio_ref_level;
+
        bool has_dequeue_callbacks = false;
        std::function<void()> dequeue_init_callback = nullptr;
        std::function<void()> dequeue_cleanup_callback = nullptr;
index 432cf423443b6271e5b0edf0983e21f5308962a4..961700eb54158a2b9245059d89f384620dac1f32 100644 (file)
@@ -86,8 +86,8 @@ void memset4(uint8_t *s, const uint8_t c[4], size_t n)
 
 }  // namespace
 
-FakeCapture::FakeCapture(unsigned width, unsigned height, unsigned fps, unsigned audio_frequency, int card_index)
-       : width(width), height(height), fps(fps), audio_frequency(audio_frequency)
+FakeCapture::FakeCapture(unsigned width, unsigned height, unsigned fps, unsigned audio_sample_frequency, int card_index, bool has_audio)
+       : width(width), height(height), fps(fps), audio_sample_frequency(audio_sample_frequency)
 {
        char buf[256];
        snprintf(buf, sizeof(buf), "Fake card %d", card_index + 1);
@@ -96,6 +96,15 @@ FakeCapture::FakeCapture(unsigned width, unsigned height, unsigned fps, unsigned
        y = ys[card_index % NUM_COLORS];
        cb = cbs[card_index % NUM_COLORS];
        cr = crs[card_index % NUM_COLORS];
+
+       if (has_audio) {
+               audio_ref_level = pow(10.0f, -23.0f / 20.0f) * (1u << 31);  // -23 dBFS (EBU R128 level).
+
+               float freq = 440.0 * pow(2.0, card_index / 12.0);
+               sincosf(2 * M_PI * freq / audio_sample_frequency, &audio_sin, &audio_cos);
+               audio_real = audio_ref_level;
+               audio_imag = 0.0f;
+       }
 }
 
 FakeCapture::~FakeCapture()
@@ -254,9 +263,16 @@ void FakeCapture::producer_thread_func()
 
                FrameAllocator::Frame audio_frame = audio_frame_allocator->alloc_frame();
                if (audio_frame.data != nullptr) {
-                       assert(audio_frame.size >= 2 * sizeof(uint32_t) * audio_frequency / fps);
-                       audio_frame.len = 2 * sizeof(uint32_t) * audio_frequency / fps;
-                       memset(audio_frame.data, 0, audio_frame.len);
+                       const unsigned num_stereo_samples = audio_sample_frequency / fps;
+                       assert(audio_frame.size >= 2 * sizeof(int32_t) * num_stereo_samples);
+                       audio_frame.len = 2 * sizeof(int32_t) * num_stereo_samples;
+
+                       if (audio_sin == 0.0f) {
+                               // Silence.
+                               memset(audio_frame.data, 0, audio_frame.len);
+                       } else {
+                               make_tone((int32_t *)audio_frame.data, num_stereo_samples);
+                       }
                }
 
                frame_callback(timecode++,
@@ -268,4 +284,26 @@ void FakeCapture::producer_thread_func()
        }
 }
 
+void FakeCapture::make_tone(int32_t *out, unsigned num_stereo_samples)
+{
+       int32_t *ptr = out;
+       float r = audio_real, i = audio_imag;
+       for (unsigned sample_num = 0; sample_num < num_stereo_samples; ++sample_num) {
+               int32_t s = lrintf(r);
+               *ptr++ = s;
+               *ptr++ = s;
+
+               // Rotate the phaser by one sample.
+               float new_r = r * audio_cos - i * audio_sin;
+               float new_i = r * audio_sin + i * audio_cos;
+               r = new_r;
+               i = new_i;
+       }
+
+       // Periodically renormalize to counteract precision issues.
+       double corr = audio_ref_level / hypot(r, i);
+       audio_real = r * corr;
+       audio_imag = i * corr;
+}
+
 }  // namespace bmusb