SoundTest.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. 
  2. #include "../testTools.h"
  3. #include "../../DFPSR/api/soundAPI.h"
  4. #include "../../DFPSR/api/fileAPI.h"
  5. #include "../../DFPSR/api/randomAPI.h"
  6. #include <math.h>
  7. void compareSoundBuffers(const SoundBuffer &given, const SoundBuffer &expected, float tolerance) {
  8. ASSERT_EQUAL(sound_getSamplesPerChannel(given), sound_getSamplesPerChannel(expected));
  9. ASSERT_EQUAL(sound_getChannelCount(given), sound_getChannelCount(expected));
  10. ASSERT_EQUAL(sound_getSampleRate(given), sound_getSampleRate(expected));
  11. SafePointer<float> givenSamples = sound_getSafePointer(given);
  12. SafePointer<float> expectedSamples = sound_getSafePointer(expected);
  13. float maxGiven = 0.0f;
  14. float minGiven = 0.0f;
  15. float maxExpected = 0.0f;
  16. float minExpected = 0.0f;
  17. float maxOffset = -1000000.0f;
  18. float minOffset = 1000000.0f;
  19. for (int t = 0; t < sound_getSamplesPerChannel(given); t++) {
  20. float givenValue = givenSamples[t];
  21. float expectedValue = expectedSamples[t];
  22. float offset = givenSamples[t] - expectedSamples[t];
  23. if (offset > maxOffset) maxOffset = offset;
  24. if (offset < minOffset) minOffset = offset;
  25. if (givenValue > maxGiven) maxGiven = givenValue;
  26. if (givenValue < minGiven) minGiven = givenValue;
  27. if (expectedValue > maxExpected) maxExpected = expectedValue;
  28. if (expectedValue < minExpected) minExpected = expectedValue;
  29. }
  30. ASSERT_LESSER(maxOffset, tolerance);
  31. ASSERT_GREATER(minOffset, -tolerance);
  32. }
  33. static const double pi = 3.1415926535897932384626433832795;
  34. static const double cyclesToRadians = pi * 2.0;
  35. // TODO: Implement basic sound generation functions and move them to the sound API.
  36. // Both in-place functions and allocating new buffers as needed to expand.
  37. // Generation functions, multiplying masks, fade masks, echo effects, frequency filters, equalization, resampling...
  38. void sound_setNoise(SoundBuffer &sound, float minimum = -1.0f, float maximum = 1.0f) {
  39. RandomGenerator generator = random_createGenerator(917542);
  40. SafePointer<float> target = sound_getSafePointer(sound);
  41. intptr_t totalSamples = sound_getSamplesPerChannel(sound) * sound_getChannelCount(sound);
  42. for (intptr_t s = 0; s < totalSamples; s++) {
  43. target[s] = random_generate_range(generator, minimum, maximum);
  44. }
  45. }
  46. START_TEST(Sound)
  47. String folderPath = file_combinePaths(U".", U"resources", U"sounds");
  48. // Check that we have a valid folder path to the resources.
  49. ASSERT_EQUAL(file_getEntryType(folderPath), EntryType::Folder);
  50. { // Single channel wave files.
  51. // Generate the reference sine wave.
  52. static const int sampleRate = 44100;
  53. static const int samplesPerChannel = 441;
  54. static const int frequency = 100;
  55. SoundBuffer referenceSine = sound_create(samplesPerChannel, 1, sampleRate);
  56. SafePointer<float> target = sound_getSafePointer(referenceSine);
  57. static const double radiansPerElement = cyclesToRadians * (double)frequency / (double)sampleRate;
  58. for (int t = 0; t < samplesPerChannel; t++) {
  59. target[t] = (float)sin(double(t) * radiansPerElement);
  60. }
  61. // Load wave files that were exported from a 10 millisecond sine wave in the Audacity sound editor for reference.
  62. // Because Audacity truncates towards zero instead of rounding to nearest, the worst case accuracy has twice the error.
  63. SoundBuffer sineU8 = sound_load(file_combinePaths(folderPath, U"SineU8.wav"));
  64. ASSERT(sound_exists(sineU8));
  65. compareSoundBuffers(sineU8, referenceSine, 0.02f);
  66. SoundBuffer sineI16 = sound_load(file_combinePaths(folderPath, U"SineI16.wav"));
  67. ASSERT(sound_exists(sineI16));
  68. compareSoundBuffers(sineI16, referenceSine, 0.0002f);
  69. SoundBuffer sineI24 = sound_load(file_combinePaths(folderPath, U"SineI24.wav"));
  70. ASSERT(sound_exists(sineI24));
  71. compareSoundBuffers(sineI24, referenceSine, 0.000002f);
  72. SoundBuffer sineI32 = sound_load(file_combinePaths(folderPath, U"SineI32.wav"));
  73. ASSERT(sound_exists(sineI32));
  74. compareSoundBuffers(sineI32, referenceSine, 0.00000001f);
  75. SoundBuffer sineF32 = sound_load(file_combinePaths(folderPath, U"SineF32.wav"));
  76. ASSERT(sound_exists(sineF32));
  77. compareSoundBuffers(sineF32, referenceSine, 0.00000001f);
  78. SoundBuffer sineF64 = sound_load(file_combinePaths(folderPath, U"SineF64.wav"));
  79. ASSERT(sound_exists(sineF64));
  80. compareSoundBuffers(sineF64, referenceSine, 0.00000001f);
  81. //sound_save_RiffWave(file_combinePaths(folderPath, U"SineI16_2.wav"), sineI16, RiffWaveFormat::RawI16);
  82. //sound_save_RiffWave(file_combinePaths(folderPath, U"SineI24_2.wav"), sineI24, RiffWaveFormat::RawI24);
  83. //sound_save_RiffWave(file_combinePaths(folderPath, U"SineI32_2.wav"), sineI32, RiffWaveFormat::RawI24);
  84. }
  85. { // Brute-force encode and decode test with random noise.
  86. RandomGenerator generator = random_createGenerator(12345);
  87. for (uint32_t channelCount = 1; channelCount <= 16; channelCount++) {
  88. SoundBuffer original = sound_create(1024, channelCount, 44100);
  89. sound_setNoise(original);
  90. Buffer encodedU8_truncate = sound_encode_RiffWave(original, RiffWaveFormat::RawU8 , RoundingMethod::Truncate);
  91. Buffer encodedU8_nearest = sound_encode_RiffWave(original, RiffWaveFormat::RawU8 , RoundingMethod::Nearest);
  92. Buffer encodedI16_truncate = sound_encode_RiffWave(original, RiffWaveFormat::RawI16, RoundingMethod::Truncate);
  93. Buffer encodedI16_nearest = sound_encode_RiffWave(original, RiffWaveFormat::RawI16, RoundingMethod::Nearest);
  94. Buffer encodedI24_nearest = sound_encode_RiffWave(original, RiffWaveFormat::RawI24, RoundingMethod::Nearest);
  95. Buffer encodedI32_nearest = sound_encode_RiffWave(original, RiffWaveFormat::RawI32, RoundingMethod::Nearest);
  96. SoundBuffer decodedU8_truncate = sound_decode_RiffWave(encodedU8_truncate);
  97. SoundBuffer decodedU8_nearest = sound_decode_RiffWave(encodedU8_nearest );
  98. SoundBuffer decodedI16_truncate = sound_decode_RiffWave(encodedI16_truncate);
  99. SoundBuffer decodedI16_nearest = sound_decode_RiffWave(encodedI16_nearest);
  100. SoundBuffer decodedI24_nearest = sound_decode_RiffWave(encodedI24_nearest);
  101. SoundBuffer decodedI32_nearest = sound_decode_RiffWave(encodedI32_nearest);
  102. compareSoundBuffers(decodedU8_truncate , original, 2.02 / 256.0);
  103. compareSoundBuffers(decodedU8_nearest , original, 1.01 / 256.0);
  104. compareSoundBuffers(decodedI16_truncate, original, 2.02 / 65536.0);
  105. compareSoundBuffers(decodedI16_nearest , original, 1.01 / 65536.0);
  106. compareSoundBuffers(decodedI24_nearest , original, 1.01 / 8388608.0);
  107. compareSoundBuffers(decodedI32_nearest , original, 1.01 / 2147483648.0);
  108. if (failed) break;
  109. }
  110. }
  111. // TODO: Test saving sounds to files.
  112. END_TEST