From c16beb439ad99ba591165f6e71ab70d82d7b9627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com> Date: Wed, 28 Nov 2018 13:44:13 -0500 Subject: [PATCH] audioframe: implement mix Change-Id: I8d8d6bd852d76040eb967dda76dcd8c85118e235 Reviewed-by: Philippe Gorley <philippe.gorley@savoirfairelinux.com> --- src/client/videomanager.cpp | 39 ++++++++++++++++++++++-- test/unitTest/media/test_media_frame.cpp | 36 ++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp index 3c646806ee..8265bf8cfa 100644 --- a/src/client/videomanager.cpp +++ b/src/client/videomanager.cpp @@ -99,9 +99,44 @@ AudioFrame::reserve(size_t nb_samples) } void -AudioFrame::mix(const AudioFrame&) +AudioFrame::mix(const AudioFrame& frame) { - RING_ERR("AudioFrame::mix not implemented yet"); + auto& f = *pointer(); + auto& fIn = *frame.pointer(); + if (f.channels != fIn.channels || f.format != fIn.format || f.sample_rate != fIn.sample_rate) { + throw std::invalid_argument("Can't mix frames with different formats"); + } + if (f.nb_samples == 0) { + reserve(fIn.nb_samples); + } else if (f.nb_samples != fIn.nb_samples) { + throw std::invalid_argument("Can't mix frames with different length"); + } + AVSampleFormat fmt = (AVSampleFormat)f.format; + bool isPlanar = av_sample_fmt_is_planar(fmt); + unsigned samplesPerChannel = isPlanar ? f.nb_samples : f.nb_samples * f.channels; + unsigned channels = isPlanar ? f.channels : 1; + if (fmt == AV_SAMPLE_FMT_S16 || fmt == AV_SAMPLE_FMT_S16P) { + for (unsigned i=0; i < channels; i++) { + auto c = (int16_t*)f.extended_data[i]; + auto cIn = (int16_t*)fIn.extended_data[i]; + for (unsigned s=0; s < samplesPerChannel; s++) { + int32_t n = (int32_t)c[s] + (int32_t)cIn[s]; + n = std::min<int32_t>(n, std::numeric_limits<int16_t>::max()); + n = std::max<int32_t>(n, std::numeric_limits<int16_t>::min()); + c[s] = n; + } + } + } else if (fmt == AV_SAMPLE_FMT_FLT || fmt == AV_SAMPLE_FMT_FLTP) { + for (unsigned i=0; i < channels; i++) { + auto c = (float*)f.extended_data[i]; + auto cIn = (float*)fIn.extended_data[i]; + for (unsigned s=0; s < samplesPerChannel; s++) { + c[s] += cIn[s]; + } + } + } else { + throw std::invalid_argument(std::string("Unsupported format for mixing: ") + av_get_sample_fmt_name(fmt)); + } } VideoFrame::~VideoFrame() diff --git a/test/unitTest/media/test_media_frame.cpp b/test/unitTest/media/test_media_frame.cpp index 3a3c2a1948..241c90b875 100644 --- a/test/unitTest/media/test_media_frame.cpp +++ b/test/unitTest/media/test_media_frame.cpp @@ -27,6 +27,7 @@ extern "C" { #include <libavutil/pixfmt.h> } +#include "audio/audiobuffer.h" #include "dring.h" #include "videomanager_interface.h" @@ -43,9 +44,11 @@ public: private: void testCopy(); + void testMix(); CPPUNIT_TEST_SUITE(MediaFrameTest); CPPUNIT_TEST(testCopy); + CPPUNIT_TEST(testMix); CPPUNIT_TEST_SUITE_END(); }; @@ -84,6 +87,39 @@ MediaFrameTest::testCopy() CPPUNIT_ASSERT(v2.pointer()->data[0][0] == 42); } +void +MediaFrameTest::testMix() +{ + const AudioFormat& format = AudioFormat::STEREO(); + const int nbSamples = format.sample_rate / 50; + auto a1 = std::make_unique<DRing::AudioFrame>(format, nbSamples); + auto d1 = reinterpret_cast<AudioSample*>(a1->pointer()->extended_data[0]); + d1[0] = 0; + d1[1] = 1; + d1[2] = 3; + d1[3] = -2; + d1[4] = 5; + d1[5] = std::numeric_limits<AudioSample>::min(); + d1[6] = std::numeric_limits<AudioSample>::max(); + auto a2 = std::make_unique<DRing::AudioFrame>(format, nbSamples); + auto d2 = reinterpret_cast<AudioSample*>(a2->pointer()->extended_data[0]); + d2[0] = 0; + d2[1] = 3; + d2[2] = -1; + d2[3] = 3; + d2[4] = -6; + d2[5] = -101; + d2[6] = 101; + a2->mix(*a1); + CPPUNIT_ASSERT(d2[0] == 0); + CPPUNIT_ASSERT(d2[1] == 4); + CPPUNIT_ASSERT(d2[2] == 2); + CPPUNIT_ASSERT(d2[3] == 1); + CPPUNIT_ASSERT(d2[4] == -1); + CPPUNIT_ASSERT(d2[5] == std::numeric_limits<AudioSample>::min()); + CPPUNIT_ASSERT(d2[6] == std::numeric_limits<AudioSample>::max()); +} + }} // namespace ring::test RING_TEST_RUNNER(ring::test::MediaFrameTest::name()); -- GitLab