Merge branch 'feature/101-wave-file-reader-improvements'
This commit is contained in:
commit
c4317245b1
|
@ -1,63 +1,7 @@
|
||||||
###############################################################################
|
|
||||||
# Set default behavior to automatically normalize line endings.
|
|
||||||
###############################################################################
|
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
###############################################################################
|
# Use Git LFS for binary files
|
||||||
# Set default behavior for command prompt diff.
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
#
|
*.flac filter=lfs diff=lfs merge=lfs -text
|
||||||
# This is need for earlier builds of msysgit that does not have it on by
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
# default for csharp files.
|
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||||
# Note: This is only used by command line
|
|
||||||
###############################################################################
|
|
||||||
#*.cs diff=csharp
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set the merge driver for project and solution files
|
|
||||||
#
|
|
||||||
# Merging from the command prompt will add diff markers to the files if there
|
|
||||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
|
||||||
# the diff markers are never inserted). Diff markers may cause the following
|
|
||||||
# file extensions to fail to load in VS. An alternative would be to treat
|
|
||||||
# these files as binary and thus will always conflict and require user
|
|
||||||
# intervention with every merge. To do so, just uncomment the entries below
|
|
||||||
###############################################################################
|
|
||||||
#*.sln merge=binary
|
|
||||||
#*.csproj merge=binary
|
|
||||||
#*.vbproj merge=binary
|
|
||||||
#*.vcxproj merge=binary
|
|
||||||
#*.vcproj merge=binary
|
|
||||||
#*.dbproj merge=binary
|
|
||||||
#*.fsproj merge=binary
|
|
||||||
#*.lsproj merge=binary
|
|
||||||
#*.wixproj merge=binary
|
|
||||||
#*.modelproj merge=binary
|
|
||||||
#*.sqlproj merge=binary
|
|
||||||
#*.wwaproj merge=binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# behavior for image files
|
|
||||||
#
|
|
||||||
# image files are treated as binary by default.
|
|
||||||
###############################################################################
|
|
||||||
#*.jpg binary
|
|
||||||
#*.png binary
|
|
||||||
#*.gif binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# diff behavior for common document formats
|
|
||||||
#
|
|
||||||
# Convert binary document formats to text before diffing them. This feature
|
|
||||||
# is only available from the command line. Turn it on by uncommenting the
|
|
||||||
# entries below.
|
|
||||||
###############################################################################
|
|
||||||
#*.doc diff=astextplain
|
|
||||||
#*.DOC diff=astextplain
|
|
||||||
#*.docx diff=astextplain
|
|
||||||
#*.DOCX diff=astextplain
|
|
||||||
#*.dot diff=astextplain
|
|
||||||
#*.DOT diff=astextplain
|
|
||||||
#*.pdf diff=astextplain
|
|
||||||
#*.PDF diff=astextplain
|
|
||||||
#*.rtf diff=astextplain
|
|
||||||
#*.RTF diff=astextplain
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
- name: Restore Boost from cache
|
- name: Restore Boost from cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
id: cache-boost
|
id: cache-boost
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
* **Added** support for more WAVE file features ([issue #101](https://github.com/DanielSWolf/rhubarb-lip-sync/issues/101))
|
||||||
* **Changed** Rhubarb Lip Sync for Spine so that it works with any modern JRE ([issue #97](https://github.com/DanielSWolf/rhubarb-lip-sync/issues/97))
|
* **Changed** Rhubarb Lip Sync for Spine so that it works with any modern JRE ([issue #97](https://github.com/DanielSWolf/rhubarb-lip-sync/issues/97))
|
||||||
* **Changed** Windows build from 32 bit to 64 bit ([issue #98](https://github.com/DanielSWolf/rhubarb-lip-sync/issues/98))
|
* **Changed** Windows build from 32 bit to 64 bit ([issue #98](https://github.com/DanielSWolf/rhubarb-lip-sync/issues/98))
|
||||||
|
|
||||||
|
|
|
@ -520,6 +520,7 @@ set(TEST_FILES
|
||||||
tests/tokenizationTests.cpp
|
tests/tokenizationTests.cpp
|
||||||
tests/g2pTests.cpp
|
tests/g2pTests.cpp
|
||||||
tests/LazyTests.cpp
|
tests/LazyTests.cpp
|
||||||
|
tests/WaveFileReaderTests.cpp
|
||||||
)
|
)
|
||||||
add_executable(runTests ${TEST_FILES})
|
add_executable(runTests ${TEST_FILES})
|
||||||
target_link_libraries(runTests
|
target_link_libraries(runTests
|
||||||
|
@ -528,6 +529,7 @@ target_link_libraries(runTests
|
||||||
gmock_main
|
gmock_main
|
||||||
rhubarb-recognition
|
rhubarb-recognition
|
||||||
rhubarb-time
|
rhubarb-time
|
||||||
|
rhubarb-audio
|
||||||
)
|
)
|
||||||
|
|
||||||
# Copies the specified files in a post-build event, then installs them
|
# Copies the specified files in a post-build event, then installs them
|
||||||
|
@ -555,9 +557,30 @@ function(copy_and_install sourceGlob relativeTargetDirectory)
|
||||||
endforeach()
|
endforeach()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# Copies the specified files in a post-build event
|
||||||
|
function(copy sourceGlob relativeTargetDirectory)
|
||||||
|
# Set `sourcePaths`
|
||||||
|
file(GLOB sourcePaths "${sourceGlob}")
|
||||||
|
|
||||||
|
foreach(sourcePath ${sourcePaths})
|
||||||
|
if(NOT IS_DIRECTORY ${sourcePath})
|
||||||
|
# Set `fileName`
|
||||||
|
get_filename_component(fileName "${sourcePath}" NAME)
|
||||||
|
|
||||||
|
# Copy file during build
|
||||||
|
add_custom_command(TARGET rhubarb POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy "${sourcePath}" "$<TARGET_FILE_DIR:rhubarb>/${relativeTargetDirectory}/${fileName}"
|
||||||
|
COMMENT "Creating '${relativeTargetDirectory}/${fileName}'"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
copy_and_install("lib/pocketsphinx-rev13216/model/en-us/*" "res/sphinx")
|
copy_and_install("lib/pocketsphinx-rev13216/model/en-us/*" "res/sphinx")
|
||||||
copy_and_install("lib/cmusphinx-en-us-5.2/*" "res/sphinx/acoustic-model")
|
copy_and_install("lib/cmusphinx-en-us-5.2/*" "res/sphinx/acoustic-model")
|
||||||
|
|
||||||
|
copy_and_install("tests/resources/*" "tests/resources")
|
||||||
|
|
||||||
install(
|
install(
|
||||||
TARGETS rhubarb
|
TARGETS rhubarb
|
||||||
RUNTIME
|
RUNTIME
|
||||||
|
|
|
@ -13,38 +13,42 @@ using std::unique_ptr;
|
||||||
using std::make_unique;
|
using std::make_unique;
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
using std::filesystem::path;
|
using std::filesystem::path;
|
||||||
|
using std::streamoff;
|
||||||
|
|
||||||
#define INT24_MIN (-8388608)
|
#define INT24_MIN (-8388608)
|
||||||
#define INT24_MAX 8388607
|
#define INT24_MAX 8388607
|
||||||
|
|
||||||
// Converts an int in the range min..max to a float in the range -1..1
|
// Converts an int in the range min..max to a float in the range -1..1
|
||||||
float toNormalizedFloat(int value, int min, int max) {
|
float toNormalizedFloat(int value, int min, int max) {
|
||||||
return (static_cast<float>(value - min) / (max - min) * 2) - 1;
|
const float fMin = static_cast<float>(min);
|
||||||
|
const float fMax = static_cast<float>(max);
|
||||||
|
const float fValue = static_cast<float>(value);
|
||||||
|
return ((fValue - fMin) / (fMax - fMin) * 2) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int roundToEven(int i) {
|
streamoff roundUpToEven(streamoff i) {
|
||||||
return (i + 1) & (~1);
|
return (i + 1) & (~1);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Codec {
|
namespace Codec {
|
||||||
constexpr int Pcm = 0x01;
|
constexpr int Pcm = 0x01;
|
||||||
constexpr int Float = 0x03;
|
constexpr int Float = 0x03;
|
||||||
|
constexpr int Extensible = 0xFFFE;
|
||||||
};
|
};
|
||||||
|
|
||||||
string codecToString(int codec);
|
string codecToString(int codec);
|
||||||
|
|
||||||
WaveFileReader::WaveFileReader(const path& filePath) :
|
WaveFormatInfo getWaveFormatInfo(const path& filePath) {
|
||||||
filePath(filePath),
|
WaveFormatInfo formatInfo {};
|
||||||
formatInfo {}
|
|
||||||
{
|
|
||||||
auto file = openFile(filePath);
|
auto file = openFile(filePath);
|
||||||
|
|
||||||
file.seekg(0, std::ios_base::end);
|
file.seekg(0, std::ios_base::end);
|
||||||
std::streamoff fileSize = file.tellg();
|
const streamoff fileSize = file.tellg();
|
||||||
file.seekg(0);
|
file.seekg(0);
|
||||||
|
|
||||||
auto remaining = [&](int byteCount) {
|
auto remaining = [&](int byteCount) {
|
||||||
const std::streamoff filePosition = file.tellg();
|
const streamoff filePosition = file.tellg();
|
||||||
return byteCount <= fileSize - filePosition;
|
return byteCount <= fileSize - filePosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,34 +56,46 @@ WaveFileReader::WaveFileReader(const path& filePath) :
|
||||||
if (!remaining(10)) {
|
if (!remaining(10)) {
|
||||||
throw runtime_error("WAVE file is corrupt. Header not found.");
|
throw runtime_error("WAVE file is corrupt. Header not found.");
|
||||||
}
|
}
|
||||||
auto rootChunkId = read<uint32_t>(file);
|
const auto rootChunkId = read<uint32_t>(file);
|
||||||
if (rootChunkId != fourcc('R', 'I', 'F', 'F')) {
|
if (rootChunkId != fourcc('R', 'I', 'F', 'F')) {
|
||||||
throw runtime_error("Unknown file format. Only WAVE files are supported.");
|
throw runtime_error("Unknown file format. Only WAVE files are supported.");
|
||||||
}
|
}
|
||||||
read<uint32_t>(file); // Chunk size
|
read<uint32_t>(file); // Chunk size
|
||||||
uint32_t waveId = read<uint32_t>(file);
|
const uint32_t waveId = read<uint32_t>(file);
|
||||||
if (waveId != fourcc('W', 'A', 'V', 'E')) {
|
if (waveId != fourcc('W', 'A', 'V', 'E')) {
|
||||||
throw runtime_error(format("File format is not WAVE, but {}.", fourccToString(waveId)));
|
throw runtime_error(format("File format is not WAVE, but {}.", fourccToString(waveId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read chunks until we reach the data chunk
|
// Read chunks until we reach the data chunk
|
||||||
bool reachedDataChunk = false;
|
bool processedFormatChunk = false;
|
||||||
while (!reachedDataChunk && remaining(8)) {
|
bool processedDataChunk = false;
|
||||||
uint32_t chunkId = read<uint32_t>(file);
|
while ((!processedFormatChunk || !processedDataChunk) && remaining(8)) {
|
||||||
int chunkSize = read<uint32_t>(file);
|
const uint32_t chunkId = read<uint32_t>(file);
|
||||||
|
const streamoff chunkSize = read<int32_t>(file);
|
||||||
|
const streamoff chunkEnd = roundUpToEven(file.tellg() + chunkSize);
|
||||||
switch (chunkId) {
|
switch (chunkId) {
|
||||||
case fourcc('f', 'm', 't', ' '):
|
case fourcc('f', 'm', 't', ' '):
|
||||||
{
|
{
|
||||||
// Read relevant data
|
// Read relevant data
|
||||||
uint16_t codec = read<uint16_t>(file);
|
uint16_t codec = read<uint16_t>(file);
|
||||||
formatInfo.channelCount = read<uint16_t>(file);
|
formatInfo.channelCount = read<uint16_t>(file);
|
||||||
formatInfo.frameRate = read<uint32_t>(file);
|
formatInfo.frameRate = read<int32_t>(file);
|
||||||
read<uint32_t>(file); // Bytes per second
|
read<uint32_t>(file); // Bytes per second
|
||||||
int frameSize = read<uint16_t>(file);
|
const int bytesPerFrame = read<uint16_t>(file);
|
||||||
int bitsPerSample = read<uint16_t>(file);
|
const int bitsPerSampleOnDisk = read<uint16_t>(file);
|
||||||
|
int bitsPerSample = bitsPerSampleOnDisk;
|
||||||
// We've read 16 bytes so far. Skip the remainder.
|
if (chunkSize > 16) {
|
||||||
file.seekg(roundToEven(chunkSize) - 16, std::ios_base::cur);
|
const int extensionSize = read<uint16_t>(file);
|
||||||
|
if (extensionSize >= 22) {
|
||||||
|
// Read extension fields
|
||||||
|
bitsPerSample = read<uint16_t>(file);
|
||||||
|
read<uint32_t>(file); // Skip channel mask
|
||||||
|
const uint16_t codecOverride = read<uint16_t>(file);
|
||||||
|
if (codec == Codec::Extensible) {
|
||||||
|
codec = codecOverride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Determine sample format
|
// Determine sample format
|
||||||
int bytesPerSample;
|
int bytesPerSample;
|
||||||
|
@ -97,11 +113,14 @@ WaveFileReader::WaveFileReader(const path& filePath) :
|
||||||
} else if (bitsPerSample <= 24) {
|
} else if (bitsPerSample <= 24) {
|
||||||
formatInfo.sampleFormat = SampleFormat::Int24;
|
formatInfo.sampleFormat = SampleFormat::Int24;
|
||||||
bytesPerSample = 3;
|
bytesPerSample = 3;
|
||||||
|
} else if (bitsPerSample <= 32) {
|
||||||
|
formatInfo.sampleFormat = SampleFormat::Int32;
|
||||||
|
bytesPerSample = 4;
|
||||||
} else {
|
} else {
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
format("Unsupported sample format: {}-bit PCM.", bitsPerSample));
|
format("Unsupported sample format: {}-bit PCM.", bitsPerSample));
|
||||||
}
|
}
|
||||||
if (bytesPerSample != frameSize / formatInfo.channelCount) {
|
if (bytesPerSample != bytesPerFrame / formatInfo.channelCount) {
|
||||||
throw runtime_error("Unsupported sample organization.");
|
throw runtime_error("Unsupported sample organization.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -109,6 +128,9 @@ WaveFileReader::WaveFileReader(const path& filePath) :
|
||||||
if (bitsPerSample == 32) {
|
if (bitsPerSample == 32) {
|
||||||
formatInfo.sampleFormat = SampleFormat::Float32;
|
formatInfo.sampleFormat = SampleFormat::Float32;
|
||||||
bytesPerSample = 4;
|
bytesPerSample = 4;
|
||||||
|
} else if (bitsPerSample == 64) {
|
||||||
|
formatInfo.sampleFormat = SampleFormat::Float64;
|
||||||
|
bytesPerSample = 8;
|
||||||
} else {
|
} else {
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
format("Unsupported sample format: {}-bit IEEE Float.", bitsPerSample)
|
format("Unsupported sample format: {}-bit IEEE Float.", bitsPerSample)
|
||||||
|
@ -122,25 +144,37 @@ WaveFileReader::WaveFileReader(const path& filePath) :
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
formatInfo.bytesPerFrame = bytesPerSample * formatInfo.channelCount;
|
formatInfo.bytesPerFrame = bytesPerSample * formatInfo.channelCount;
|
||||||
|
processedFormatChunk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case fourcc('d', 'a', 't', 'a'):
|
case fourcc('d', 'a', 't', 'a'):
|
||||||
{
|
{
|
||||||
reachedDataChunk = true;
|
|
||||||
formatInfo.dataOffset = file.tellg();
|
formatInfo.dataOffset = file.tellg();
|
||||||
formatInfo.frameCount = chunkSize / formatInfo.bytesPerFrame;
|
formatInfo.frameCount = chunkSize / formatInfo.bytesPerFrame;
|
||||||
|
processedDataChunk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
// Skip unknown chunk
|
// Ignore unknown chunk
|
||||||
file.seekg(roundToEven(chunkSize), std::ios_base::cur);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seek to end of chunk
|
||||||
|
file.seekg(chunkEnd, std::ios_base::beg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!processedFormatChunk) throw runtime_error("Missing format chunk.");
|
||||||
|
if (!processedDataChunk) throw runtime_error("Missing data chunk.");
|
||||||
|
|
||||||
|
return formatInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WaveFileReader::WaveFileReader(const path& filePath) :
|
||||||
|
filePath(filePath),
|
||||||
|
formatInfo(getWaveFormatInfo(filePath)) {}
|
||||||
|
|
||||||
unique_ptr<AudioClip> WaveFileReader::clone() const {
|
unique_ptr<AudioClip> WaveFileReader::clone() const {
|
||||||
return make_unique<WaveFileReader>(*this);
|
return make_unique<WaveFileReader>(*this);
|
||||||
}
|
}
|
||||||
|
@ -172,11 +206,22 @@ inline AudioClip::value_type readSample(
|
||||||
sum += toNormalizedFloat(raw, INT24_MIN, INT24_MAX);
|
sum += toNormalizedFloat(raw, INT24_MIN, INT24_MAX);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SampleFormat::Int32:
|
||||||
|
{
|
||||||
|
const int32_t raw = read<int32_t>(file);
|
||||||
|
sum += toNormalizedFloat(raw, INT32_MIN, INT32_MAX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SampleFormat::Float32:
|
case SampleFormat::Float32:
|
||||||
{
|
{
|
||||||
sum += read<float>(file);
|
sum += read<float>(file);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SampleFormat::Float64:
|
||||||
|
{
|
||||||
|
sum += static_cast<float>(read<double>(file));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,13 +236,13 @@ SampleReader WaveFileReader::createUnsafeSampleReader() const {
|
||||||
filePos = std::streampos(0)
|
filePos = std::streampos(0)
|
||||||
](size_type index) mutable {
|
](size_type index) mutable {
|
||||||
const std::streampos newFilePos = formatInfo.dataOffset
|
const std::streampos newFilePos = formatInfo.dataOffset
|
||||||
+ static_cast<std::streamoff>(index * formatInfo.bytesPerFrame);
|
+ static_cast<streamoff>(index * formatInfo.bytesPerFrame);
|
||||||
if (newFilePos != filePos) {
|
if (newFilePos != filePos) {
|
||||||
file->seekg(newFilePos);
|
file->seekg(newFilePos);
|
||||||
}
|
}
|
||||||
const value_type result =
|
const value_type result =
|
||||||
readSample(*file, formatInfo.sampleFormat, formatInfo.channelCount);
|
readSample(*file, formatInfo.sampleFormat, formatInfo.channelCount);
|
||||||
filePos = newFilePos + static_cast<std::streamoff>(formatInfo.bytesPerFrame);
|
filePos = newFilePos + static_cast<streamoff>(formatInfo.bytesPerFrame);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,22 @@ enum class SampleFormat {
|
||||||
UInt8,
|
UInt8,
|
||||||
Int16,
|
Int16,
|
||||||
Int24,
|
Int24,
|
||||||
Float32
|
Int32,
|
||||||
|
Float32,
|
||||||
|
Float64
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WaveFormatInfo {
|
||||||
|
int bytesPerFrame;
|
||||||
|
SampleFormat sampleFormat;
|
||||||
|
int frameRate;
|
||||||
|
int64_t frameCount;
|
||||||
|
int channelCount;
|
||||||
|
std::streampos dataOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
WaveFormatInfo getWaveFormatInfo(const std::filesystem::path& filePath);
|
||||||
|
|
||||||
class WaveFileReader : public AudioClip {
|
class WaveFileReader : public AudioClip {
|
||||||
public:
|
public:
|
||||||
WaveFileReader(const std::filesystem::path& filePath);
|
WaveFileReader(const std::filesystem::path& filePath);
|
||||||
|
@ -20,15 +33,6 @@ public:
|
||||||
private:
|
private:
|
||||||
SampleReader createUnsafeSampleReader() const override;
|
SampleReader createUnsafeSampleReader() const override;
|
||||||
|
|
||||||
struct WaveFormatInfo {
|
|
||||||
int bytesPerFrame;
|
|
||||||
SampleFormat sampleFormat;
|
|
||||||
int frameRate;
|
|
||||||
int64_t frameCount;
|
|
||||||
int channelCount;
|
|
||||||
std::streampos dataOffset;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::filesystem::path filePath;
|
std::filesystem::path filePath;
|
||||||
WaveFormatInfo formatInfo;
|
WaveFormatInfo formatInfo;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include "audio/WaveFileReader.h"
|
||||||
|
#include "tools/platformTools.h"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, float32FromAudacity) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-audacity.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Float32);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 4);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 88);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, float32FromAudition) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-audition.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Float32);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 4);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 92);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, float32FromFfmpeg) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-ffmpeg.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Float32);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 4);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 114);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, float32FromSoundforge) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float32-soundforge.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Float32);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 4);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, float64FromFfmpeg) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-float64-ffmpeg.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Float64);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 8);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 114);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int16FromAudacity) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-audacity.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int16);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 2);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int16FromAudition) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-audition.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int16);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 2);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 92);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int16FromFfmpeg) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-ffmpeg.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int16);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 2);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 78);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int16FromSoundforge) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int16-soundforge.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int16);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 2);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int24FromAudacity) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-audacity.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int24);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 3);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int24FromAudition) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-audition.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int24);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 3);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 92);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int24FromFfmpeg) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-ffmpeg.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int24);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 3);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 102);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int24FromSoundforge) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int24-soundforge.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int24);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 3);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int32FromFfmpeg) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int32-ffmpeg.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int32);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 4);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 102);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, int32FromSoundforge) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-int32-soundforge.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::Int32);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 4);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, uint8FromAudition) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-audition.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::UInt8);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 1);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 92);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, uint8FromFfmpeg) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-ffmpeg.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::UInt8);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 1);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 78);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(getWaveFormatInfo, uint8FromSoundforge) {
|
||||||
|
auto formatInfo = getWaveFormatInfo(getBinDirectory() / "tests/resources/sine-triangle-uint8-soundforge.wav");
|
||||||
|
EXPECT_EQ(formatInfo.frameRate, 48000);
|
||||||
|
EXPECT_EQ(formatInfo.frameCount, 480000);
|
||||||
|
EXPECT_EQ(formatInfo.channelCount, 2);
|
||||||
|
EXPECT_EQ(formatInfo.sampleFormat, SampleFormat::UInt8);
|
||||||
|
EXPECT_EQ(formatInfo.bytesPerFrame, 2 * 1);
|
||||||
|
EXPECT_EQ(formatInfo.dataOffset, 44);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
This directory contains test files for the WAVE file reader.
|
||||||
|
|
||||||
|
All files starting with _sine-rect_ contain the same 10-second stereo signal sampled at 48,000 Hz. The left channel contains a 1 kHz sine wave, the right channel contains a 1 kHz triangle wave. As those signals are strictly periodic, Git can compress these files very efficiently.
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue