diff options
Diffstat (limited to 'Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp')
-rw-r--r-- | Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp new file mode 100644 index 000000000..2aab65dd8 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if PLATFORM(MAC) + +#include "Test.h" +#include <WebCore/CARingBuffer.h> +#include <wtf/MainThread.h> + +using namespace WebCore; + +namespace TestWebKitAPI { + +class CARingBufferTest : public testing::Test { +public: + + virtual void SetUp() + { + WTF::initializeMainThread(); + m_ringBuffer = std::make_unique<CARingBuffer>(); + } + + // CAAudioStreamDescription(double sampleRate, UInt32 numChannels, PCMFormat format, bool isInterleaved, size_t capacity) + void setup(double sampleRate, UInt32 numChannels, CAAudioStreamDescription::PCMFormat format, bool isInterleaved, size_t capacity) + { + m_description = CAAudioStreamDescription(sampleRate, numChannels, format, isInterleaved); + m_capacity = capacity; + size_t listSize = offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * std::max<uint32_t>(1, m_description.numberOfChannelStreams())); + m_bufferList = std::unique_ptr<AudioBufferList>(static_cast<AudioBufferList*>(::operator new (listSize))); + m_ringBuffer->allocate(m_description, capacity); + } + + void setListDataBuffer(uint8_t* bufferData, size_t sampleCount) + { + size_t bufferCount = m_description.numberOfChannelStreams(); + size_t channelCount = m_description.numberOfInterleavedChannels(); + size_t bytesPerChannel = sampleCount * m_description.bytesPerFrame(); + + m_bufferList->mNumberBuffers = bufferCount; + for (unsigned i = 0; i < bufferCount; ++i) { + m_bufferList->mBuffers[i].mNumberChannels = channelCount; + m_bufferList->mBuffers[i].mDataByteSize = bytesPerChannel; + m_bufferList->mBuffers[i].mData = bufferData; + if (bufferData) + bufferData = bufferData + bytesPerChannel; + } + } + + const CAAudioStreamDescription& description() const { return m_description; } + AudioBufferList& bufferList() const { return *m_bufferList.get(); } + CARingBuffer& ringBuffer() const { return *m_ringBuffer.get(); } + size_t capacity() const { return m_capacity; } + +private: + size_t audioBufferListSizeForStream(const CAAudioStreamDescription& format) + { + return offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * std::max<uint32_t>(1, format.numberOfChannelStreams())); + } + + void configureBufferListForStream(AudioBufferList& bufferList, const CAAudioStreamDescription& format, uint8_t* bufferData, size_t sampleCount) + { + size_t bufferCount = format.numberOfChannelStreams(); + size_t channelCount = format.numberOfInterleavedChannels(); + size_t bytesPerChannel = sampleCount * format.bytesPerFrame(); + + bufferList.mNumberBuffers = bufferCount; + for (unsigned i = 0; i < bufferCount; ++i) { + bufferList.mBuffers[i].mNumberChannels = channelCount; + bufferList.mBuffers[i].mDataByteSize = bytesPerChannel; + bufferList.mBuffers[i].mData = bufferData; + if (bufferData) + bufferData = bufferData + bytesPerChannel; + } + } + + std::unique_ptr<AudioBufferList> m_bufferList; + std::unique_ptr<CARingBuffer> m_ringBuffer; + CAAudioStreamDescription m_description = { }; + size_t m_capacity = { 0 }; +}; + +TEST_F(CARingBufferTest, Basics) +{ + const int capacity = 32; + + setup(44100, 1, CAAudioStreamDescription::PCMFormat::Float32, true, capacity); + + float sourceBuffer[capacity]; + for (int i = 0; i < capacity; i++) + sourceBuffer[i] = i + 0.5; + + setListDataBuffer(reinterpret_cast<uint8_t*>(sourceBuffer), capacity); + + // Fill the first half of the buffer ... + int sampleCount = capacity / 2; + CARingBuffer::Error err = ringBuffer().store(&bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + uint64_t startTime; + uint64_t endTime; + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ(0, (int)startTime); + EXPECT_EQ((int)sampleCount, (int)endTime); + + float scratchBuffer[capacity]; + setListDataBuffer(reinterpret_cast<uint8_t*>(scratchBuffer), capacity); + + err = ringBuffer().fetch(&bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + EXPECT_TRUE(!memcmp(sourceBuffer, scratchBuffer, sampleCount * description().sampleWordSize())); + + // ... and the second half. + err = ringBuffer().store(&bufferList(), capacity / 2, capacity / 2); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ(0, (int)startTime); + EXPECT_EQ(capacity, (int)endTime); + + memset(scratchBuffer, 0, sampleCount * description().sampleWordSize()); + err = ringBuffer().fetch(&bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + EXPECT_TRUE(!memcmp(sourceBuffer, scratchBuffer, sampleCount * description().sampleWordSize())); + + // Force the buffer to wrap around + err = ringBuffer().store(&bufferList(), capacity, capacity - 1); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ((int)capacity - 1, (int)startTime); + EXPECT_EQ(capacity - 1 + capacity, (int)endTime); + + // Make sure it returns an error when asked to store too much ... + err = ringBuffer().store(&bufferList(), capacity * 3, capacity / 2); + EXPECT_EQ(err, CARingBuffer::Error::TooMuch); + + // ... and doesn't modify the buffer + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ((int)capacity - 1, (int)startTime); + EXPECT_EQ(capacity - 1 + capacity, (int)endTime); + + ringBuffer().flush(); + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ(0, (int)startTime); + EXPECT_EQ(0, (int)endTime); +} + +template <typename type> +class MixingTest { +public: + static void run(CARingBufferTest& test) + { + const int sampleCount = 64; + + CAAudioStreamDescription::PCMFormat format; + if (std::is_same<type, float>::value) + format = CAAudioStreamDescription::PCMFormat::Float32; + else if (std::is_same<type, double>::value) + format = CAAudioStreamDescription::PCMFormat::Float64; + else if (std::is_same<type, int32_t>::value) + format = CAAudioStreamDescription::PCMFormat::Int32; + else if (std::is_same<type, int16_t>::value) + format = CAAudioStreamDescription::PCMFormat::Int16; + else + ASSERT_NOT_REACHED(); + + test.setup(44100, 1, format, true, sampleCount); + + type referenceBuffer[sampleCount]; + type sourceBuffer[sampleCount]; + type readBuffer[sampleCount]; + + for (int i = 0; i < sampleCount; i++) { + sourceBuffer[i] = i * 0.5; + referenceBuffer[i] = sourceBuffer[i]; + } + + test.setListDataBuffer(reinterpret_cast<uint8_t*>(sourceBuffer), sampleCount); + CARingBuffer::Error err = test.ringBuffer().store(&test.bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + memset(readBuffer, 0, sampleCount * test.description().sampleWordSize()); + test.setListDataBuffer(reinterpret_cast<uint8_t*>(readBuffer), sampleCount); + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + for (int i = 0; i < sampleCount; i++) + EXPECT_EQ(readBuffer[i], referenceBuffer[i]) << "Ring buffer value differs at index " << i; + + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + for (int i = 0; i < sampleCount; i++) + referenceBuffer[i] += sourceBuffer[i] * 3; + + for (int i = 0; i < sampleCount; i++) + EXPECT_EQ(readBuffer[i], referenceBuffer[i]) << "Ring buffer value differs at index " << i; + } +}; + +TEST_F(CARingBufferTest, FloatMixing) +{ + MixingTest<float>::run(*this); +} + +TEST_F(CARingBufferTest, DoubleMixing) +{ + MixingTest<double>::run(*this); +} + +TEST_F(CARingBufferTest, Int32Mixing) +{ + MixingTest<int32_t>::run(*this); +} + +TEST_F(CARingBufferTest, Int16Mixing) +{ + MixingTest<int16_t>::run(*this); +} + +} + +#endif |