summaryrefslogtreecommitdiff
path: root/Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp')
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp251
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