Move some of the audio provider machinery to libaegisub

And add tests.
This commit is contained in:
Thomas Goyne 2014-07-09 07:22:49 -07:00
parent e942a7f0f7
commit 585e9489d9
46 changed files with 1272 additions and 1074 deletions

View file

@ -1,5 +1,5 @@
ifneq (yes, $(INCLUDING_CHILD_MAKEFILES))
COMMANDS := all install clean distclean test depclean osx-bundle osx-dmg test-automation
COMMANDS := all install clean distclean test depclean osx-bundle osx-dmg test-automation test-libaegisub
.PHONY: $(COMMANDS)
.DEFAULT_GOAL := all

View file

@ -4,6 +4,7 @@ aegisub_OBJ := \
$(d)common/parser.o \
$(d)ass/dialogue_parser.o \
$(d)ass/time.o \
$(subst .cpp,.o,$(wildcard $(d)audio/*.cpp)) \
$(subst .cpp,.o,$(wildcard $(d)common/cajun/*.cpp)) \
$(subst .cpp,.o,$(wildcard $(d)lua/modules/*.cpp)) \
$(subst .c,.o,$(wildcard $(d)lua/modules/*.c)) \

View file

@ -0,0 +1,137 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "libaegisub/audio/provider.h"
#include "libaegisub/fs.h"
#include "libaegisub/io.h"
#include "libaegisub/log.h"
#include "libaegisub/util.h"
namespace agi {
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
GetAudio(buf, start, count);
if (volume == 1.0) return;
if (bytes_per_sample != 2)
throw agi::InternalError("GetAudioWithVolume called on unconverted audio stream");
auto buffer = static_cast<int16_t *>(buf);
for (size_t i = 0; i < (size_t)count; ++i)
buffer[i] = util::mid<int>(-0x8000, buffer[i] * volume + 0.5, 0x7FFF);
}
void AudioProvider::ZeroFill(void *buf, int64_t count) const {
if (bytes_per_sample == 1)
// 8 bit formats are usually unsigned with bias 127
memset(buf, 127, count * channels);
else // While everything else is signed
memset(buf, 0, count * bytes_per_sample * channels);
}
void AudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
if (start < 0) {
ZeroFill(buf, std::min(-start, count));
buf = static_cast<char *>(buf) + -start * bytes_per_sample * channels;
count += start;
start = 0;
}
if (start + count > num_samples) {
int64_t zero_count = std::min(count, start + count - num_samples);
count -= zero_count;
ZeroFill(static_cast<char *>(buf) + count * bytes_per_sample * channels, zero_count);
}
if (count <= 0) return;
try {
FillBuffer(buf, start, count);
}
catch (AudioDecodeError const& e) {
// We don't have any good way to report errors here, so just log the
// failure and return silence
LOG_E("audio_provider") << e.GetMessage();
ZeroFill(buf, count);
return;
}
catch (...) {
LOG_E("audio_provider") << "Unknown audio decoding error";
ZeroFill(buf, count);
return;
}
}
namespace {
class writer {
io::Save outfile;
std::ostream& out;
public:
writer(agi::fs::path const& filename) : outfile(filename, true), out(outfile.Get()) { }
template<int N>
void write(const char(&str)[N]) {
out.write(str, N - 1);
}
void write(std::vector<char> const& data) {
out.write(data.data(), data.size());
}
template<typename Dest, typename Src>
void write(Src v) {
auto converted = static_cast<Dest>(v);
out.write(reinterpret_cast<char *>(&converted), sizeof(Dest));
}
};
}
void SaveAudioClip(AudioProvider *provider, fs::path const& path, int start_time, int end_time) {
auto start_sample = ((int64_t)start_time * provider->GetSampleRate() + 999) / 1000;
auto end_sample = ((int64_t)end_time * provider->GetSampleRate() + 999) / 1000;
if (start_sample >= provider->GetNumSamples() || start_sample >= end_sample) return;
size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();
size_t bufsize = (end_sample - start_sample) * bytes_per_sample;
writer out{path};
out.write("RIFF");
out.write<int32_t>(bufsize + 36);
out.write("WAVEfmt ");
out.write<int32_t>(16); // Size of chunk
out.write<int16_t>(1); // compression format (PCM)
out.write<int16_t>(provider->GetChannels());
out.write<int32_t>(provider->GetSampleRate());
out.write<int32_t>(provider->GetSampleRate() * provider->GetChannels() * provider->GetBytesPerSample());
out.write<int16_t>(provider->GetChannels() * provider->GetBytesPerSample());
out.write<int16_t>(provider->GetBytesPerSample() * 8);
out.write("data");
out.write<int32_t>(bufsize);
// samples per read
size_t spr = 65536 / bytes_per_sample;
std::vector<char> buf;
for (int64_t i = start_sample; i < end_sample; i += spr) {
spr = std::min<size_t>(spr, end_sample - i);
buf.resize(spr * bytes_per_sample);
provider->GetAudio(&buf[0], i, spr);
out.write(buf);
}
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@ -14,57 +14,55 @@
//
// Aegisub Project http://www.aegisub.org/
/// @file audio_provider_convert.cpp
/// @brief Intermediate sample format-converting audio provider
/// @ingroup audio_input
///
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include "libaegisub/audio/provider.h"
#include <libaegisub/log.h>
#include <libaegisub/make_unique.h>
#include <limits>
using namespace agi;
/// Anything integral -> 16 bit signed machine-endian audio converter
namespace {
template<class Target>
class BitdepthConvertAudioProvider final : public AudioProviderWrapper {
int src_bytes_per_sample;
public:
BitdepthConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
if (bytes_per_sample > 8)
throw agi::AudioProviderOpenError("Audio format converter: audio with bitdepths greater than 64 bits/sample is currently unsupported");
throw AudioProviderError("Audio format converter: audio with bitdepths greater than 64 bits/sample is currently unsupported");
src_bytes_per_sample = bytes_per_sample;
bytes_per_sample = sizeof(Target);
}
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
std::vector<char> src_buf(count * src_bytes_per_sample * channels);
source->GetAudio(&src_buf[0], start, count);
std::vector<uint8_t> src_buf(count * src_bytes_per_sample * channels);
source->GetAudio(src_buf.data(), start, count);
int16_t *dest = reinterpret_cast<int16_t*>(buf);
for (int64_t i = 0; i < count * channels; ++i) {
int64_t sample = 0;
char *sample_ptr = (char*)&sample;
char *src = &src_buf[i * src_bytes_per_sample];
// 8 bits per sample is assumed to be unsigned with a bias of 127,
// while everything else is assumed to be signed with zero bias
if (src_bytes_per_sample == 1)
*sample_ptr = static_cast<uint8_t>(*src) - 127;
else
memcpy(sample_ptr, src, src_bytes_per_sample);
sample = src_buf[i] - 127;
else {
for (int j = 0; j < src_bytes_per_sample; ++j) {
sample <<= 8;
sample += src_buf[i * src_bytes_per_sample + j];
}
}
if (static_cast<size_t>(src_bytes_per_sample) > sizeof(Target))
sample >>= (src_bytes_per_sample - sizeof(Target)) * 8;
else if (static_cast<size_t>(src_bytes_per_sample) < sizeof(Target))
sample <<= (sizeof(Target) - src_bytes_per_sample ) * 8;
dest[i] = (Target)sample;
dest[i] = static_cast<Target>(sample);
}
}
};
@ -82,7 +80,7 @@ public:
std::vector<Source> src_buf(count * channels);
source->GetAudio(&src_buf[0], start, count);
Target *dest = reinterpret_cast<Target*>(buf);
auto dest = reinterpret_cast<Target*>(buf);
for (size_t i = 0; i < static_cast<size_t>(count * channels); ++i) {
Source expanded;
@ -107,9 +105,9 @@ class DownmixAudioProvider final : public AudioProviderWrapper {
public:
DownmixAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
if (bytes_per_sample != 2)
throw agi::InternalError("DownmixAudioProvider requires 16-bit input");
throw InternalError("DownmixAudioProvider requires 16-bit input");
if (channels == 1)
throw agi::InternalError("DownmixAudioProvider requires multi-channel input");
throw InternalError("DownmixAudioProvider requires multi-channel input");
src_channels = channels;
channels = 1;
}
@ -137,9 +135,9 @@ class SampleDoublingAudioProvider final : public AudioProviderWrapper {
public:
SampleDoublingAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
if (source->GetBytesPerSample() != 2)
throw agi::InternalError("UpsampleAudioProvider requires 16-bit input");
throw InternalError("UpsampleAudioProvider requires 16-bit input");
if (source->GetChannels() != 1)
throw agi::InternalError("UpsampleAudioProvider requires mono input");
throw InternalError("UpsampleAudioProvider requires mono input");
sample_rate *= 2;
num_samples *= 2;
@ -149,11 +147,11 @@ public:
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
if (count == 0) return;
int not_end = start + count < num_samples;
bool not_end = start + count < num_samples;
int64_t src_count = count / 2;
source->GetAudio(buf, start / 2, src_count + not_end);
int16_t *buf16 = reinterpret_cast<int16_t*>(buf);
auto buf16 = reinterpret_cast<int16_t*>(buf);
if (!not_end) {
// We weren't able to request a sample past the end so just
@ -171,7 +169,9 @@ public:
}
}
};
}
namespace agi {
std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioProvider> provider) {
// Ensure 16-bit audio with proper endianness
if (provider->AreSamplesFloat()) {
@ -200,3 +200,4 @@ std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioP
return provider;
}
}

View file

@ -0,0 +1,80 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "libaegisub/audio/provider.h"
#include "libaegisub/fs.h"
#include "libaegisub/make_unique.h"
#include <boost/algorithm/string/predicate.hpp>
#include <random>
/*
* scheme ::= "dummy-audio" ":" signal-specifier "?" signal-parameters
* signal-specifier ::= "silence" | "noise" | "sine" "/" frequency
* frequency ::= integer
* signal-parameters ::= signal-parameter [ "&" signal-parameters ]
* signal-parameter ::= signal-parameter-name "=" integer
* signal-parameter-name ::= "sr" | "bd" | "ch" | "ln"
*
* Signal types:
* "silence", a silent signal is generated.
* "noise", a white noise signal is generated.
* "sine", a sine wave is generated at the specified frequency.
*
* Signal parameters:
* "sr", sample rate to generate signal at.
* "bd", bit depth to generate signal at (usually 16).
* "ch", number of channels to generate, usually 1 or 2. The same signal is generated
* in every channel even if one would be LFE.
* "ln", length of signal in samples. ln/sr gives signal length in seconds.
*/
namespace {
using namespace agi;
class DummyAudioProvider final : public AudioProvider {
bool noise;
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
if (noise) {
std::default_random_engine e;
std::uniform_int_distribution<int16_t> uniform_dist(-5000, 5000);
for (size_t i = 0; i < count; ++i)
static_cast<short *>(buf)[i] = uniform_dist(e);
}
else
memset(buf, 0, count * bytes_per_sample);
}
public:
DummyAudioProvider(agi::fs::path const& uri) {
noise = boost::contains(uri.string(), ":noise?");
channels = 1;
sample_rate = 44100;
bytes_per_sample = 2;
float_samples = false;
decoded_samples = num_samples = (int64_t)5*30*60*1000 * sample_rate / 1000;
}
};
}
namespace agi {
std::unique_ptr<AudioProvider> CreateDummyAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) {
if (!boost::starts_with(file.string(), "dummy-audio:"))
return {};
return agi::make_unique<DummyAudioProvider>(file);
}
}

View file

@ -14,10 +14,7 @@
//
// Aegisub Project http://www.aegisub.org/
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include "options.h"
#include "libaegisub/audio/provider.h"
#include <libaegisub/file_mapping.h>
#include <libaegisub/format.h>
@ -27,11 +24,14 @@
#include <boost/filesystem/path.hpp>
#include <boost/interprocess/detail/os_thread_functions.hpp>
#include <ctime>
#include <thread>
namespace {
using namespace agi;
class HDAudioProvider final : public AudioProviderWrapper {
std::unique_ptr<agi::temp_file_mapping> file;
mutable temp_file_mapping file;
std::atomic<bool> cancelled = {false};
std::thread decoder;
@ -45,35 +45,31 @@ class HDAudioProvider final : public AudioProviderWrapper {
if (count > 0) {
start *= bytes_per_sample;
count *= bytes_per_sample;
memcpy(buf, file->read(start, count), count);
memcpy(buf, file.read(start, count), count);
}
}
fs::path CacheFilename(fs::path const& dir) {
// Check free space
if ((uint64_t)num_samples * bytes_per_sample > fs::FreeSpace(dir))
throw AudioProviderError("Not enough free disk space in " + dir.string() + " to cache the audio");
return format("audio-%lld-%lld", time(nullptr),
boost::interprocess::ipcdetail::get_current_process_id());
}
public:
HDAudioProvider(std::unique_ptr<AudioProvider> src)
HDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const& dir)
: AudioProviderWrapper(std::move(src))
, file(dir / CacheFilename(dir), num_samples * bytes_per_sample)
{
decoded_samples = 0;
auto path = OPT_GET("Audio/Cache/HD/Location")->GetString();
if (path == "default")
path = "?temp";
auto cache_dir = config::path->MakeAbsolute(config::path->Decode(path), "?temp");
// Check free space
if ((uint64_t)num_samples * bytes_per_sample > agi::fs::FreeSpace(cache_dir))
throw agi::AudioCacheOpenError("Not enough free disk space in " + cache_dir.string() + " to cache the audio");
auto filename = agi::format("audio-%lld-%lld", time(nullptr),
boost::interprocess::ipcdetail::get_current_process_id());
file = agi::make_unique<agi::temp_file_mapping>(cache_dir / filename, num_samples * bytes_per_sample);
decoder = std::thread([&] {
int64_t block = 65536;
for (int64_t i = 0; i < num_samples; i += block) {
if (cancelled) break;
block = std::min(block, num_samples - i);
source->GetAudio(file->write(i * bytes_per_sample, block * bytes_per_sample), i, block);
source->GetAudio(file.write(i * bytes_per_sample, block * bytes_per_sample), i, block);
decoded_samples += block;
}
});
@ -86,6 +82,8 @@ public:
};
}
std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> src) {
return agi::make_unique<HDAudioProvider>(std::move(src));
namespace agi {
std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const& dir) {
return make_unique<HDAudioProvider>(std::move(src), dir);
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@ -11,20 +11,17 @@
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file audio_provider_lock.cpp
/// @brief An audio provider adapter for un-threadsafe audio providers
/// @ingroup audio_input
#include "include/aegisub/audio_provider.h"
#include "libaegisub/audio/provider.h"
#include <libaegisub/make_unique.h>
#include <memory>
#include <mutex>
namespace {
class LockAudioProvider final : public AudioProviderWrapper {
class LockAudioProvider final : public agi::AudioProviderWrapper {
mutable std::mutex mutex;
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
@ -40,6 +37,8 @@ public:
};
}
namespace agi {
std::unique_ptr<AudioProvider> CreateLockAudioProvider(std::unique_ptr<AudioProvider> src) {
return agi::make_unique<LockAudioProvider>(std::move(src));
}
}

View file

@ -0,0 +1,239 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "libaegisub/audio/provider.h"
#include "libaegisub/file_mapping.h"
#include "libaegisub/fs.h"
#include "libaegisub/make_unique.h"
#include <vector>
namespace {
using namespace agi;
struct IndexPoint {
uint64_t start_byte;
uint64_t num_samples;
};
struct file_ended {};
class PCMAudioProvider : public AudioProvider {
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
auto write_buf = static_cast<char *>(buf);
auto bps = bytes_per_sample * channels;
uint64_t pos = 0;
for (auto ip : index_points) {
if (count == 0) break;
if (pos + ip.num_samples <= (uint64_t)start) {
pos += ip.num_samples;
continue;
}
auto read_offset = start - pos;
auto read_count = std::min<size_t>(count, ip.num_samples - read_offset);
auto bytes = read_count * bps;
memcpy(write_buf, file.read(ip.start_byte + read_offset * bps, bytes), bytes);
write_buf += bytes;
count -= read_count;
start += read_count;
pos += ip.num_samples;
}
}
protected:
mutable read_file_mapping file;
uint64_t file_pos = 0;
PCMAudioProvider(fs::path const& filename) : file(filename) { }
template<typename T, typename UInt>
const T *Read(UInt *data_left) {
if (*data_left < sizeof(T)) throw file_ended();
if (file.size() - file_pos < sizeof(T)) throw file_ended();
auto data = file.read(file_pos, sizeof(T));
file_pos += sizeof(T);
*data_left -= sizeof(T);
return reinterpret_cast<const T *>(data);
}
std::vector<IndexPoint> index_points;
};
struct FourCC {
std::array<char, 4> data;
bool operator!=(const char *cmp) const {
return data[0] != cmp[0] || data[1] != cmp[1]
|| data[2] != cmp[2] || data[3] != cmp[3];
}
bool operator==(const char *cmp) const { return !(*this != cmp); }
};
// Overview of RIFF WAV: <http://www.sonicspot.com/guide/wavefiles.html>
struct RiffWav {
using DataSize = uint32_t;
using ChunkId = FourCC;
static const char *riff_id() { return "RIFF"; }
static const char *wave_id() { return "WAVE"; }
static const char *fmt_id() { return "fmt "; }
static const char *data_id() { return "data "; }
static const int alignment = 1;
static uint32_t data_size(uint32_t size) { return size; }
static uint32_t chunk_size(uint32_t size) { return size; }
};
typedef std::array<uint8_t, 16> GUID;
static const GUID w64GuidRIFF = {{
// {66666972-912E-11CF-A5D6-28DB04C10000}
0x72, 0x69, 0x66, 0x66, 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
}};
static const GUID w64GuidWAVE = {{
// {65766177-ACF3-11D3-8CD1-00C04F8EDB8A}
0x77, 0x61, 0x76, 0x65, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
}};
static const GUID w64Guidfmt = {{
// {20746D66-ACF3-11D3-8CD1-00C04F8EDB8A}
0x66, 0x6D, 0x74, 0x20, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
}};
static const GUID w64Guiddata = {{
// {61746164-ACF3-11D3-8CD1-00C04F8EDB8A}
0x64, 0x61, 0x74, 0x61, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
}};
// http://www.vcs.de/fileadmin/user_upload/MBS/PDF/Whitepaper/Informations_about_Sony_Wave64.pdf
struct Wave64 {
using DataSize = uint64_t;
using ChunkId = GUID;
static GUID riff_id() { return w64GuidRIFF; }
static GUID wave_id() { return w64GuidWAVE; }
static GUID fmt_id() { return w64Guidfmt; }
static GUID data_id() { return w64Guiddata; }
static const uint64_t alignment = 7ULL;
// Wave 64 includes the size of the header in the chunk sizes
static uint64_t data_size(uint64_t size) { return size - 16; }
static uint64_t chunk_size(uint64_t size) { return size - 24; }
};
template<typename Impl>
class WavPCMAudioProvider : public PCMAudioProvider {
public:
WavPCMAudioProvider(fs::path const& filename)
: PCMAudioProvider(filename)
{
using DataSize = typename Impl::DataSize;
using ChunkId = typename Impl::ChunkId;
try {
auto data_left = std::numeric_limits<DataSize>::max();
if (*Read<ChunkId>(&data_left) != Impl::riff_id())
throw AudioDataNotFound("File is not a RIFF file");
data_left = Impl::data_size(*Read<DataSize>(&data_left));
if (*Read<ChunkId>(&data_left) != Impl::wave_id())
throw AudioDataNotFound("File is not a RIFF WAV file");
while (data_left) {
auto chunk_fcc = *Read<ChunkId>(&data_left);
auto chunk_size = Impl::chunk_size(*Read<DataSize>(&data_left));
data_left -= chunk_size;
if (chunk_fcc == Impl::fmt_id()) {
if (channels || sample_rate || bytes_per_sample)
throw AudioProviderError("Multiple 'fmt ' chunks not supported");
auto compression = *Read<uint16_t>(&chunk_size);
if (compression != 1)
throw AudioProviderError("File is not uncompressed PCM");
channels = *Read<uint16_t>(&chunk_size);
sample_rate = *Read<uint32_t>(&chunk_size);
Read<uint32_t>(&chunk_size); // Average bytes per sample; meaningless
Read<uint16_t>(&chunk_size); // Block align
bytes_per_sample = (*Read<uint16_t>(&chunk_size) + 7) / 8;
}
else if (chunk_fcc == Impl::data_id()) {
if (!channels || !sample_rate || !bytes_per_sample)
throw AudioProviderError("Found 'data' chunk without format being set.");
index_points.emplace_back(IndexPoint{file_pos, chunk_size / bytes_per_sample / channels});
num_samples += chunk_size / bytes_per_sample / channels;
}
// There's a bunch of other chunk types. They're all dumb.
// blocks are aligned and the padding bytes are not included in
// the size of the chunk
file_pos += (chunk_size + Impl::alignment) & ~Impl::alignment;
}
}
catch (file_ended) {
if (!channels || !sample_rate || !bytes_per_sample)
throw AudioDataNotFound("File ended before reaching format chunk");
// Truncated files are fine otherwise
}
decoded_samples = num_samples;
}
};
}
namespace agi {
std::unique_ptr<AudioProvider> CreatePCMAudioProvider(fs::path const& filename, BackgroundRunner *) {
bool wrong_file_type = true;
std::string msg;
try {
return make_unique<WavPCMAudioProvider<RiffWav>>(filename);
}
catch (AudioDataNotFound const& err) {
msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
}
catch (AudioProviderError const& err) {
wrong_file_type = false;
msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
}
try {
return make_unique<WavPCMAudioProvider<Wave64>>(filename);
}
catch (AudioDataNotFound const& err) {
msg += "\nWave64 audio provider: " + err.GetMessage();
}
catch (AudioProviderError const& err) {
wrong_file_type = false;
msg += "\nWave64 audio provider: " + err.GetMessage();
}
if (wrong_file_type)
throw AudioDataNotFound(msg);
else
throw AudioProviderError(msg);
}
}

View file

@ -1,48 +1,29 @@
// Copyright (c) 2005-2006, Rodrigo Braz Monteiro, Fredrik Mellbin
// All rights reserved.
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file audio_provider_ram.cpp
/// @brief Caching audio provider using heap memory for backing
/// @ingroup audio_input
///
#include "libaegisub/audio/provider.h"
#include "include/aegisub/audio_provider.h"
#include "audio_controller.h"
#include <libaegisub/make_unique.h>
#include "libaegisub/make_unique.h"
#include <array>
#include <boost/container/stable_vector.hpp>
#include <thread>
namespace {
using namespace agi;
#define CacheBits 22
#define CacheBlockSize (1 << CacheBits)
@ -68,7 +49,7 @@ public:
blockcache.resize((source->GetNumSamples() * source->GetBytesPerSample() + CacheBlockSize - 1) >> CacheBits);
}
catch (std::bad_alloc const&) {
throw agi::AudioCacheOpenError("Couldn't open audio, not enough ram available.");
throw AudioProviderError("Not enough memory available to cache in RAM");
}
decoder = std::thread([&] {
@ -108,6 +89,8 @@ void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const
}
}
namespace agi {
std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> src) {
return agi::make_unique<RAMAudioProvider>(std::move(src));
return make_unique<RAMAudioProvider>(std::move(src));
}
}

View file

@ -0,0 +1,95 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#pragma once
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
#include <atomic>
#include <vector>
namespace agi {
class AudioProvider {
protected:
int channels = 0;
/// Total number of samples per channel
int64_t num_samples = 0;
/// Samples per channel which have been decoded and can be fetched with FillBuffer
/// Only applicable for the cache providers
std::atomic<int64_t> decoded_samples{0};
int sample_rate = 0;
int bytes_per_sample = 0;
bool float_samples = false;
virtual void FillBuffer(void *buf, int64_t start, int64_t count) const = 0;
void ZeroFill(void *buf, int64_t count) const;
public:
virtual ~AudioProvider() = default;
void GetAudio(void *buf, int64_t start, int64_t count) const;
void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const;
int64_t GetNumSamples() const { return num_samples; }
int64_t GetDecodedSamples() const { return decoded_samples; }
int GetSampleRate() const { return sample_rate; }
int GetBytesPerSample() const { return bytes_per_sample; }
int GetChannels() const { return channels; }
bool AreSamplesFloat() const { return float_samples; }
/// Does this provider benefit from external caching?
virtual bool NeedsCache() const { return false; }
};
/// Helper base class for an audio provider which wraps another provider
class AudioProviderWrapper : public AudioProvider {
protected:
std::unique_ptr<AudioProvider> source;
public:
AudioProviderWrapper(std::unique_ptr<AudioProvider> src)
: source(std::move(src))
{
channels = source->GetChannels();
num_samples = source->GetNumSamples();
decoded_samples = source->GetDecodedSamples();
sample_rate = source->GetSampleRate();
bytes_per_sample = source->GetBytesPerSample();
float_samples = source->AreSamplesFloat();
}
};
DEFINE_EXCEPTION(AudioProviderError, Exception);
/// Error of some sort occurred while decoding a frame
DEFINE_EXCEPTION(AudioDecodeError, AudioProviderError);
/// This provider could not find any audio data in the file
DEFINE_EXCEPTION(AudioDataNotFound, AudioProviderError);
class BackgroundRunner;
std::unique_ptr<AudioProvider> CreateDummyAudioProvider(fs::path const& filename, BackgroundRunner *);
std::unique_ptr<AudioProvider> CreatePCMAudioProvider(fs::path const& filename, BackgroundRunner *);
std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioProvider> source_provider);
std::unique_ptr<AudioProvider> CreateLockAudioProvider(std::unique_ptr<AudioProvider> source_provider);
std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> source_provider, fs::path const& dir);
std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> source_provider);
void SaveAudioClip(AudioProvider *provider, fs::path const& path, int start_time, int end_time);
}

View file

@ -36,13 +36,7 @@ src_OBJ := \
$(d)audio_karaoke.o \
$(d)audio_marker.o \
$(d)audio_player.o \
$(d)audio_provider.o \
$(d)audio_provider_convert.o \
$(d)audio_provider_dummy.o \
$(d)audio_provider_hd.o \
$(d)audio_provider_lock.o \
$(d)audio_provider_pcm.o \
$(d)audio_provider_ram.o \
$(d)audio_provider_factory.o \
$(d)audio_renderer.o \
$(d)audio_renderer_spectrum.o \
$(d)audio_renderer_waveform.o \
@ -190,7 +184,7 @@ endif
#####################
$(d)MatroskaParser.o_FLAGS := -Wno-sometimes-uninitialized
$(d)audio_player.o_FLAGS := $(CFLAGS_ALSA) $(CFLAGS_PORTAUDIO) $(CFLAGS_LIBPULSE) $(CFLAGS_OPENAL)
$(d)audio_provider.o_FLAGS := $(CFLAGS_FFMS2)
$(d)audio_provider_factory.o_FLAGS := $(CFLAGS_FFMS2)
$(d)auto4_base.o_FLAGS := $(CFLAGS_FREETYPE)
$(d)charset_detect.o_FLAGS := -D_X86_
$(d)font_file_lister_fontconfig.o_FLAGS := $(CFLAGS_FONTCONFIG)

View file

@ -31,11 +31,12 @@
#include "audio_timing.h"
#include "include/aegisub/audio_player.h"
#include "include/aegisub/audio_provider.h"
#include "include/aegisub/context.h"
#include "options.h"
#include "project.h"
#include <libaegisub/audio/provider.h>
#include <algorithm>
AudioController::AudioController(agi::Context *context)
@ -102,12 +103,13 @@ void AudioController::OnAudioPlayerChanged()
}
catch (...)
{
/// @todo This really shouldn't be just swallowing all audio player open errors
context->project->CloseAudio();
}
AnnounceAudioPlayerOpened();
}
void AudioController::OnAudioProvider(AudioProvider *new_provider)
void AudioController::OnAudioProvider(agi::AudioProvider *new_provider)
{
provider = new_provider;
Stop();

View file

@ -36,10 +36,10 @@
#include <wx/timer.h>
class AudioPlayer;
class AudioProvider;
class AudioTimingController;
namespace agi { struct Context; }
class TimeRange;
namespace agi { class AudioProvider; }
namespace agi { struct Context; }
/// @class AudioController
/// @brief Manage playback of an open audio stream
@ -84,10 +84,10 @@ class AudioController final : public wxEvtHandler {
wxTimer playback_timer;
/// The audio provider
AudioProvider *provider = nullptr;
agi::AudioProvider *provider = nullptr;
agi::signal::Connection provider_connection;
void OnAudioProvider(AudioProvider *new_provider);
void OnAudioProvider(agi::AudioProvider *new_provider);
/// Event handler for the playback timer
void OnPlaybackTimer(wxTimerEvent &event);
@ -189,29 +189,3 @@ public:
DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener)
DEFINE_SIGNAL_ADDERS(AnnounceAudioPlayerOpened, AddAudioPlayerOpenListener)
};
namespace agi {
/// Base class for all audio-related errors
DEFINE_EXCEPTION(AudioError, Exception);
/// Opening the audio failed for any reason
DEFINE_EXCEPTION(AudioOpenError, AudioError);
/// There are no audio providers available to open audio files
DEFINE_EXCEPTION(NoAudioProvidersError, AudioOpenError);
/// The file exists, but no providers could find any audio tracks in it
DEFINE_EXCEPTION(AudioDataNotFoundError, AudioOpenError);
/// There are audio tracks, but no provider could actually read them
DEFINE_EXCEPTION(AudioProviderOpenError, AudioOpenError);
/// The audio cache failed to initialize
DEFINE_EXCEPTION(AudioCacheOpenError, AudioOpenError);
/// There are no audio players available
DEFINE_EXCEPTION(NoAudioPlayersError, AudioOpenError);
/// The audio player failed to initialize
DEFINE_EXCEPTION(AudioPlayerOpenError, AudioOpenError);
}

View file

@ -37,7 +37,6 @@
#include "audio_timing.h"
#include "compat.h"
#include "format.h"
#include "include/aegisub/audio_provider.h"
#include "include/aegisub/context.h"
#include "include/aegisub/hotkey.h"
#include "options.h"
@ -46,6 +45,7 @@
#include "video_controller.h"
#include <libaegisub/ass/time.h>
#include <libaegisub/audio/provider.h>
#include <libaegisub/make_unique.h>
#include <algorithm>
@ -1171,7 +1171,7 @@ int AudioDisplay::GetDuration() const
return (provider->GetNumSamples() * 1000 + provider->GetSampleRate() - 1) / provider->GetSampleRate();
}
void AudioDisplay::OnAudioOpen(AudioProvider *provider)
void AudioDisplay::OnAudioOpen(agi::AudioProvider *provider)
{
this->provider = provider;

View file

@ -40,12 +40,12 @@
#include <wx/timer.h>
#include <wx/window.h>
namespace agi { class AudioProvider; }
namespace agi { struct Context; }
class AudioController;
class AudioRenderer;
class AudioRendererBitmapProvider;
class AudioProvider;
class TimeRange;
// Helper classes used in implementation of the audio display
@ -107,7 +107,7 @@ class AudioDisplay: public wxWindow {
/// The controller managing us
AudioController *controller = nullptr;
AudioProvider *provider = nullptr;
agi::AudioProvider *provider = nullptr;
/// Scrollbar helper object
std::unique_ptr<AudioDisplayScrollbar> scrollbar;
@ -230,7 +230,7 @@ class AudioDisplay: public wxWindow {
int GetDuration() const;
void OnAudioOpen(AudioProvider *provider);
void OnAudioOpen(agi::AudioProvider *provider);
void OnPlaybackPosition(int ms_position);
void OnSelectionChanged();
void OnStyleRangesChanged();

View file

@ -120,7 +120,7 @@ void AudioKaraoke::OnFileChanged(int type, std::set<const AssDialogue *> const&
}
}
void AudioKaraoke::OnAudioOpened(AudioProvider *provider) {
void AudioKaraoke::OnAudioOpened(agi::AudioProvider *provider) {
if (provider)
SetEnabled(enabled);
else

View file

@ -26,8 +26,8 @@
class AssDialogue;
class AssKaraoke;
class AudioProvider;
class wxButton;
namespace agi { class AudioProvider; }
namespace agi { struct Context; }
/// @class AudioKaraoke
@ -141,7 +141,7 @@ class AudioKaraoke final : public wxWindow {
void OnMouse(wxMouseEvent &event);
void OnPaint(wxPaintEvent &event);
void OnSize(wxSizeEvent &event);
void OnAudioOpened(AudioProvider *provider);
void OnAudioOpened(agi::AudioProvider *provider);
void OnScrollTimer(wxTimerEvent &event);
public:

View file

@ -40,23 +40,18 @@
#include <boost/range/iterator_range.hpp>
AudioPlayer::AudioPlayer(AudioProvider *provider)
: provider(provider)
{
}
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateDirectSound2Player(AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateOpenALPlayer(AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateOSSPlayer(AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(agi::AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(agi::AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateDirectSound2Player(agi::AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateOpenALPlayer(agi::AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(agi::AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(agi::AudioProvider *providers, wxWindow *window);
std::unique_ptr<AudioPlayer> CreateOSSPlayer(agi::AudioProvider *providers, wxWindow *window);
namespace {
struct factory {
const char *name;
std::unique_ptr<AudioPlayer> (*create)(AudioProvider *, wxWindow *window);
std::unique_ptr<AudioPlayer> (*create)(agi::AudioProvider *, wxWindow *window);
bool hidden;
};
@ -87,9 +82,9 @@ std::vector<std::string> AudioPlayerFactory::GetClasses() {
return ::GetClasses(boost::make_iterator_range(std::begin(factories), std::end(factories)));
}
std::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(AudioProvider *provider, wxWindow *window) {
std::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(agi::AudioProvider *provider, wxWindow *window) {
if (std::begin(factories) == std::end(factories))
throw agi::NoAudioPlayersError("No audio players are available.");
throw AudioPlayerOpenError("No audio players are available.");
auto preferred = OPT_GET("Audio/Player")->GetString();
auto sorted = GetSorted(boost::make_iterator_range(std::begin(factories), std::end(factories)), preferred);
@ -99,9 +94,9 @@ std::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(AudioProvider *p
try {
return factory->create(provider, window);
}
catch (agi::AudioPlayerOpenError const& err) {
catch (AudioPlayerOpenError const& err) {
error += std::string(factory->name) + " factory: " + err.GetMessage() + "\n";
}
}
throw agi::AudioPlayerOpenError(error);
throw AudioPlayerOpenError(error);
}

View file

@ -36,11 +36,11 @@
#include "include/aegisub/audio_player.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h"
#include "compat.h"
#include "frame_main.h"
#include "options.h"
#include <libaegisub/audio/provider.h>
#include <libaegisub/log.h>
#include <libaegisub/make_unique.h>
@ -102,7 +102,7 @@ class AlsaPlayer final : public AudioPlayer {
}
public:
AlsaPlayer(AudioProvider *provider);
AlsaPlayer(agi::AudioProvider *provider);
~AlsaPlayer();
void Play(int64_t start, int64_t count) override;
@ -289,13 +289,13 @@ do_setup:
}
}
AlsaPlayer::AlsaPlayer(AudioProvider *provider) try
AlsaPlayer::AlsaPlayer(agi::AudioProvider *provider) try
: AudioPlayer(provider)
, thread(&AlsaPlayer::PlaybackThread, this)
{
}
catch (std::system_error const&) {
throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed");
throw AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed");
}
AlsaPlayer::~AlsaPlayer()
@ -347,7 +347,7 @@ int64_t AlsaPlayer::GetCurrentPosition()
}
}
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(AudioProvider *provider, wxWindow *)
std::unique_ptr<AudioPlayer> CreateAlsaPlayer(agi::AudioProvider *provider, wxWindow *)
{
return agi::make_unique<AlsaPlayer>(provider);
}

View file

@ -36,10 +36,10 @@
#include "include/aegisub/audio_player.h"
#include "audio_controller.h"
#include "include/aegisub/audio_provider.h"
#include "frame_main.h"
#include "utils.h"
#include <libaegisub/audio/provider.h>
#include <libaegisub/log.h>
#include <libaegisub/make_unique.h>
@ -81,7 +81,7 @@ class DirectSoundPlayer final : public AudioPlayer {
DirectSoundPlayerThread *thread = nullptr;
public:
DirectSoundPlayer(AudioProvider *provider, wxWindow *parent);
DirectSoundPlayer(agi::AudioProvider *provider, wxWindow *parent);
~DirectSoundPlayer();
void Play(int64_t start,int64_t count);
@ -96,13 +96,13 @@ public:
void SetVolume(double vol) { volume = vol; }
};
DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider, wxWindow *parent)
DirectSoundPlayer::DirectSoundPlayer(agi::AudioProvider *provider, wxWindow *parent)
: AudioPlayer(provider)
{
// Initialize the DirectSound object
HRESULT res;
res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,nullptr); // TODO: support selecting audio device
if (FAILED(res)) throw agi::AudioPlayerOpenError("Failed initializing DirectSound");
if (FAILED(res)) throw AudioPlayerOpenError("Failed initializing DirectSound");
// Set DirectSound parameters
directSound->SetCooperativeLevel((HWND)parent->GetHandle(),DSSCL_PRIORITY);
@ -133,11 +133,11 @@ DirectSoundPlayer::DirectSoundPlayer(AudioProvider *provider, wxWindow *parent)
// Create the buffer
IDirectSoundBuffer *buf;
res = directSound->CreateSoundBuffer(&desc,&buf,nullptr);
if (res != DS_OK) throw agi::AudioPlayerOpenError("Failed creating DirectSound buffer");
if (res != DS_OK) throw AudioPlayerOpenError("Failed creating DirectSound buffer");
// Copy interface to buffer
res = buf->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*) &buffer);
if (res != S_OK) throw agi::AudioPlayerOpenError("Failed casting interface to IDirectSoundBuffer8");
if (res != S_OK) throw AudioPlayerOpenError("Failed casting interface to IDirectSoundBuffer8");
// Set data
offset = 0;
@ -367,7 +367,7 @@ void DirectSoundPlayerThread::Stop() {
}
}
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(AudioProvider *provider, wxWindow *parent) {
std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(