aboutsummaryrefslogtreecommitdiff
path: root/test/audio
diff options
context:
space:
mode:
Diffstat (limited to 'test/audio')
-rw-r--r--test/audio/audiostream.h209
-rw-r--r--test/audio/helper.h112
-rw-r--r--test/audio/raw.h358
-rw-r--r--test/audio/timestamp.h241
4 files changed, 920 insertions, 0 deletions
diff --git a/test/audio/audiostream.h b/test/audio/audiostream.h
new file mode 100644
index 0000000000..1ffb2308ec
--- /dev/null
+++ b/test/audio/audiostream.h
@@ -0,0 +1,209 @@
+#include <cxxtest/TestSuite.h>
+
+#include "audio/audiostream.h"
+
+#include "helper.h"
+
+class AudioStreamTestSuite : public CxxTest::TestSuite
+{
+public:
+ void test_convertTimeToStreamPos() {
+ const Audio::Timestamp a = Audio::convertTimeToStreamPos(Audio::Timestamp(500, 1000), 11025, true);
+ // The last bit has to be 0 in any case for a stereo stream.
+ TS_ASSERT_EQUALS(a.totalNumberOfFrames() & 1, 0);
+
+ // TODO: This test is rather hacky... actually converTimeToStreamPos might also return 11026
+ // instead of 11024 and it would still be a valid sample position for ~500ms.
+ TS_ASSERT_EQUALS(a.totalNumberOfFrames(), 11024);
+
+ const Audio::Timestamp b = Audio::convertTimeToStreamPos(Audio::Timestamp(500, 1000), 11025, false);
+ TS_ASSERT_EQUALS(b.totalNumberOfFrames(), 500 * 11025 / 1000);
+
+ // Test Audio CD positioning
+
+ // for 44kHz and stereo
+ const Audio::Timestamp c = Audio::convertTimeToStreamPos(Audio::Timestamp(0, 50, 75), 44100, true);
+ TS_ASSERT_EQUALS(c.totalNumberOfFrames(), 50 * 44100 * 2 / 75);
+
+ // for 11kHz and mono
+ const Audio::Timestamp d = Audio::convertTimeToStreamPos(Audio::Timestamp(0, 50, 75), 11025, false);
+ TS_ASSERT_EQUALS(d.totalNumberOfFrames(), 50 * 11025 / 75);
+
+ // Some misc test
+ const Audio::Timestamp e = Audio::convertTimeToStreamPos(Audio::Timestamp(1, 1, 4), 11025, false);
+ TS_ASSERT_EQUALS(e.totalNumberOfFrames(), 5 * 11025 / 4);
+ }
+
+private:
+ void testLoopingAudioStreamFixedIter(const int sampleRate, const bool isStereo) {
+ const int secondLength = sampleRate * (isStereo ? 2 : 1);
+
+ int16 *sine = 0;
+ Audio::SeekableAudioStream *s = createSineStream<int16>(sampleRate, 1, &sine, false, isStereo);
+ Audio::LoopingAudioStream *loop = new Audio::LoopingAudioStream(s, 7);
+
+ int16 *buffer = new int16[secondLength * 3];
+
+ // Check parameters
+ TS_ASSERT_EQUALS(loop->isStereo(), isStereo);
+ TS_ASSERT_EQUALS(loop->getRate(), sampleRate);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+ TS_ASSERT_EQUALS(loop->getCompleteIterations(), (uint)0);
+
+ // Read one second
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, secondLength), secondLength);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine, secondLength * sizeof(int16)), 0);
+
+ TS_ASSERT_EQUALS(loop->getCompleteIterations(), (uint)1);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+
+ // Read two seconds
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, secondLength * 2), secondLength * 2);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine, secondLength * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(memcmp(buffer + secondLength, sine, secondLength * sizeof(int16)), 0);
+
+ TS_ASSERT_EQUALS(loop->getCompleteIterations(), (uint)3);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+
+ // Read three seconds
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, secondLength * 3), secondLength * 3);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine, secondLength * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(memcmp(buffer + secondLength, sine, secondLength * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(memcmp(buffer + secondLength * 2, sine, secondLength * sizeof(int16)), 0);
+
+ TS_ASSERT_EQUALS(loop->getCompleteIterations(), (uint)6);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+
+ // Read the last second in two parts
+ const int firstStep = secondLength / 2;
+ const int secondStep = secondLength - firstStep;
+
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, firstStep), firstStep);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine, firstStep * sizeof(int16)), 0);
+
+ TS_ASSERT_EQUALS(loop->getCompleteIterations(), (uint)6);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, secondLength), secondStep);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine + firstStep, secondStep * sizeof(int16)), 0);
+
+ TS_ASSERT_EQUALS(loop->getCompleteIterations(), (uint)7);
+ TS_ASSERT_EQUALS(loop->endOfData(), true);
+
+ // Try to read beyond the end of the stream
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, secondLength), 0);
+ TS_ASSERT_EQUALS(loop->getCompleteIterations(), (uint)7);
+ TS_ASSERT_EQUALS(loop->endOfData(), true);
+
+ delete[] buffer;
+ delete loop;
+ delete[] sine;
+ }
+
+public:
+ void test_looping_audio_stream_mono_11025_fixed_iter() {
+ testLoopingAudioStreamFixedIter(11025, false);
+ }
+
+ void test_looping_audio_stream_mono_22050_fixed_iter() {
+ testLoopingAudioStreamFixedIter(22050, false);
+ }
+
+ void test_looping_audio_stream_stereo_11025_fixed_iter() {
+ testLoopingAudioStreamFixedIter(11025, true);
+ }
+
+ void test_looping_audio_stream_stereo_22050_fixed_iter() {
+ testLoopingAudioStreamFixedIter(22050, true);
+ }
+
+private:
+ void testSubLoopingAudioStreamFixedIter(const int sampleRate, const bool isStereo, const int time, const int loopEndTime) {
+ const int secondLength = sampleRate * (isStereo ? 2 : 1);
+
+ const Audio::Timestamp loopStart(500, 1000), loopEnd(loopEndTime * 1000, 1000);
+
+ const int32 loopStartPos = Audio::convertTimeToStreamPos(loopStart, sampleRate, isStereo).totalNumberOfFrames();
+ const int32 loopEndPos = Audio::convertTimeToStreamPos(loopEnd, sampleRate, isStereo).totalNumberOfFrames();
+
+ const int32 loopIteration = loopEndPos - loopStartPos;
+
+ int16 *sine = 0;
+ Audio::SeekableAudioStream *s = createSineStream<int16>(sampleRate, time, &sine, false, isStereo);
+ Audio::SubLoopingAudioStream *loop = new Audio::SubLoopingAudioStream(s, 5, loopStart, loopEnd);
+
+ const int32 bufferLen = MAX<int32>(loopIteration * 3, loopEndPos);
+ int16 *buffer = new int16[bufferLen];
+
+ // Check parameters
+ TS_ASSERT_EQUALS(loop->isStereo(), isStereo);
+ TS_ASSERT_EQUALS(loop->getRate(), sampleRate);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+
+ // Read the non-looped part + one iteration
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, loopEndPos), loopEndPos);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine, loopEndPos * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+
+ // We should have one full iteration now
+
+ // Read another loop iteration
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, loopIteration), loopIteration);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine + loopStartPos, loopIteration * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(loop->endOfData(), false);
+
+ // We should have two full iterations now
+
+ // Read three loop iterations at once
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, loopIteration * 3), loopIteration * 3);
+ TS_ASSERT_EQUALS(memcmp(buffer + loopIteration * 0, sine + loopStartPos, loopIteration * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(memcmp(buffer + loopIteration * 1, sine + loopStartPos, loopIteration * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(memcmp(buffer + loopIteration * 2, sine + loopStartPos, loopIteration * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(loop->endOfData(), true);
+
+ // We should have five full iterations now, thus the stream should be done
+
+ // Try to read beyond the end of the stream (note this only applies, till we define that SubLoopingAudioStream should
+ // stop playing after the looped area).
+ TS_ASSERT_EQUALS(loop->readBuffer(buffer, secondLength), 0);
+ TS_ASSERT_EQUALS(loop->endOfData(), true);
+
+ delete[] buffer;
+ delete loop;
+ delete[] sine;
+ }
+
+public:
+ void test_sub_looping_audio_stream_mono_11025_mid_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(11025, false, 2, 1);
+ }
+
+ void test_sub_looping_audio_stream_mono_22050_mid_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(22050, false, 2, 1);
+ }
+
+ void test_sub_looping_audio_stream_stereo_11025_mid_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(11025, true, 2, 1);
+ }
+
+ void test_sub_looping_audio_stream_stereo_22050_mid_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(22050, true, 2, 1);
+ }
+
+ void test_sub_looping_audio_stream_mono_11025_end_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(11025, false, 2, 2);
+ }
+
+ void test_sub_looping_audio_stream_mono_22050_end_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(22050, false, 2, 2);
+ }
+
+ void test_sub_looping_audio_stream_stereo_11025_end_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(11025, true, 2, 2);
+ }
+
+ void test_sub_looping_audio_stream_stereo_22050_end_fixed_iter() {
+ testSubLoopingAudioStreamFixedIter(22050, true, 2, 2);
+ }
+};
+
diff --git a/test/audio/helper.h b/test/audio/helper.h
new file mode 100644
index 0000000000..262ca1c060
--- /dev/null
+++ b/test/audio/helper.h
@@ -0,0 +1,112 @@
+#ifndef TEST_SOUND_HELPER_H
+#define TEST_SOUND_HELPER_H
+
+#include "audio/decoders/raw.h"
+
+#include "common/stream.h"
+#include "common/endian.h"
+
+#include <math.h>
+#include <limits>
+
+template<typename T>
+static T *createSine(const int sampleRate, const int time) {
+ T *sine = (T *)malloc(sizeof(T) * time * sampleRate);
+
+ const bool isUnsigned = !std::numeric_limits<T>::is_signed;
+ const T xorMask = isUnsigned ? (1 << (std::numeric_limits<T>::digits - 1)) : 0;
+ const T maxValue = std::numeric_limits<T>::max() ^ xorMask;
+
+ for (int i = 0; i < time * sampleRate; ++i)
+ sine[i] = ((T)(sin((double)i / sampleRate * 2 * PI) * maxValue)) ^ xorMask;
+
+ return sine;
+}
+
+template<typename T>
+static Common::SeekableReadStream *createPartitionStream(T *sine, const int samples, Audio::RawStreamBlockList &blockList) {
+ const int block1Len = samples / 2;
+ const int block1Size = block1Len * sizeof(T);
+ const int block2Len = samples - block1Len;
+ const int block2Size = block2Len * sizeof(T);
+
+ const int bufferLen = samples * 2;
+ const int bufferSize = bufferLen * sizeof(T);
+ T *partition = (T *)calloc(1, bufferSize);
+
+ Audio::RawStreamBlock block;
+
+ // The will layout the buffer like the following:
+ // [Zero], [Part2], [Zero], [Part1]
+
+ // The first part of the stream is at the end of the memory buffer
+ block.pos = bufferSize - block1Size;
+ block.len = block1Len;
+ memcpy(partition + bufferLen - block1Len, sine, block1Size);
+ blockList.push_back(block);
+
+ // The second part of the stream is near the beginning of the memory buffer
+ block.pos = block2Size;
+ block.len = block2Len;
+ memcpy(partition + block2Len, sine + block1Len, block2Size);
+ blockList.push_back(block);
+
+ free(sine);
+
+ return new Common::MemoryReadStream((const byte *)partition, bufferSize, DisposeAfterUse::YES);
+}
+
+template<typename T>
+static Audio::SeekableAudioStream *createSineStream(const int sampleRate, const int time, int16 **comp, bool le, bool isStereo, bool makePartition = false) {
+ T *sine = createSine<T>(sampleRate, time * (isStereo ? 2 : 1));
+
+ const bool isUnsigned = !std::numeric_limits<T>::is_signed;
+ const T xorMask = isUnsigned ? (1 << (std::numeric_limits<T>::digits - 1)) : 0;
+ const bool is16Bits = (sizeof(T) == 2);
+ assert(sizeof(T) == 2 || sizeof(T) == 1);
+
+ const int samples = sampleRate * time * (isStereo ? 2 : 1);
+
+ if (comp) {
+ *comp = new int16[samples];
+ for (int i = 0; i < samples; ++i) {
+ if (is16Bits)
+ (*comp)[i] = sine[i] ^ xorMask;
+ else
+ (*comp)[i] = (sine[i] ^ xorMask) << 8;
+ }
+ }
+
+ if (is16Bits) {
+ if (le) {
+ for (int i = 0; i < samples; ++i)
+ WRITE_LE_UINT16(&sine[i], sine[i]);
+ } else {
+ for (int i = 0; i < samples; ++i)
+ WRITE_BE_UINT16(&sine[i], sine[i]);
+ }
+ }
+
+ Audio::SeekableAudioStream *s = 0;
+ if (makePartition) {
+ Audio::RawStreamBlockList blockList;
+ Common::SeekableReadStream *sD = createPartitionStream<T>(sine, samples, blockList);
+ s = Audio::makeRawStream(sD, blockList, sampleRate,
+ (is16Bits ? Audio::FLAG_16BITS : 0)
+ | (isUnsigned ? Audio::FLAG_UNSIGNED : 0)
+ | (le ? Audio::FLAG_LITTLE_ENDIAN : 0)
+ | (isStereo ? Audio::FLAG_STEREO : 0));
+ } else {
+ Common::SeekableReadStream *sD = new Common::MemoryReadStream((const byte *)sine, sizeof(T) * samples, DisposeAfterUse::YES);
+ s = Audio::makeRawStream(sD, sampleRate,
+ (is16Bits ? Audio::FLAG_16BITS : 0)
+ | (isUnsigned ? Audio::FLAG_UNSIGNED : 0)
+ | (le ? Audio::FLAG_LITTLE_ENDIAN : 0)
+ | (isStereo ? Audio::FLAG_STEREO : 0));
+ }
+
+ return s;
+}
+
+#endif
+
diff --git a/test/audio/raw.h b/test/audio/raw.h
new file mode 100644
index 0000000000..51ec067f7e
--- /dev/null
+++ b/test/audio/raw.h
@@ -0,0 +1,358 @@
+#include <cxxtest/TestSuite.h>
+
+#include "audio/decoders/raw.h"
+
+#include "helper.h"
+
+class RawStreamTestSuite : public CxxTest::TestSuite
+{
+private:
+ template<typename T>
+ void readBufferTestTemplate(const int sampleRate, const int time, const bool le, const bool isStereo, const bool makePartition = false) {
+ int16 *sine;
+ Audio::SeekableAudioStream *s = createSineStream<int8>(sampleRate, time, &sine, le, isStereo, makePartition);
+
+ const int totalSamples = sampleRate * time * (isStereo ? 2 : 1);
+ int16 *buffer = new int16[totalSamples];
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, totalSamples), totalSamples);
+ TS_ASSERT_EQUALS(memcmp(sine, buffer, sizeof(int16) * totalSamples), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ delete[] sine;
+ delete[] buffer;
+ delete s;
+ }
+
+public:
+ void test_read_buffer_8_bit_signed_mono() {
+ readBufferTestTemplate<int8>(11025, 2, false, false);
+ }
+
+ void test_read_buffer_8_bit_signed_stereo() {
+ readBufferTestTemplate<int8>(11025, 2, false, true);
+ }
+
+ void test_read_buffer_8_bit_unsigned_mono() {
+ readBufferTestTemplate<uint8>(11025, 2, false, false);
+ }
+
+ void test_read_buffer_16_bit_signed_be_mono() {
+ readBufferTestTemplate<int16>(11025, 2, false, false);
+ }
+
+ void test_read_buffer_16_bit_signed_be_stereo() {
+ readBufferTestTemplate<int16>(11025, 2, false, true);
+ }
+
+ void test_read_buffer_16_bit_unsigned_be_mono() {
+ readBufferTestTemplate<uint16>(11025, 2, false, false);
+ }
+
+ void test_read_buffer_16_bit_unsigned_be_stereo() {
+ readBufferTestTemplate<uint16>(11025, 2, false, true);
+ }
+
+ void test_read_buffer_16_bit_signed_le_mono() {
+ readBufferTestTemplate<int16>(11025, 2, true, false);
+ }
+
+ void test_read_buffer_16_bit_signed_le_stereo() {
+ readBufferTestTemplate<int16>(11025, 2, true, true);
+ }
+
+ void test_read_buffer_16_bit_unsigned_le_mono() {
+ readBufferTestTemplate<uint16>(11025, 2, true, false);
+ }
+
+ void test_read_buffer_16_bit_unsigned_le_stereo() {
+ readBufferTestTemplate<uint16>(11025, 2, true, true);
+ }
+
+ void test_read_buffer_8_bit_signed_mono_parted() {
+ readBufferTestTemplate<int8>(11025, 2, false, false, true);
+ }
+
+ void test_read_buffer_8_bit_signed_stereo_parted() {
+ readBufferTestTemplate<int8>(11025, 2, false, true, true);
+ }
+
+ void test_read_buffer_8_bit_unsigned_mono_parted() {
+ readBufferTestTemplate<uint8>(11025, 2, false, false, true);
+ }
+
+ void test_read_buffer_16_bit_signed_be_mono_parted() {
+ readBufferTestTemplate<int16>(11025, 2, false, false, true);
+ }
+
+ void test_read_buffer_16_bit_signed_be_stereo_parted() {
+ readBufferTestTemplate<int16>(11025, 2, false, true, true);
+ }
+
+ void test_read_buffer_16_bit_unsigned_be_mono_parted() {
+ readBufferTestTemplate<uint16>(11025, 2, false, false, true);
+ }
+
+ void test_read_buffer_16_bit_unsigned_be_stereo_parted() {
+ readBufferTestTemplate<uint16>(11025, 2, false, true, true);
+ }
+
+ void test_read_buffer_16_bit_signed_le_mono_parted() {
+ readBufferTestTemplate<int16>(11025, 2, true, false, true);
+ }
+
+ void test_read_buffer_16_bit_signed_le_stereo_parted() {
+ readBufferTestTemplate<int16>(11025, 2, true, true, true);
+ }
+
+ void test_read_buffer_16_bit_unsigned_le_mono_parted() {
+ readBufferTestTemplate<uint16>(11025, 2, true, false, true);
+ }
+
+ void test_read_buffer_16_bit_unsigned_le_stereo_parted() {
+ readBufferTestTemplate<uint16>(11025, 2, true, true, true);
+ }
+
+private:
+ void partialReadTest(const bool makePartition) {
+ const int sampleRate = 11025;
+ const int time = 4;
+
+ int16 *sine;
+ Audio::SeekableAudioStream *s = createSineStream<int8>(sampleRate, time, &sine, false, false, makePartition);
+ int16 *buffer = new int16[sampleRate * time];
+
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, sampleRate), sampleRate);
+ TS_ASSERT_EQUALS(memcmp(sine, buffer, sampleRate), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, sampleRate * 2), sampleRate * 2);
+ TS_ASSERT_EQUALS(memcmp(sine + sampleRate, buffer, sampleRate * 2), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, sampleRate), sampleRate);
+ TS_ASSERT_EQUALS(memcmp(sine + sampleRate * 3, buffer, sampleRate), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ delete[] sine;
+ delete[] buffer;
+ delete s;
+ }
+public:
+ void test_partial_read() {
+ partialReadTest(false);
+ }
+
+ void test_partial_read_parted() {
+ partialReadTest(true);
+ }
+
+private:
+ void readAfterEndTest(const bool makePartition) {
+ const int sampleRate = 11025;
+ const int time = 1;
+ Audio::SeekableAudioStream *s = createSineStream<int8>(sampleRate, time, 0, false, false);
+ int16 *buffer = new int16[sampleRate * time];
+
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, sampleRate * time), sampleRate * time);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, sampleRate * time), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ delete[] buffer;
+ delete s;
+ }
+
+public:
+ void test_read_after_end() {
+ readAfterEndTest(false);
+ }
+
+ void test_read_after_end_parted() {
+ readAfterEndTest(true);
+ }
+
+private:
+ void rewindTest(const bool makePartition) {
+ const int sampleRate = 11025;
+ const int time = 2;
+ Audio::SeekableAudioStream *s = createSineStream<int8>(sampleRate, time, 0, false, false, makePartition);
+ int16 *buffer = new int16[sampleRate * time];
+
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, sampleRate * time), sampleRate * time);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ s->rewind();
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, sampleRate * time), sampleRate * time);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ delete[] buffer;
+ delete s;
+ }
+public:
+ void test_rewind() {
+ rewindTest(false);
+ }
+
+ void test_rewind_parted() {
+ rewindTest(true);
+ }
+
+private:
+ void lengthTest(const bool makePartition) {
+ int sampleRate = 0;
+ const int time = 4;
+
+ Audio::SeekableAudioStream *s = 0;
+
+ // 11025 Hz tests
+ sampleRate = 11025;
+ s = createSineStream<int8>(sampleRate, time, 0, false, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ s = createSineStream<uint16>(sampleRate, time, 0, false, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ // 48000 Hz tests
+ sampleRate = 48000;
+ s = createSineStream<int8>(sampleRate, time, 0, false, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ s = createSineStream<uint16>(sampleRate, time, 0, true, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ // 11840 Hz tests
+ sampleRate = 11840;
+ s = createSineStream<int8>(sampleRate, time, 0, false, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ s = createSineStream<uint16>(sampleRate, time, 0, false, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ // 11111 Hz tests
+ sampleRate = 11111;
+ s = createSineStream<int8>(sampleRate, time, 0, false, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ s = createSineStream<uint16>(sampleRate, time, 0, false, false, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ // 22050 Hz stereo test
+ sampleRate = 22050;
+ s = createSineStream<int8>(sampleRate, time, 0, false, true, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+
+ s = createSineStream<uint16>(sampleRate, time, 0, true, true, makePartition);
+ TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
+ delete s;
+ }
+
+public:
+ void test_length() {
+ lengthTest(false);
+ }
+
+ void test_length_parted() {
+ lengthTest(true);
+ }
+
+private:
+ void seekTest(const int sampleRate, const int time, const bool isStereo, const bool makePartition) {
+ const int totalFrames = sampleRate * time * (isStereo ? 2 : 1);
+ int readData = 0, offset = 0;
+
+ int16 *buffer = new int16[totalFrames];
+ Audio::SeekableAudioStream *s = 0;
+ int16 *sine = 0;
+
+ s = createSineStream<int8>(sampleRate, time, &sine, false, isStereo, makePartition);
+
+ // Seek to 500ms
+ const Audio::Timestamp a(0, 1, 2);
+ offset = Audio::convertTimeToStreamPos(a, sampleRate, isStereo).totalNumberOfFrames();
+ readData = totalFrames - offset;
+
+ TS_ASSERT_EQUALS(s->seek(a), true);
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, readData), readData);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine + offset, readData * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ // Seek to 3/4 of a second
+ const Audio::Timestamp b(0, 3, 4);
+ offset = Audio::convertTimeToStreamPos(b, sampleRate, isStereo).totalNumberOfFrames();
+ readData = totalFrames - offset;
+
+ TS_ASSERT_EQUALS(s->seek(b), true);
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, readData), readData);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine + offset, readData * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ // Seek to the start of the stream
+ TS_ASSERT_EQUALS(s->seek(0), true);
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+
+ // Seek to the mid of the stream
+ const Audio::Timestamp c(time * 1000 / 2, 1000);
+ offset = Audio::convertTimeToStreamPos(c, sampleRate, isStereo).totalNumberOfFrames();
+ readData = totalFrames - offset;
+
+ TS_ASSERT_EQUALS(s->seek(c), true);
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, readData), readData);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine + offset, readData * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ // Seek to the 1/4th of the last second of the stream
+ const Audio::Timestamp d(time - 1, 1, 4);
+ offset = Audio::convertTimeToStreamPos(d, sampleRate, isStereo).totalNumberOfFrames();
+ readData = totalFrames - offset;
+
+ TS_ASSERT_EQUALS(s->seek(d), true);
+ TS_ASSERT_EQUALS(s->endOfData(), false);
+ TS_ASSERT_EQUALS(s->readBuffer(buffer, readData), readData);
+ TS_ASSERT_EQUALS(memcmp(buffer, sine + offset, readData * sizeof(int16)), 0);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ // Try to seek after the end of the stream
+ TS_ASSERT_EQUALS(s->seek(Audio::Timestamp(time, 1, 100000)), false);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ // Try to seek exactly at the end of the stream
+ TS_ASSERT_EQUALS(s->seek(Audio::Timestamp(time * 1000, 1000)), true);
+ TS_ASSERT_EQUALS(s->endOfData(), true);
+
+ delete[] sine;
+ delete s;
+ delete[] buffer;
+ }
+
+public:
+ void test_seek_mono() {
+ seekTest(11025, 2, false, false);
+ }
+
+ void test_seek_stereo() {
+ seekTest(11025, 2, true, false);
+ }
+
+ void test_seek_mono_parted() {
+ seekTest(11025, 2, false, true);
+ }
+
+ void test_seek_stereo_parted() {
+ seekTest(11025, 2, true, true);
+ }
+};
diff --git a/test/audio/timestamp.h b/test/audio/timestamp.h
new file mode 100644
index 0000000000..ca56e34a4d
--- /dev/null
+++ b/test/audio/timestamp.h
@@ -0,0 +1,241 @@
+#include <cxxtest/TestSuite.h>
+
+#include "audio/timestamp.h"
+
+class TimestampTestSuite : public CxxTest::TestSuite
+{
+ public:
+ void test_diff_add_frames() {
+ const Audio::Timestamp a(10000, 1000);
+ const Audio::Timestamp b(10001, 1000);
+ const Audio::Timestamp c(10002, 1000);
+
+ TS_ASSERT_EQUALS(a.frameDiff(b), -1);
+ TS_ASSERT_EQUALS(b.frameDiff(a), 1);
+ TS_ASSERT_EQUALS(c.frameDiff(a), 2);
+ TS_ASSERT_EQUALS(b.addFrames(2000).frameDiff(a), 2001);
+ TS_ASSERT_EQUALS(a.frameDiff(b), -1);
+ TS_ASSERT_EQUALS(b.frameDiff(a), 1);
+ TS_ASSERT_EQUALS(c.frameDiff(a), 2);
+ TS_ASSERT_EQUALS(b.addFrames(2000).frameDiff(a.addFrames(-1000)), 3001);
+ TS_ASSERT_EQUALS(a.frameDiff(b), -1);
+ TS_ASSERT_EQUALS(b.frameDiff(a), 1);
+ TS_ASSERT_EQUALS(c.frameDiff(a), 2);
+ }
+
+ void test_diff_add_msecs() {
+ Audio::Timestamp ts0(3, 22050);
+ Audio::Timestamp ts1(0, 22050);
+ Audio::Timestamp ts2(0, 22050);
+
+ TS_ASSERT_EQUALS(ts0.msecs(), 3);
+ TS_ASSERT_EQUALS(ts0.totalNumberOfFrames(), 3 * 22050 / 1000);
+ TS_ASSERT_EQUALS(ts0.numberOfFrames(), 3 * 22050 / 1000);
+
+ ts1 = ts1.addFrames(53248);
+ TS_ASSERT_EQUALS(ts1.secs(), 2);
+ TS_ASSERT_EQUALS(ts1.msecs(), 53248 * 1000 / 22050);
+ TS_ASSERT_EQUALS(ts1.totalNumberOfFrames(), 53248);
+ TS_ASSERT_EQUALS(ts1.numberOfFrames(), 53248 - 2 * 22050);
+ ts1 = ts1.addMsecs(47);
+ TS_ASSERT_EQUALS(ts1.secs(), 2);
+ TS_ASSERT_EQUALS(ts1.msecs(), 2414+47);
+ TS_ASSERT_EQUALS(ts1.totalNumberOfFrames(), 47*22050 / 1000 + 53248);
+ TS_ASSERT_EQUALS(ts1.numberOfFrames(), 47*22050 / 1000 + 53248 - 2 * 22050);
+
+ ts2 = ts2.addMsecs(47);
+ TS_ASSERT_EQUALS(ts2.secs(), 0);
+ TS_ASSERT_EQUALS(ts2.msecs(), 47);
+ TS_ASSERT_EQUALS(ts2.totalNumberOfFrames(), 47*22050 / 1000);
+ TS_ASSERT_EQUALS(ts2.numberOfFrames(), 47*22050 / 1000);
+ ts2 = ts2.addFrames(53248);
+ TS_ASSERT_EQUALS(ts2.secs(), 2);
+ TS_ASSERT_EQUALS(ts2.msecs(), 2414+47);
+ TS_ASSERT_EQUALS(ts2.totalNumberOfFrames(), 47*22050 / 1000 + 53248);
+ TS_ASSERT_EQUALS(ts2.numberOfFrames(), 47*22050 / 1000 + 53248 - 2 * 22050);
+ }
+
+ void test_ticks() {
+ const Audio::Timestamp a(1234, 60);
+ const Audio::Timestamp b(5678, 60);
+
+ TS_ASSERT_EQUALS(a.msecs(), 1234);
+ TS_ASSERT_EQUALS(b.msecs(), 5678);
+
+ TS_ASSERT_EQUALS(a.secs(), 1);
+ TS_ASSERT_EQUALS(b.secs(), 5);
+
+ TS_ASSERT_EQUALS(a.msecsDiff(b), 1234 - 5678);
+ TS_ASSERT_EQUALS(b.msecsDiff(a), 5678 - 1234);
+
+ TS_ASSERT_EQUALS(a.frameDiff(b), (1234 - 5678) * 60 / 1000);
+ TS_ASSERT_EQUALS(b.frameDiff(a), (5678 - 1234) * 60 / 1000);
+
+ TS_ASSERT_EQUALS(a.addFrames(1).msecs(), (1234 + 1000 * 1/60));
+ TS_ASSERT_EQUALS(a.addFrames(59).msecs(), (1234 + 1000 * 59/60));
+ TS_ASSERT_EQUALS(a.addFrames(60).msecs(), (1234 + 1000 * 60/60));
+
+ // As soon as we go back even by only one frame, the msec value
+ // has to drop by at least one.
+ TS_ASSERT_EQUALS(a.addFrames(-1).msecs(), (1234 - 1000 * 1/60 - 1));
+ TS_ASSERT_EQUALS(a.addFrames(-59).msecs(), (1234 - 1000 * 59/60 - 1));
+ TS_ASSERT_EQUALS(a.addFrames(-60).msecs(), (1234 - 1000 * 60/60));
+ }
+
+ void test_more_add_diff() {
+ const Audio::Timestamp c(10002, 1000);
+
+ for (int i = -10000; i < 10000; i++) {
+ int v = c.addFrames(i).frameDiff(c);
+ TS_ASSERT_EQUALS(v, i);
+ }
+ }
+
+ void test_negate() {
+ const Audio::Timestamp a = Audio::Timestamp(0, 60).addFrames(13);
+ const Audio::Timestamp b = -a;
+
+ TS_ASSERT_EQUALS(a.msecs() + 1, -b.msecs());
+ TS_ASSERT_EQUALS(a.totalNumberOfFrames(), -b.totalNumberOfFrames());
+ TS_ASSERT_EQUALS(a.numberOfFrames(), 60 - b.numberOfFrames());
+ }
+
+ void test_add_sub() {
+ const Audio::Timestamp a = Audio::Timestamp(0, 60).addFrames(13);
+ const Audio::Timestamp b = -a;
+ const Audio::Timestamp c = Audio::Timestamp(0, 60).addFrames(20);
+
+ TS_ASSERT_EQUALS((a+a).secs(), 0);
+ TS_ASSERT_EQUALS((a+a).numberOfFrames(), 2*13);
+
+ TS_ASSERT_EQUALS((a+b).secs(), 0);
+ TS_ASSERT_EQUALS((a+b).numberOfFrames(), 0);
+
+ TS_ASSERT_EQUALS((a+c).secs(), 0);
+ TS_ASSERT_EQUALS((a+c).numberOfFrames(), 13+20);
+
+ TS_ASSERT_EQUALS((a-a).secs(), 0);
+ TS_ASSERT_EQUALS((a-a).numberOfFrames(), 0);
+
+ TS_ASSERT_EQUALS((a-b).secs(), 0);
+ TS_ASSERT_EQUALS((a-b).numberOfFrames(), 2*13);
+
+ TS_ASSERT_EQUALS((a-c).secs(), -1);
+ TS_ASSERT_EQUALS((a-c).numberOfFrames(), 60 + (13 - 20));
+ }
+
+ void test_diff_with_conversion() {
+ const Audio::Timestamp a = Audio::Timestamp(10, 1000).addFrames(20);
+ const Audio::Timestamp b = Audio::Timestamp(10, 1000/5).addFrames(20/5);
+ const Audio::Timestamp c = Audio::Timestamp(10, 1000*2).addFrames(20*2);
+
+ TS_ASSERT_EQUALS(a.frameDiff(a), 0);
+ TS_ASSERT_EQUALS(a.frameDiff(b), 0);
+ TS_ASSERT_EQUALS(a.frameDiff(c), 0);
+
+ TS_ASSERT_EQUALS(b.frameDiff(a), 0);
+ TS_ASSERT_EQUALS(b.frameDiff(b), 0);
+ TS_ASSERT_EQUALS(b.frameDiff(c), 0);
+
+ TS_ASSERT_EQUALS(c.frameDiff(a), 0);
+ TS_ASSERT_EQUALS(c.frameDiff(b), 0);
+ TS_ASSERT_EQUALS(c.frameDiff(c), 0);
+ }
+
+
+ void test_convert() {
+ const Audio::Timestamp a = Audio::Timestamp(10, 1000).addFrames(20);
+ const Audio::Timestamp b = Audio::Timestamp(10, 1000/5).addFrames(20/5);
+ const Audio::Timestamp c = Audio::Timestamp(10, 1000*2).addFrames(20*2);
+
+ TS_ASSERT_EQUALS(a.convertToFramerate(1000/5), b);
+ TS_ASSERT_EQUALS(a.convertToFramerate(1000*2), c);
+ }
+
+ void test_equals() {
+ const Audio::Timestamp a = Audio::Timestamp(500, 1000);
+ Audio::Timestamp b = Audio::Timestamp(0, 1000);
+ Audio::Timestamp c = Audio::Timestamp(0, 100);
+
+ TS_ASSERT_EQUALS(a, Audio::Timestamp(0, 500, 1000));
+
+ TS_ASSERT(a != b);
+ TS_ASSERT(a != c);
+ TS_ASSERT(b == c);
+
+ b = b.addFrames(500);
+ c = c.addFrames(50);
+
+ TS_ASSERT(a == b);
+ TS_ASSERT(a == c);
+ TS_ASSERT(b == c);
+ }
+
+
+ void test_compare() {
+ const Audio::Timestamp a = Audio::Timestamp(60, 1000);
+ Audio::Timestamp b = Audio::Timestamp(60, 60);
+ Audio::Timestamp c = Audio::Timestamp(60, 44100);
+
+ TS_ASSERT(a <= b);
+ TS_ASSERT(b <= c);
+ TS_ASSERT(a <= c);
+
+ TS_ASSERT(b >= a);
+ TS_ASSERT(c >= b);
+ TS_ASSERT(c >= a);
+
+ b = b.addFrames(60 / 12);
+ c = c.addFrames(44100 / 10);
+
+ TS_ASSERT(a < b);
+ TS_ASSERT(b < c);
+ TS_ASSERT(a < c);
+
+ TS_ASSERT(b > a);
+ TS_ASSERT(c > b);
+ TS_ASSERT(c > a);
+
+ TS_ASSERT(a <= b);
+ TS_ASSERT(b <= c);
+ TS_ASSERT(a <= c);
+
+ TS_ASSERT(b >= a);
+ TS_ASSERT(c >= b);
+ TS_ASSERT(c >= a);
+ }
+
+
+ void test_framerate() {
+ const Audio::Timestamp a = Audio::Timestamp(500, 1000);
+ const Audio::Timestamp b = Audio::Timestamp(500, 67);
+ const Audio::Timestamp c = Audio::Timestamp(500, 100);
+ const Audio::Timestamp d = Audio::Timestamp(500, 44100);
+
+ TS_ASSERT_EQUALS(a.framerate(), (uint)1000);
+ TS_ASSERT_EQUALS(b.framerate(), (uint)67);
+ TS_ASSERT_EQUALS(c.framerate(), (uint)100);
+ TS_ASSERT_EQUALS(d.framerate(), (uint)44100);
+ }
+
+ void test_direct_query() {
+ const Audio::Timestamp a = Audio::Timestamp(0, 22050);
+ const Audio::Timestamp b = a.addFrames(11025);
+ const Audio::Timestamp c = Audio::Timestamp(1500, 22050);
+
+ TS_ASSERT_EQUALS(a.secs(), 0);
+ TS_ASSERT_EQUALS(a.msecs(), 0);
+ TS_ASSERT_EQUALS(a.numberOfFrames(), 0);
+ TS_ASSERT_EQUALS(a.totalNumberOfFrames(), 0);
+
+ TS_ASSERT_EQUALS(b.secs(), 0);
+ TS_ASSERT_EQUALS(b.msecs(), 500);
+ TS_ASSERT_EQUALS(b.numberOfFrames(), 11025);
+ TS_ASSERT_EQUALS(b.totalNumberOfFrames(), 11025);
+
+ TS_ASSERT_EQUALS(c.secs(), 1);
+ TS_ASSERT_EQUALS(c.msecs(), 1500);
+ TS_ASSERT_EQUALS(c.numberOfFrames(), 11025);
+ TS_ASSERT_EQUALS(c.totalNumberOfFrames(), 33075);
+ }
+};