2020-07-26 00:48:43 +02:00
|
|
|
// SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
|
|
|
|
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
|
|
|
|
// license. You should have receive a LICENSE.md file along with the code.
|
|
|
|
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
|
|
|
|
|
|
|
|
|
#include "AudioReader.h"
|
|
|
|
|
#include <benchmark/benchmark.h>
|
|
|
|
|
#include <sndfile.hh>
|
|
|
|
|
#include <ghc/fs_std.hpp>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <system_error>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <cstdio>
|
2020-10-05 19:04:25 +02:00
|
|
|
#include <cstdlib>
|
2020-07-26 00:48:43 +02:00
|
|
|
#include <cmath>
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <io.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
///
|
2020-10-05 19:04:25 +02:00
|
|
|
struct TemporaryFile {
|
|
|
|
|
TemporaryFile();
|
|
|
|
|
~TemporaryFile();
|
2020-07-26 00:48:43 +02:00
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
TemporaryFile(const TemporaryFile&) = delete;
|
|
|
|
|
TemporaryFile& operator=(const TemporaryFile&) = delete;
|
2020-07-26 00:48:43 +02:00
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
TemporaryFile(TemporaryFile&&) = default;
|
|
|
|
|
TemporaryFile& operator=(TemporaryFile&&) = default;
|
2020-07-26 00:48:43 +02:00
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
const fs::path& path() const { return path_; }
|
2020-07-26 00:48:43 +02:00
|
|
|
|
|
|
|
|
private:
|
2020-10-05 19:04:25 +02:00
|
|
|
fs::path path_;
|
|
|
|
|
static fs::path createTemporaryFile();
|
2020-07-26 00:48:43 +02:00
|
|
|
};
|
|
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
TemporaryFile::TemporaryFile()
|
|
|
|
|
: path_(createTemporaryFile())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TemporaryFile::~TemporaryFile()
|
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::remove(path_, ec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
|
fs::path TemporaryFile::createTemporaryFile()
|
|
|
|
|
{
|
|
|
|
|
char path[] = P_tmpdir "/sndXXXXXX";
|
|
|
|
|
int fd = mkstemp(path);
|
|
|
|
|
if (fd == -1)
|
|
|
|
|
throw std::runtime_error("Cannot create temporary file.");
|
|
|
|
|
close(fd);
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
fs::path TemporaryFile::createTemporaryFile()
|
|
|
|
|
{
|
|
|
|
|
DWORD ret = GetTempPathW(0, buffer);
|
|
|
|
|
std::unique_ptr<WCHAR[]> path;
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
path.reset(new WCHAR[ret + 8]{});
|
|
|
|
|
ret = GetTempPathW(0, buffer);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
wcscat(path.get(), L"\\XXXXXX");
|
|
|
|
|
}
|
|
|
|
|
if (ret == 0 || !_wmktemp(path.get()))
|
|
|
|
|
throw std::runtime_error("Cannot create temporary file.");
|
|
|
|
|
return path.get();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-07-26 00:48:43 +02:00
|
|
|
///
|
|
|
|
|
class AudioReaderFixture : public benchmark::Fixture {
|
|
|
|
|
public:
|
|
|
|
|
void SetUp(const ::benchmark::State& state) override
|
|
|
|
|
{
|
|
|
|
|
workBuffer.resize(2 * static_cast<size_t>(state.range(0)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TearDown(const ::benchmark::State& /* state */) override
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
static TemporaryFile createAudioFile(int format);
|
2020-07-26 00:48:43 +02:00
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
static TemporaryFile fileWav;
|
|
|
|
|
static TemporaryFile fileFlac;
|
2020-10-31 11:59:45 +01:00
|
|
|
static TemporaryFile fileAiff;
|
2020-10-05 19:04:25 +02:00
|
|
|
static TemporaryFile fileOgg;
|
2020-07-26 00:48:43 +02:00
|
|
|
|
|
|
|
|
std::vector<float> workBuffer;
|
|
|
|
|
};
|
|
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
TemporaryFile AudioReaderFixture::fileWav = createAudioFile(SF_FORMAT_WAV|SF_FORMAT_PCM_16);
|
|
|
|
|
TemporaryFile AudioReaderFixture::fileFlac = createAudioFile(SF_FORMAT_FLAC|SF_FORMAT_PCM_16);
|
2020-10-31 11:59:45 +01:00
|
|
|
TemporaryFile AudioReaderFixture::fileAiff = createAudioFile(SF_FORMAT_AIFF|SF_FORMAT_PCM_16);
|
2020-10-05 19:04:25 +02:00
|
|
|
TemporaryFile AudioReaderFixture::fileOgg = createAudioFile(SF_FORMAT_OGG|SF_FORMAT_VORBIS);
|
2020-07-26 00:48:43 +02:00
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
TemporaryFile AudioReaderFixture::createAudioFile(int format)
|
2020-07-26 00:48:43 +02:00
|
|
|
{
|
|
|
|
|
constexpr unsigned sampleRate = 44100;
|
|
|
|
|
constexpr unsigned fileDuration = 10;
|
|
|
|
|
constexpr unsigned fileFrames = sampleRate * fileDuration;
|
|
|
|
|
|
|
|
|
|
// synth 2 channels of arbitrary waveform
|
|
|
|
|
std::unique_ptr<double[]> sndData { new double[2 * fileFrames] };
|
|
|
|
|
double phase = 0.0;
|
|
|
|
|
for (unsigned i = 0; i < fileFrames; ++i) {
|
|
|
|
|
sndData[2 * i ] = std::sin(2.0 * M_PI * phase);
|
|
|
|
|
sndData[2 * i + 1] = std::cos(2.0 * M_PI * phase);
|
|
|
|
|
phase += 440.0 * (1.0 / sampleRate);
|
|
|
|
|
phase -= static_cast<long>(phase);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
// create temp file
|
|
|
|
|
TemporaryFile temp;
|
|
|
|
|
fprintf(stderr, "* Temporary file: %s\n", temp.path().u8string().c_str());
|
|
|
|
|
|
|
|
|
|
// write to file
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
|
SndfileHandle snd(temp.path().c_str(), SFM_WRITE, format, 2, sampleRate);
|
|
|
|
|
#else
|
|
|
|
|
SndfileHandle snd(temp.path().wstring().c_str(), SFM_WRITE, format, 2, sampleRate);
|
|
|
|
|
#endif
|
2020-07-26 00:48:43 +02:00
|
|
|
|
|
|
|
|
if (snd.error())
|
|
|
|
|
throw std::runtime_error("cannot open sound file for writing");
|
|
|
|
|
snd.writef(sndData.get(), fileFrames);
|
|
|
|
|
snd = SndfileHandle();
|
|
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
return temp;
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
static void doReaderBenchmark(const fs::path& path, std::vector<float> &buffer, sfz::AudioReaderType type)
|
2020-07-26 00:48:43 +02:00
|
|
|
{
|
2020-10-05 19:04:25 +02:00
|
|
|
sfz::AudioReaderPtr reader = sfz::createExplicitAudioReader(path, type);
|
2020-07-26 00:48:43 +02:00
|
|
|
while (reader->readNextBlock(buffer.data(), buffer.size() / 2) > 0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 19:04:25 +02:00
|
|
|
static void doEntireRead(const fs::path& path)
|
2020-07-26 00:48:43 +02:00
|
|
|
{
|
2020-10-07 17:23:59 +02:00
|
|
|
sfz::AudioReaderPtr reader = sfz::createAudioReader(path, false);
|
|
|
|
|
if (!reader)
|
|
|
|
|
return;
|
2020-07-26 00:48:43 +02:00
|
|
|
|
2020-10-07 17:23:59 +02:00
|
|
|
std::vector<float> buffer(static_cast<size_t>(2 * reader->frames()));
|
|
|
|
|
reader->readNextBlock(buffer.data(), buffer.size());
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, EntireWav)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doEntireRead(fileWav.path());
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardWav)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doReaderBenchmark(fileWav.path(), workBuffer, sfz::AudioReaderType::Forward);
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseWav)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doReaderBenchmark(fileWav.path(), workBuffer, sfz::AudioReaderType::Reverse);
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, EntireFlac)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doEntireRead(fileFlac.path());
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardFlac)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doReaderBenchmark(fileFlac.path(), workBuffer, sfz::AudioReaderType::Forward);
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseFlac)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doReaderBenchmark(fileFlac.path(), workBuffer, sfz::AudioReaderType::Reverse);
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-31 11:59:45 +01:00
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, EntireAiff)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
|
|
|
|
doEntireRead(fileAiff.path());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardAiff)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
|
|
|
|
doReaderBenchmark(fileAiff.path(), workBuffer, sfz::AudioReaderType::Forward);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseAiff)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
|
|
|
|
doReaderBenchmark(fileAiff.path(), workBuffer, sfz::AudioReaderType::Reverse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-26 00:48:43 +02:00
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, EntireOgg)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doEntireRead(fileOgg.path());
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardOgg)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
2020-10-05 19:04:25 +02:00
|
|
|
doReaderBenchmark(fileOgg.path(), workBuffer, sfz::AudioReaderType::Forward);
|
2020-07-26 00:48:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-09 17:37:11 +02:00
|
|
|
#if !defined(ST_AUDIO_FILE_USE_SNDFILE)
|
|
|
|
|
BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseOgg)(benchmark::State& state)
|
|
|
|
|
{
|
|
|
|
|
for (auto _ : state) {
|
|
|
|
|
doReaderBenchmark(fileOgg.path(), workBuffer, sfz::AudioReaderType::Reverse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2020-07-26 00:48:43 +02:00
|
|
|
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, EntireWav)->Range(1, 1);
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardFlac)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseFlac)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, EntireFlac)->Range(1, 1);
|
2020-10-31 11:59:45 +01:00
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardAiff)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseAiff)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, EntireAiff)->Range(1, 1);
|
2020-07-26 00:48:43 +02:00
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
2020-10-09 17:37:11 +02:00
|
|
|
#if !defined(ST_AUDIO_FILE_USE_SNDFILE)
|
|
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10));
|
|
|
|
|
#endif
|
2020-07-26 00:48:43 +02:00
|
|
|
BENCHMARK_REGISTER_F(AudioReaderFixture, EntireOgg)->Range(1, 1);
|
|
|
|
|
BENCHMARK_MAIN();
|