From e0837a17b5a497476d67237c768836e51f8a4ce7 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Fri, 29 Jul 2016 12:32:16 +0200 Subject: [PATCH] Make FakeCapture capable of outputting audio (a simple sine tone). --- bmusb/fake_capture.h | 11 ++++++++-- fake_capture.cpp | 48 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/bmusb/fake_capture.h b/bmusb/fake_capture.h index 48dca06..17982ea 100644 --- a/bmusb/fake_capture.h +++ b/bmusb/fake_capture.h @@ -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 dequeue_init_callback = nullptr; std::function dequeue_cleanup_callback = nullptr; diff --git a/fake_capture.cpp b/fake_capture.cpp index 432cf42..961700e 100644 --- a/fake_capture.cpp +++ b/fake_capture.cpp @@ -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 -- 2.39.2