diff options
Diffstat (limited to 'test/audio')
-rw-r--r-- | test/audio/audiostream.h | 209 | ||||
-rw-r--r-- | test/audio/helper.h | 112 | ||||
-rw-r--r-- | test/audio/raw.h | 358 | ||||
-rw-r--r-- | test/audio/timestamp.h | 241 |
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); + } +}; |