Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

208
test/audio/audiostream.h Normal file
View File

@@ -0,0 +1,208 @@
#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);
}
};

68
test/audio/helper.h Normal file
View File

@@ -0,0 +1,68 @@
#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 * M_PI) * maxValue)) ^ xorMask;
return sine;
}
template<typename T>
static Audio::SeekableAudioStream *createSineStream(const int sampleRate, const int time, int16 **comp, bool le, bool isStereo) {
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;
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

291
test/audio/raw.h Normal file
View File

@@ -0,0 +1,291 @@
#include <cxxtest/TestSuite.h>
#include "audio/decoders/raw.h"
#include "audio/audiostream.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) {
int16 *sine;
Audio::SeekableAudioStream *s = createSineStream<int8>(sampleRate, time, &sine, le, isStereo);
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);
}
private:
void partialReadTest() {
const int sampleRate = 11025;
const int time = 4;
int16 *sine;
Audio::SeekableAudioStream *s = createSineStream<int8>(sampleRate, time, &sine, false, false);
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();
}
private:
void readAfterEndTest() {
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();
}
private:
void rewindTest() {
const int sampleRate = 11025;
const int time = 2;
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);
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();
}
private:
void lengthTest() {
int sampleRate = 0;
const int time = 4;
Audio::SeekableAudioStream *s = 0;
// 11025 Hz tests
sampleRate = 11025;
s = createSineStream<int8>(sampleRate, time, 0, false, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
s = createSineStream<uint16>(sampleRate, time, 0, false, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
// 48000 Hz tests
sampleRate = 48000;
s = createSineStream<int8>(sampleRate, time, 0, false, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
s = createSineStream<uint16>(sampleRate, time, 0, true, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
// 11840 Hz tests
sampleRate = 11840;
s = createSineStream<int8>(sampleRate, time, 0, false, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
s = createSineStream<uint16>(sampleRate, time, 0, false, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
// 11111 Hz tests
sampleRate = 11111;
s = createSineStream<int8>(sampleRate, time, 0, false, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
s = createSineStream<uint16>(sampleRate, time, 0, false, false);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
// 22050 Hz stereo test
sampleRate = 22050;
s = createSineStream<int8>(sampleRate, time, 0, false, true);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
s = createSineStream<uint16>(sampleRate, time, 0, true, true);
TS_ASSERT_EQUALS(s->getLength().totalNumberOfFrames(), sampleRate * time);
delete s;
}
public:
void test_length() {
lengthTest();
}
private:
void seekTest(const int sampleRate, const int time, const bool isStereo) {
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);
// 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);
}
void test_seek_stereo() {
seekTest(11025, 2, true);
}
};

252
test/audio/timestamp.h Normal file
View File

@@ -0,0 +1,252 @@
#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);
}
void test_no_overflow() {
// The constructor should not overflow and give incoherent values
const Audio::Timestamp a = Audio::Timestamp(0, UINT_MAX, 1000);
int secs = UINT_MAX / 1000;
int frames = UINT_MAX % 1000;
TS_ASSERT_EQUALS(a.secs(), secs);
TS_ASSERT_EQUALS(a.numberOfFrames(), frames);
}
};