mirror of https://github.com/odrling/Aegisub
Implement wangqr Audio Changes:
- To allow for XAudio2 to work properly, we need to rework how does provider work since they only are used to be able to take in mono audio. - Other providers have been dumbed down to accept single channel audio since originally aegisub only accepted 1 channel audio. - meson.build has been modified to accommodate for xaudio, as we currently don't accept redistributable forms of xaudio, we need to work around the WinNT version. - There has been 1 more fix to res.rc to allow for compiling on non tagged releases.
This commit is contained in:
parent
3dfea0c315
commit
fd28458ed8
|
@ -497,6 +497,7 @@ set_property(SOURCE src/subtitles_provider_csri.cpp PROPERTY INCLUDE_DIRECTORIES
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_link_libraries (Aegisub dsound)
|
target_link_libraries (Aegisub dsound)
|
||||||
add_definitions("-DWITH_DIRECTSOUND")
|
add_definitions("-DWITH_DIRECTSOUND")
|
||||||
|
add_definitions("-DWITH_XAUDIO2")
|
||||||
target_sources(Aegisub PRIVATE src/audio_player_dsound.cpp src/audio_player_dsound2.cpp src/audio_player_xaudio2.cpp)
|
target_sources(Aegisub PRIVATE src/audio_player_dsound.cpp src/audio_player_dsound2.cpp src/audio_player_xaudio2.cpp)
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,108 @@
|
||||||
#include "libaegisub/log.h"
|
#include "libaegisub/log.h"
|
||||||
#include "libaegisub/util.h"
|
#include "libaegisub/util.h"
|
||||||
|
|
||||||
namespace agi {
|
namespace {
|
||||||
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
|
|
||||||
GetAudio(buf, start, count);
|
|
||||||
|
|
||||||
|
template<typename Source>
|
||||||
|
class ConvertFloatToInt16 {
|
||||||
|
Source* src;
|
||||||
|
public:
|
||||||
|
ConvertFloatToInt16(Source* src) :src(src) {}
|
||||||
|
int16_t operator[](size_t idx) const {
|
||||||
|
Source expanded = src[idx] * 32768;
|
||||||
|
return expanded < -32768 ? -32768 :
|
||||||
|
expanded > 32767 ? 32767 :
|
||||||
|
static_cast<int16_t>(expanded);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 8 bits per sample is assumed to be unsigned with a bias of 128,
|
||||||
|
// while everything else is assumed to be signed with zero bias
|
||||||
|
class ConvertIntToInt16 {
|
||||||
|
void* src;
|
||||||
|
int bytes_per_sample;
|
||||||
|
public:
|
||||||
|
ConvertIntToInt16(void* src, int bytes_per_sample) :src(src), bytes_per_sample(bytes_per_sample) {}
|
||||||
|
const int16_t& operator[](size_t idx) const {
|
||||||
|
return *reinterpret_cast<int16_t*>(reinterpret_cast<char*>(src) + (idx + 1) * bytes_per_sample - sizeof(int16_t));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class ConvertUInt8ToInt16 {
|
||||||
|
uint8_t* src;
|
||||||
|
public:
|
||||||
|
ConvertUInt8ToInt16(uint8_t* src) :src(src) {}
|
||||||
|
int16_t operator[](size_t idx) const {
|
||||||
|
return int16_t(src[idx]-128) << 8;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Source>
|
||||||
|
class DownmixToMono {
|
||||||
|
Source src;
|
||||||
|
int channels;
|
||||||
|
public:
|
||||||
|
DownmixToMono(Source src, int channels) :src(src), channels(channels) {}
|
||||||
|
int16_t operator[](size_t idx) const {
|
||||||
|
int ret = 0;
|
||||||
|
// Just average the channels together
|
||||||
|
for (int i = 0; i < channels; ++i)
|
||||||
|
ret += src[idx * channels + i];
|
||||||
|
return ret / channels;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace agi {
|
||||||
|
|
||||||
|
void AudioProvider::FillBufferInt16Mono(int16_t* buf, int64_t start, int64_t count) const {
|
||||||
|
if (!float_samples && bytes_per_sample == 2 && channels == 1) {
|
||||||
|
FillBuffer(buf, start, count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void* buff = malloc(bytes_per_sample * count * channels);
|
||||||
|
FillBuffer(buff, start, count);
|
||||||
|
if (channels == 1) {
|
||||||
|
if (float_samples) {
|
||||||
|
if (bytes_per_sample == sizeof(float))
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = ConvertFloatToInt16<float>(reinterpret_cast<float*>(buff))[i];
|
||||||
|
else if (bytes_per_sample == sizeof(double))
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = ConvertFloatToInt16<double>(reinterpret_cast<double*>(buff))[i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (bytes_per_sample == sizeof(uint8_t))
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = ConvertUInt8ToInt16(reinterpret_cast<uint8_t*>(buff))[i];
|
||||||
|
else
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = ConvertIntToInt16(buff, bytes_per_sample)[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (float_samples) {
|
||||||
|
if (bytes_per_sample == sizeof(float))
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = DownmixToMono<ConvertFloatToInt16<float> >(ConvertFloatToInt16<float>(reinterpret_cast<float*>(buff)), channels)[i];
|
||||||
|
else if (bytes_per_sample == sizeof(double))
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = DownmixToMono<ConvertFloatToInt16<double> >(ConvertFloatToInt16<double>(reinterpret_cast<double*>(buff)), channels)[i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (bytes_per_sample == sizeof(uint8_t))
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = DownmixToMono<ConvertUInt8ToInt16>(ConvertUInt8ToInt16(reinterpret_cast<uint8_t*>(buff)), channels)[i];
|
||||||
|
else
|
||||||
|
for (int64_t i = 0; i < count; ++i)
|
||||||
|
buf[i] = DownmixToMono<ConvertIntToInt16>(ConvertIntToInt16(buff, bytes_per_sample), channels)[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioProvider::GetInt16MonoAudioWithVolume(int16_t *buf, int64_t start, int64_t count, double volume) const {
|
||||||
|
GetInt16MonoAudio(buf, start, count);
|
||||||
if (volume == 1.0) return;
|
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);
|
auto buffer = static_cast<int16_t *>(buf);
|
||||||
for (size_t i = 0; i < (size_t)count; ++i)
|
for (size_t i = 0; i < (size_t)count; ++i)
|
||||||
|
@ -75,6 +170,39 @@ void AudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioProvider::GetInt16MonoAudio(int16_t* buf, int64_t start, int64_t count) const {
|
||||||
|
if (start < 0) {
|
||||||
|
memset(buf, 0, sizeof(int16_t) * std::min(-start, count));
|
||||||
|
buf -= start;
|
||||||
|
count += start;
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start + count > num_samples) {
|
||||||
|
int64_t zero_count = std::min(count, start + count - num_samples);
|
||||||
|
count -= zero_count;
|
||||||
|
memset(buf + count, 0, sizeof(int16_t) * zero_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count <= 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
FillBufferInt16Mono(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();
|
||||||
|
memset(buf, 0, sizeof(int16_t) * count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
LOG_E("audio_provider") << "Unknown audio decoding error";
|
||||||
|
memset(buf, 0, sizeof(int16_t) * count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class writer {
|
class writer {
|
||||||
io::Save outfile;
|
io::Save outfile;
|
||||||
|
|
|
@ -22,119 +22,19 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
using namespace agi;
|
using namespace agi;
|
||||||
|
|
||||||
/// Anything integral -> 16 bit signed machine-endian audio converter
|
|
||||||
namespace {
|
namespace {
|
||||||
template<class Target>
|
class ConvertAudioProvider final : public AudioProviderWrapper {
|
||||||
class BitdepthConvertAudioProvider final : public AudioProviderWrapper {
|
|
||||||
int src_bytes_per_sample;
|
|
||||||
mutable std::vector<uint8_t> src_buf;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BitdepthConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
|
ConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
|
||||||
if (bytes_per_sample > 8)
|
|
||||||
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 count64) const override {
|
|
||||||
auto count = static_cast<size_t>(count64);
|
|
||||||
assert(count == count64);
|
|
||||||
|
|
||||||
src_buf.resize(count * src_bytes_per_sample * channels);
|
|
||||||
source->GetAudio(src_buf.data(), start, count);
|
|
||||||
|
|
||||||
auto dest = static_cast<int16_t*>(buf);
|
|
||||||
|
|
||||||
for (int64_t i = 0; i < count * channels; ++i) {
|
|
||||||
int64_t sample = 0;
|
|
||||||
|
|
||||||
// 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 = src_buf[i] - 128;
|
|
||||||
else {
|
|
||||||
for (int j = src_bytes_per_sample; j > 0; --j) {
|
|
||||||
sample <<= 8;
|
|
||||||
sample += src_buf[i * src_bytes_per_sample + j - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (static_cast<size_t>(src_bytes_per_sample) > sizeof(Target))
|
|
||||||
sample /= 1LL << (src_bytes_per_sample - sizeof(Target)) * 8;
|
|
||||||
else if (static_cast<size_t>(src_bytes_per_sample) < sizeof(Target))
|
|
||||||
sample *= 1LL << (sizeof(Target) - src_bytes_per_sample ) * 8;
|
|
||||||
|
|
||||||
dest[i] = static_cast<Target>(sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Floating point -> 16 bit signed machine-endian audio converter
|
|
||||||
template<class Source, class Target>
|
|
||||||
class FloatConvertAudioProvider final : public AudioProviderWrapper {
|
|
||||||
mutable std::vector<Source> src_buf;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FloatConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
|
|
||||||
bytes_per_sample = sizeof(Target);
|
|
||||||
float_samples = false;
|
float_samples = false;
|
||||||
}
|
|
||||||
|
|
||||||
void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
|
|
||||||
auto count = static_cast<size_t>(count64);
|
|
||||||
assert(count == count64);
|
|
||||||
|
|
||||||
src_buf.resize(count * channels);
|
|
||||||
source->GetAudio(&src_buf[0], start, count);
|
|
||||||
|
|
||||||
auto dest = static_cast<Target*>(buf);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(count * channels); ++i) {
|
|
||||||
Source expanded;
|
|
||||||
if (src_buf[i] < 0)
|
|
||||||
expanded = static_cast<Target>(-src_buf[i] * std::numeric_limits<Target>::min());
|
|
||||||
else
|
|
||||||
expanded = static_cast<Target>(src_buf[i] * std::numeric_limits<Target>::max());
|
|
||||||
|
|
||||||
dest[i] = expanded < std::numeric_limits<Target>::min() ? std::numeric_limits<Target>::min() :
|
|
||||||
expanded > std::numeric_limits<Target>::max() ? std::numeric_limits<Target>::max() :
|
|
||||||
static_cast<Target>(expanded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Non-mono 16-bit signed machine-endian -> mono 16-bit signed machine endian converter
|
|
||||||
class DownmixAudioProvider final : public AudioProviderWrapper {
|
|
||||||
int src_channels;
|
|
||||||
mutable std::vector<int16_t> src_buf;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DownmixAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
|
|
||||||
src_channels = channels;
|
|
||||||
channels = 1;
|
channels = 1;
|
||||||
|
bytes_per_sample = sizeof(int16_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
|
void FillBuffer(void *buf, int64_t start, int64_t count) const override {
|
||||||
auto count = static_cast<size_t>(count64);
|
source->GetInt16MonoAudio(reinterpret_cast<int16_t*>(buf), start, count);
|
||||||
assert(count == count64);
|
|
||||||
|
|
||||||
src_buf.resize(count * src_channels);
|
|
||||||
source->GetAudio(&src_buf[0], start, count);
|
|
||||||
|
|
||||||
auto dst = static_cast<int16_t*>(buf);
|
|
||||||
// Just average the channels together
|
|
||||||
while (count-- > 0) {
|
|
||||||
int sum = 0;
|
|
||||||
for (int c = 0; c < src_channels; ++c)
|
|
||||||
sum += src_buf[count * src_channels + c];
|
|
||||||
dst[count] = static_cast<int16_t>(sum / src_channels);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sample doubler with linear interpolation for the samples provider
|
/// Sample doubler with linear interpolation for the samples provider
|
||||||
/// Requires 16-bit mono input
|
/// Requires 16-bit mono input
|
||||||
class SampleDoublingAudioProvider final : public AudioProviderWrapper {
|
class SampleDoublingAudioProvider final : public AudioProviderWrapper {
|
||||||
|
@ -177,26 +77,23 @@ std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioP
|
||||||
// Ensure 16-bit audio with proper endianness
|
// Ensure 16-bit audio with proper endianness
|
||||||
if (provider->AreSamplesFloat()) {
|
if (provider->AreSamplesFloat()) {
|
||||||
LOG_D("audio_provider") << "Converting float to S16";
|
LOG_D("audio_provider") << "Converting float to S16";
|
||||||
if (provider->GetBytesPerSample() == sizeof(float))
|
|
||||||
provider = agi::make_unique<FloatConvertAudioProvider<float, int16_t>>(std::move(provider));
|
|
||||||
else
|
|
||||||
provider = agi::make_unique<FloatConvertAudioProvider<double, int16_t>>(std::move(provider));
|
|
||||||
}
|
}
|
||||||
if (provider->GetBytesPerSample() != 2) {
|
if (provider->GetBytesPerSample() != 2) {
|
||||||
LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample or wrong endian to S16";
|
LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample to S16";
|
||||||
provider = agi::make_unique<BitdepthConvertAudioProvider<int16_t>>(std::move(provider));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We currently only support mono audio
|
// We currently only support mono audio
|
||||||
if (provider->GetChannels() != 1) {
|
if (provider->GetChannels() != 1) {
|
||||||
LOG_D("audio_provider") << "Downmixing to mono from " << provider->GetChannels() << " channels";
|
LOG_D("audio_provider") << "Downmixing to mono from " << provider->GetChannels() << " channels";
|
||||||
provider = agi::make_unique<DownmixAudioProvider>(std::move(provider));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some players don't like low sample rate audio
|
// Some players don't like low sample rate audio
|
||||||
while (provider->GetSampleRate() < 32000) {
|
if (provider->GetSampleRate() < 32000) {
|
||||||
LOG_D("audio_provider") << "Doubling sample rate";
|
provider = agi::make_unique<ConvertAudioProvider>(std::move(provider));
|
||||||
provider = agi::make_unique<SampleDoublingAudioProvider>(std::move(provider));
|
while (provider->GetSampleRate() < 32000) {
|
||||||
|
LOG_D("audio_provider") << "Doubling sample rate";
|
||||||
|
provider = agi::make_unique<SampleDoublingAudioProvider>(std::move(provider));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
|
|
|
@ -43,15 +43,15 @@ class HDAudioProvider final : public AudioProviderWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
start *= bytes_per_sample;
|
start *= bytes_per_sample * channels;
|
||||||
count *= bytes_per_sample;
|
count *= bytes_per_sample * channels;
|
||||||
memcpy(buf, file.read(start, count), count);
|
memcpy(buf, file.read(start, count), count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path CacheFilename(fs::path const& dir) {
|
fs::path CacheFilename(fs::path const& dir) {
|
||||||
// Check free space
|
// Check free space
|
||||||
if ((uint64_t)num_samples * bytes_per_sample > fs::FreeSpace(dir))
|
if ((uint64_t)num_samples * bytes_per_sample * channels > fs::FreeSpace(dir))
|
||||||
throw AudioProviderError("Not enough free disk space in " + dir.string() + " to cache the audio");
|
throw AudioProviderError("Not enough free disk space in " + dir.string() + " to cache the audio");
|
||||||
|
|
||||||
return format("audio-%lld-%lld", time(nullptr),
|
return format("audio-%lld-%lld", time(nullptr),
|
||||||
|
@ -61,7 +61,7 @@ class HDAudioProvider final : public AudioProviderWrapper {
|
||||||
public:
|
public:
|
||||||
HDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const& dir)
|
HDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const& dir)
|
||||||
: AudioProviderWrapper(std::move(src))
|
: AudioProviderWrapper(std::move(src))
|
||||||
, file(dir / CacheFilename(dir), num_samples * bytes_per_sample)
|
, file(dir / CacheFilename(dir), num_samples * bytes_per_sample * channels)
|
||||||
{
|
{
|
||||||
decoded_samples = 0;
|
decoded_samples = 0;
|
||||||
decoder = std::thread([&] {
|
decoder = std::thread([&] {
|
||||||
|
|
|
@ -71,20 +71,22 @@ public:
|
||||||
|
|
||||||
void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
|
void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
|
||||||
auto charbuf = static_cast<char *>(buf);
|
auto charbuf = static_cast<char *>(buf);
|
||||||
for (int64_t bytes_remaining = count * bytes_per_sample; bytes_remaining; ) {
|
for (int64_t bytes_remaining = count * bytes_per_sample * channels; bytes_remaining; ) {
|
||||||
if (start >= decoded_samples) {
|
if (start >= decoded_samples) {
|
||||||
memset(charbuf, 0, bytes_remaining);
|
memset(charbuf, 0, bytes_remaining);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int i = (start * bytes_per_sample) >> CacheBits;
|
const int64_t samples_per_block = CacheBlockSize / bytes_per_sample / channels;
|
||||||
const int start_offset = (start * bytes_per_sample) & (CacheBlockSize-1);
|
|
||||||
const int read_size = std::min<int>(bytes_remaining, CacheBlockSize - start_offset);
|
const size_t i = start / samples_per_block;
|
||||||
|
const int start_offset = (start % samples_per_block) * bytes_per_sample * channels;
|
||||||
|
const int read_size = std::min<int>(bytes_remaining, samples_per_block * bytes_per_sample * channels - start_offset);
|
||||||
|
|
||||||
memcpy(charbuf, &blockcache[i][start_offset], read_size);
|
memcpy(charbuf, &blockcache[i][start_offset], read_size);
|
||||||
charbuf += read_size;
|
charbuf += read_size;
|
||||||
bytes_remaining -= read_size;
|
bytes_remaining -= read_size;
|
||||||
start += read_size / bytes_per_sample;
|
start += read_size / bytes_per_sample / channels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include <libaegisub/fs_fwd.h>
|
#include <libaegisub/fs_fwd.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
|
@ -37,6 +36,7 @@ protected:
|
||||||
bool float_samples = false;
|
bool float_samples = false;
|
||||||
|
|
||||||
virtual void FillBuffer(void *buf, int64_t start, int64_t count) const = 0;
|
virtual void FillBuffer(void *buf, int64_t start, int64_t count) const = 0;
|
||||||
|
virtual void FillBufferInt16Mono(int16_t* buf, int64_t start, int64_t count) const;
|
||||||
|
|
||||||
void ZeroFill(void *buf, int64_t count) const;
|
void ZeroFill(void *buf, int64_t count) const;
|
||||||
|
|
||||||
|
@ -44,7 +44,8 @@ public:
|
||||||
virtual ~AudioProvider() = default;
|
virtual ~AudioProvider() = default;
|
||||||
|
|
||||||
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
void GetAudio(void *buf, int64_t start, int64_t count) const;
|
||||||
void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const;
|
void GetInt16MonoAudio(int16_t* buf, int64_t start, int64_t count) const;
|
||||||
|
void GetInt16MonoAudioWithVolume(int16_t *buf, int64_t start, int64_t count, double volume) const;
|
||||||
|
|
||||||
int64_t GetNumSamples() const { return num_samples; }
|
int64_t GetNumSamples() const { return num_samples; }
|
||||||
int64_t GetDecodedSamples() const { return decoded_samples; }
|
int64_t GetDecodedSamples() const { return decoded_samples; }
|
||||||
|
@ -93,4 +94,4 @@ std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvid
|
||||||
std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> source_provider);
|
std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> source_provider);
|
||||||
|
|
||||||
void SaveAudioClip(AudioProvider const& provider, fs::path const& path, int start_time, int end_time);
|
void SaveAudioClip(AudioProvider const& provider, fs::path const& path, int start_time, int end_time);
|
||||||
}
|
}
|
14
meson.build
14
meson.build
|
@ -7,7 +7,7 @@ project('Aegisub', ['c', 'cpp'],
|
||||||
cmake = import('cmake')
|
cmake = import('cmake')
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
add_project_arguments('-DNOMINMAX', '-D_WIN32_WINNT=0x0601', language: 'cpp')
|
add_project_arguments('-DNOMINMAX', language: 'cpp')
|
||||||
|
|
||||||
if not get_option('csri').disabled()
|
if not get_option('csri').disabled()
|
||||||
add_global_arguments('-DCSRI_NO_EXPORT', language: 'c')
|
add_global_arguments('-DCSRI_NO_EXPORT', language: 'c')
|
||||||
|
@ -259,16 +259,26 @@ if host_machine.system() == 'windows'
|
||||||
if not get_option('xaudio2').disabled()
|
if not get_option('xaudio2').disabled()
|
||||||
have_xaudio_h = cc.has_header('xaudio2.h')
|
have_xaudio_h = cc.has_header('xaudio2.h')
|
||||||
xaudio2_dep = cc.find_library('xaudio2', required: true)
|
xaudio2_dep = cc.find_library('xaudio2', required: true)
|
||||||
if have_xaudio_h
|
if have_xaudio_h and xaudio2_dep.found()
|
||||||
deps += [xaudio2_dep]
|
deps += [xaudio2_dep]
|
||||||
conf.set('WITH_XAUDIO2', 1)
|
conf.set('WITH_XAUDIO2', 1)
|
||||||
dep_avail += 'XAudio2'
|
dep_avail += 'XAudio2'
|
||||||
|
# XAudio2 needs Windows 8 or newer, so we tell meson not to define an older windows or else it can break things.
|
||||||
|
add_project_arguments('-D_WIN32_WINNT=0x0602', language: 'cpp')
|
||||||
|
else
|
||||||
|
# Windows 8 not required if XAudio2 fails to be found. revert for compat.
|
||||||
|
add_project_arguments('-D_WIN32_WINNT=0x0601', language: 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if not have_dsound_h and get_option('xaudio2').enabled()
|
if not have_dsound_h and get_option('xaudio2').enabled()
|
||||||
error('xaudio2 enabled but xaudio2.h not found')
|
error('xaudio2 enabled but xaudio2.h not found')
|
||||||
endif
|
endif
|
||||||
|
else
|
||||||
|
# Windows 8 not required if XAudio2 is disabled. revert for compat.
|
||||||
|
add_project_arguments('-D_WIN32_WINNT=0x0601', language: 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if host_machine.system() == 'darwin'
|
if host_machine.system() == 'darwin'
|
||||||
|
|
|
@ -127,7 +127,7 @@ void AlsaPlayer::PlaybackThread()
|
||||||
|
|
||||||
do_setup:
|
do_setup:
|
||||||
snd_pcm_format_t pcm_format;
|
snd_pcm_format_t pcm_format;
|
||||||
switch (provider->GetBytesPerSample())
|
switch (/*provider->GetBytesPerSample()*/ sizeof(int16_t))
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
LOG_D("audio/player/alsa") << "format U8";
|
LOG_D("audio/player/alsa") << "format U8";
|
||||||
|
@ -143,7 +143,7 @@ do_setup:
|
||||||
if (snd_pcm_set_params(pcm,
|
if (snd_pcm_set_params(pcm,
|
||||||
pcm_format,
|
pcm_format,
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED,
|
SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||||
provider->GetChannels(),
|
/*provider->GetChannels()*/ 1,
|
||||||
provider->GetSampleRate(),
|
provider->GetSampleRate(),
|
||||||
1, // allow resample
|
1, // allow resample
|
||||||
100*1000 // 100 milliseconds latency
|
100*1000 // 100 milliseconds latency
|
||||||
|
@ -151,7 +151,8 @@ do_setup:
|
||||||
return;
|
return;
|
||||||
LOG_D("audio/player/alsa") << "set pcm params";
|
LOG_D("audio/player/alsa") << "set pcm params";
|
||||||
|
|
||||||
size_t framesize = provider->GetChannels() * provider->GetBytesPerSample();
|
//size_t framesize = provider->GetChannels() * provider->GetBytesPerSample();
|
||||||
|
size_t framesize = sizeof(int16_t);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -175,7 +176,7 @@ do_setup:
|
||||||
{
|
{
|
||||||
auto avail = std::min(snd_pcm_avail(pcm), (snd_pcm_sframes_t)(end_position-position));
|
auto avail = std::min(snd_pcm_avail(pcm), (snd_pcm_sframes_t)(end_position-position));
|
||||||
decode_buffer.resize(avail * framesize);
|
decode_buffer.resize(avail * framesize);
|
||||||
provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume);
|
provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(decode_buffer.data()), position, avail, volume);
|
||||||
|
|
||||||
snd_pcm_sframes_t written = 0;
|
snd_pcm_sframes_t written = 0;
|
||||||
while (written <= 0)
|
while (written <= 0)
|
||||||
|
@ -235,7 +236,7 @@ do_setup:
|
||||||
|
|
||||||
{
|
{
|
||||||
decode_buffer.resize(avail * framesize);
|
decode_buffer.resize(avail * framesize);
|
||||||
provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume);
|
provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(decode_buffer.data()), position, avail, volume);
|
||||||
snd_pcm_sframes_t written = 0;
|
snd_pcm_sframes_t written = 0;
|
||||||
while (written <= 0)
|
while (written <= 0)
|
||||||
{
|
{
|
||||||
|
@ -352,4 +353,4 @@ std::unique_ptr<AudioPlayer> CreateAlsaPlayer(agi::AudioProvider *provider, wxWi
|
||||||
return agi::make_unique<AlsaPlayer>(provider);
|
return agi::make_unique<AlsaPlayer>(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_ALSA
|
#endif // WITH_ALSA
|
|
@ -45,6 +45,7 @@
|
||||||
|
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
#include <dsound.h>
|
#include <dsound.h>
|
||||||
|
#include <cguid.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class DirectSoundPlayer;
|
class DirectSoundPlayer;
|
||||||
|
@ -111,8 +112,10 @@ DirectSoundPlayer::DirectSoundPlayer(agi::AudioProvider *provider, wxWindow *par
|
||||||
WAVEFORMATEX waveFormat;
|
WAVEFORMATEX waveFormat;
|
||||||
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
waveFormat.nSamplesPerSec = provider->GetSampleRate();
|
waveFormat.nSamplesPerSec = provider->GetSampleRate();
|
||||||
waveFormat.nChannels = provider->GetChannels();
|
//waveFormat.nChannels = provider->GetChannels();
|
||||||
waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
|
//waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
|
||||||
|
waveFormat.nChannels = 1;
|
||||||
|
waveFormat.wBitsPerSample = sizeof(int16_t) * 8;
|
||||||
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
||||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
||||||
waveFormat.cbSize = sizeof(waveFormat);
|
waveFormat.cbSize = sizeof(waveFormat);
|
||||||
|
@ -160,7 +163,7 @@ bool DirectSoundPlayer::FillBuffer(bool fill) {
|
||||||
HRESULT res;
|
HRESULT res;
|
||||||
void *ptr1, *ptr2;
|
void *ptr1, *ptr2;
|
||||||
unsigned long int size1, size2;
|
unsigned long int size1, size2;
|
||||||
int bytesps = provider->GetBytesPerSample();
|
int bytesps = /*provider->GetBytesPerSample()*/ sizeof(int16_t);
|
||||||
|
|
||||||
// To write length
|
// To write length
|
||||||
int toWrite = 0;
|
int toWrite = 0;
|
||||||
|
@ -223,8 +226,8 @@ RetryLock:
|
||||||
LOG_D_IF(!count1 && !count2, "audio/player/dsound1") << "DS fill: nothing";
|
LOG_D_IF(!count1 && !count2, "audio/player/dsound1") << "DS fill: nothing";
|
||||||
|
|
||||||
// Get source wave
|
// Get source wave
|
||||||
if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume);
|
if (count1) provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(ptr1), playPos, count1, volume);
|
||||||
if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume);
|
if (count2) provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(ptr2), playPos+count1, count2, volume);
|
||||||
playPos += count1+count2;
|
playPos += count1+count2;
|
||||||
|
|
||||||
buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
|
buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
|
||||||
|
@ -254,7 +257,7 @@ void DirectSoundPlayer::Play(int64_t start,int64_t count) {
|
||||||
FillBuffer(true);
|
FillBuffer(true);
|
||||||
|
|
||||||
DWORD play_flag = 0;
|
DWORD play_flag = 0;
|
||||||
if (count*provider->GetBytesPerSample() > bufSize) {
|
if (count*/*provider->GetBytesPerSample()*/sizeof(int16_t) > bufSize) {
|
||||||
// Start thread
|
// Start thread
|
||||||
thread = new DirectSoundPlayerThread(this);
|
thread = new DirectSoundPlayerThread(this);
|
||||||
thread->Create();
|
thread->Create();
|
||||||
|
@ -371,4 +374,4 @@ std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(agi::AudioProvider *provide
|
||||||
return agi::make_unique<DirectSoundPlayer>(provider, parent);
|
return agi::make_unique<DirectSoundPlayer>(provider, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_DIRECTSOUND
|
#endif // WITH_DIRECTSOUND
|
|
@ -48,6 +48,7 @@
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <dsound.h>
|
#include <dsound.h>
|
||||||
|
#include <cguid.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class DirectSoundPlayer2Thread;
|
class DirectSoundPlayer2Thread;
|
||||||
|
@ -319,8 +320,10 @@ void DirectSoundPlayer2Thread::Run()
|
||||||
WAVEFORMATEX waveFormat;
|
WAVEFORMATEX waveFormat;
|
||||||
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
waveFormat.nSamplesPerSec = provider->GetSampleRate();
|
waveFormat.nSamplesPerSec = provider->GetSampleRate();
|
||||||
waveFormat.nChannels = provider->GetChannels();
|
//waveFormat.nChannels = provider->GetChannels();
|
||||||
waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
|
//waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
|
||||||
|
waveFormat.nChannels = 1;
|
||||||
|
waveFormat.wBitsPerSample = sizeof(int16_t) * 8;
|
||||||
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
||||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
||||||
waveFormat.cbSize = sizeof(waveFormat);
|
waveFormat.cbSize = sizeof(waveFormat);
|
||||||
|
@ -369,7 +372,7 @@ void DirectSoundPlayer2Thread::Run()
|
||||||
DWORD buffer_offset = 0;
|
DWORD buffer_offset = 0;
|
||||||
bool playback_should_be_running = false;
|
bool playback_should_be_running = false;
|
||||||
int current_latency = wanted_latency;
|
int current_latency = wanted_latency;
|
||||||
const DWORD wanted_latency_bytes = wanted_latency*waveFormat.nSamplesPerSec*provider->GetBytesPerSample()/1000;
|
const DWORD wanted_latency_bytes = wanted_latency*waveFormat.nSamplesPerSec*/*provider->GetBytesPerSample()*/sizeof(int16_t)/1000;
|
||||||
|
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
|
@ -422,7 +425,7 @@ void DirectSoundPlayer2Thread::Run()
|
||||||
if (bytes_filled < wanted_latency_bytes)
|
if (bytes_filled < wanted_latency_bytes)
|
||||||
{
|
{
|
||||||
// Very short playback length, do without streaming playback
|
// Very short playback length, do without streaming playback
|
||||||
current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample());
|
current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*/*provider->GetBytesPerSample()*/sizeof(int16_t));
|
||||||
if (FAILED(bfr->Play(0, 0, 0)))
|
if (FAILED(bfr->Play(0, 0, 0)))
|
||||||
REPORT_ERROR("Could not start single-buffer playback.")
|
REPORT_ERROR("Could not start single-buffer playback.")
|
||||||
}
|
}
|
||||||
|
@ -553,7 +556,7 @@ do_fill_buffer:
|
||||||
else if (bytes_filled < wanted_latency_bytes)
|
else if (bytes_filled < wanted_latency_bytes)
|
||||||
{
|
{
|
||||||
// Didn't fill as much as we wanted to, let's get back to filling sooner than normal
|
// Didn't fill as much as we wanted to, let's get back to filling sooner than normal
|
||||||
current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample());
|
current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*/*provider->GetBytesPerSample()*/sizeof(int16_t));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -577,7 +580,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v
|
||||||
{
|
{
|
||||||
// Assume buffers have been locked and are ready to be filled
|
// Assume buffers have been locked and are ready to be filled
|
||||||
|
|
||||||
DWORD bytes_per_frame = provider->GetChannels() * provider->GetBytesPerSample();
|
DWORD bytes_per_frame = /*provider->GetChannels() * provider->GetBytesPerSample()*/sizeof(int16_t);
|
||||||
DWORD buf1szf = buf1sz / bytes_per_frame;
|
DWORD buf1szf = buf1sz / bytes_per_frame;
|
||||||
DWORD buf2szf = buf2sz / bytes_per_frame;
|
DWORD buf2szf = buf2sz / bytes_per_frame;
|
||||||
|
|
||||||
|
@ -608,7 +611,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v
|
||||||
buf2sz = 0;
|
buf2sz = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
provider->GetAudioWithVolume(buf1, input_frame, buf1szf, volume);
|
provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(buf1), input_frame, buf1szf, volume);
|
||||||
|
|
||||||
input_frame += buf1szf;
|
input_frame += buf1szf;
|
||||||
}
|
}
|
||||||
|
@ -621,7 +624,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v
|
||||||
buf2sz = buf2szf * bytes_per_frame;
|
buf2sz = buf2szf * bytes_per_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
provider->GetAudioWithVolume(buf2, input_frame, buf2szf, volume);
|
provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(buf2), input_frame, buf2szf, volume);
|
||||||
|
|
||||||
input_frame += buf2szf;
|
input_frame += buf2szf;
|
||||||
}
|
}
|
||||||
|
@ -932,4 +935,4 @@ std::unique_ptr<AudioPlayer> CreateDirectSound2Player(agi::AudioProvider *provid
|
||||||
return agi::make_unique<DirectSoundPlayer2>(provider, parent);
|
return agi::make_unique<DirectSoundPlayer2>(provider, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_DIRECTSOUND
|
#endif // WITH_DIRECTSOUND
|
|
@ -125,7 +125,7 @@ public:
|
||||||
OpenALPlayer::OpenALPlayer(agi::AudioProvider *provider)
|
OpenALPlayer::OpenALPlayer(agi::AudioProvider *provider)
|
||||||
: AudioPlayer(provider)
|
: AudioPlayer(provider)
|
||||||
, samplerate(provider->GetSampleRate())
|
, samplerate(provider->GetSampleRate())
|
||||||
, bpf(provider->GetChannels() * provider->GetBytesPerSample())
|
, bpf(/*provider->GetChannels() * provider->GetBytesPerSample()*/sizeof(int16_t))
|
||||||
{
|
{
|
||||||
device = alcOpenDevice(nullptr);
|
device = alcOpenDevice(nullptr);
|
||||||
if (!device) throw AudioPlayerOpenError("Failed opening default OpenAL device");
|
if (!device) throw AudioPlayerOpenError("Failed opening default OpenAL device");
|
||||||
|
@ -241,7 +241,7 @@ void OpenALPlayer::FillBuffers(ALsizei count)
|
||||||
|
|
||||||
if (fill_len > 0)
|
if (fill_len > 0)
|
||||||
// Get fill_len frames of audio
|
// Get fill_len frames of audio
|
||||||
provider->GetAudioWithVolume(&decode_buffer[0], cur_frame, fill_len, volume);
|
provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(decode_buffer.data()), cur_frame, fill_len, volume);
|
||||||
if ((size_t)fill_len * bpf < decode_buffer.size())
|
if ((size_t)fill_len * bpf < decode_buffer.size())
|
||||||
// And zerofill the rest
|
// And zerofill the rest
|
||||||
memset(&decode_buffer[fill_len * bpf], 0, decode_buffer.size() - fill_len * bpf);
|
memset(&decode_buffer[fill_len * bpf], 0, decode_buffer.size() - fill_len * bpf);
|
||||||
|
@ -308,4 +308,4 @@ std::unique_ptr<AudioPlayer> CreateOpenALPlayer(agi::AudioProvider *provider, wx
|
||||||
return agi::make_unique<OpenALPlayer>(provider);
|
return agi::make_unique<OpenALPlayer>(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_OPENAL
|
#endif // WITH_OPENAL
|
|
@ -131,7 +131,7 @@ public:
|
||||||
|
|
||||||
while (!TestDestroy() && parent->cur_frame < parent->end_frame) {
|
while (!TestDestroy() && parent->cur_frame < parent->end_frame) {
|
||||||
int rsize = std::min(wsize, parent->end_frame - parent->cur_frame);
|
int rsize = std::min(wsize, parent->end_frame - parent->cur_frame);
|
||||||
parent->provider->GetAudioWithVolume(buf, parent->cur_frame,
|
parent->provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(buf), parent->cur_frame,
|
||||||
rsize, parent->volume);
|
rsize, parent->volume);
|
||||||
int written = ::write(parent->dspdev, buf, rsize * parent->bpf);
|
int written = ::write(parent->dspdev, buf, rsize * parent->bpf);
|
||||||
parent->cur_frame += written / parent->bpf;
|
parent->cur_frame += written / parent->bpf;
|
||||||
|
@ -146,7 +146,7 @@ public:
|
||||||
|
|
||||||
void OSSPlayer::OpenStream()
|
void OSSPlayer::OpenStream()
|
||||||
{
|
{
|
||||||
bpf = provider->GetChannels() * provider->GetBytesPerSample();
|
bpf = /*provider->GetChannels() * provider->GetBytesPerSample()*/sizeof(int16_t);
|
||||||
|
|
||||||
// Open device
|
// Open device
|
||||||
wxString device = to_wx(OPT_GET("Player/Audio/OSS/Device")->GetString());
|
wxString device = to_wx(OPT_GET("Player/Audio/OSS/Device")->GetString());
|
||||||
|
@ -162,14 +162,14 @@ void OSSPlayer::OpenStream()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set number of channels
|
// Set number of channels
|
||||||
int channels = provider->GetChannels();
|
int channels = /*provider->GetChannels()*/1;
|
||||||
if (ioctl(dspdev, SNDCTL_DSP_CHANNELS, &channels) < 0) {
|
if (ioctl(dspdev, SNDCTL_DSP_CHANNELS, &channels) < 0) {
|
||||||
throw AudioPlayerOpenError("OSS player: setting channels failed");
|
throw AudioPlayerOpenError("OSS player: setting channels failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sample format
|
// Set sample format
|
||||||
int sample_format;
|
int sample_format;
|
||||||
switch (provider->GetBytesPerSample()) {
|
switch (/*provider->GetBytesPerSample()*/sizeof(int16_t)) {
|
||||||
case 1:
|
case 1:
|
||||||
sample_format = AFMT_S8;
|
sample_format = AFMT_S8;
|
||||||
break;
|
break;
|
||||||
|
@ -283,4 +283,4 @@ std::unique_ptr<AudioPlayer> CreateOSSPlayer(agi::AudioProvider *provider, wxWin
|
||||||
return agi::make_unique<OSSPlayer>(provider);
|
return agi::make_unique<OSSPlayer>(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_OSS
|
#endif // WITH_OSS
|
|
@ -140,7 +140,7 @@ void PortAudioPlayer::OpenStream() {
|
||||||
const PaDeviceInfo *device_info = Pa_GetDeviceInfo((*device_ids)[i]);
|
const PaDeviceInfo *device_info = Pa_GetDeviceInfo((*device_ids)[i]);
|
||||||
PaStreamParameters pa_output_p;
|
PaStreamParameters pa_output_p;
|
||||||
pa_output_p.device = (*device_ids)[i];
|
pa_output_p.device = (*device_ids)[i];
|
||||||
pa_output_p.channelCount = provider->GetChannels();
|
pa_output_p.channelCount = /*provider->GetChannels()*/ 1;
|
||||||
pa_output_p.sampleFormat = paInt16;
|
pa_output_p.sampleFormat = paInt16;
|
||||||
pa_output_p.suggestedLatency = device_info->defaultLowOutputLatency;
|
pa_output_p.suggestedLatency = device_info->defaultLowOutputLatency;
|
||||||
pa_output_p.hostApiSpecificStreamInfo = nullptr;
|
pa_output_p.hostApiSpecificStreamInfo = nullptr;
|
||||||
|
@ -222,7 +222,7 @@ int PortAudioPlayer::paCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
|
|
||||||
// Play something
|
// Play something
|
||||||
if (lenAvailable > 0) {
|
if (lenAvailable > 0) {
|
||||||
player->provider->GetAudioWithVolume(outputBuffer, player->current, lenAvailable, player->GetVolume());
|
player->provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(outputBuffer), player->current, lenAvailable, player->GetVolume());
|
||||||
|
|
||||||
// Set play position
|
// Set play position
|
||||||
player->current += lenAvailable;
|
player->current += lenAvailable;
|
||||||
|
@ -283,4 +283,4 @@ std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(agi::AudioProvider *provider,
|
||||||
return agi::make_unique<PortAudioPlayer>(provider);
|
return agi::make_unique<PortAudioPlayer>(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WITH_PORTAUDIO
|
#endif // WITH_PORTAUDIO
|
|
@ -133,11 +133,11 @@ PulseAudioPlayer::PulseAudioPlayer(agi::AudioProvider *provider) : AudioPlayer(p
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up stream
|
// Set up stream
|
||||||
bpf = provider->GetChannels() * provider->GetBytesPerSample();
|
bpf = /*provider->GetChannels() * provider->GetBytesPerSample()*/sizeof(int16_t);
|
||||||
pa_sample_spec ss;
|
pa_sample_spec ss;
|
||||||
ss.format = PA_SAMPLE_S16LE; // FIXME
|
ss.format = PA_SAMPLE_S16LE; // FIXME
|
||||||
ss.rate = provider->GetSampleRate();
|
ss.rate = provider->GetSampleRate();
|
||||||
ss.channels = provider->GetChannels();
|
ss.channels = /*provider->GetChannels()*/1;
|
||||||
pa_channel_map map;
|
pa_channel_map map;
|
||||||
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
|
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPl
|
||||||
unsigned long maxframes = thread->end_frame - thread->cur_frame;
|
unsigned long maxframes = thread->end_frame - thread->cur_frame;
|
||||||
if (frames > maxframes) frames = maxframes;
|
if (frames > maxframes) frames = maxframes;
|
||||||
void *buf = malloc(frames * bpf);
|
void *buf = malloc(frames * bpf);
|
||||||
thread->provider->GetAudioWithVolume(buf, thread->cur_frame, frames, thread->volume);
|
thread->provider->GetInt16MonoAudioWithVolume(reinterpret_cast<int16_t*>(buf), thread->cur_frame, frames, thread->volume);
|
||||||
::pa_stream_write(p, buf, frames*bpf, free, 0, PA_SEEK_RELATIVE);
|
::pa_stream_write(p, buf, frames*bpf, free, 0, PA_SEEK_RELATIVE);
|
||||||
thread->cur_frame += frames;
|
thread->cur_frame += frames;
|
||||||
}
|
}
|
||||||
|
@ -324,4 +324,4 @@ void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread)
|
||||||
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(agi::AudioProvider *provider, wxWindow *) {
|
std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(agi::AudioProvider *provider, wxWindow *) {
|
||||||
return agi::make_unique<PulseAudioPlayer>(provider);
|
return agi::make_unique<PulseAudioPlayer>(provider);
|
||||||
}
|
}
|
||||||
#endif // WITH_LIBPULSE
|
#endif // WITH_LIBPULSE
|
|
@ -236,7 +236,7 @@ opt_src = [
|
||||||
['OSS', 'audio_player_oss.cpp'],
|
['OSS', 'audio_player_oss.cpp'],
|
||||||
['DirectSound', ['audio_player_dsound.cpp',
|
['DirectSound', ['audio_player_dsound.cpp',
|
||||||
'audio_player_dsound2.cpp']],
|
'audio_player_dsound2.cpp']],
|
||||||
|
['XAudio2', 'audio_player_xaudio2.cpp'],
|
||||||
['FFMS2', ['audio_provider_ffmpegsource.cpp',
|
['FFMS2', ['audio_provider_ffmpegsource.cpp',
|
||||||
'video_provider_ffmpegsource.cpp',
|
'video_provider_ffmpegsource.cpp',
|
||||||
'ffmpegsource_common.cpp']],
|
'ffmpegsource_common.cpp']],
|
||||||
|
|
|
@ -39,8 +39,13 @@ eyedropper_cursor CURSOR "../bitmaps/windows/eyedropper.cur"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
|
#ifdef TAGGED_RELEASE
|
||||||
FILEVERSION RESOURCE_BASE_VERSION, BUILD_GIT_VERSION_NUMBER
|
FILEVERSION RESOURCE_BASE_VERSION, BUILD_GIT_VERSION_NUMBER
|
||||||
PRODUCTVERSION RESOURCE_BASE_VERSION, 0
|
PRODUCTVERSION RESOURCE_BASE_VERSION, 0
|
||||||
|
#else
|
||||||
|
FILEVERSION BUILD_GIT_VERSION_NUMBER, BUILD_GIT_VERSION_NUMBER
|
||||||
|
PRODUCTVERSION BUILD_GIT_VERSION_NUMBER, 0
|
||||||
|
#endif
|
||||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||||
FILEFLAGS (AGI_RC_FLAG_DEBUG|AGI_RC_FLAG_PRERELEASE)
|
FILEFLAGS (AGI_RC_FLAG_DEBUG|AGI_RC_FLAG_PRERELEASE)
|
||||||
FILEOS VOS__WINDOWS32
|
FILEOS VOS__WINDOWS32
|
||||||
|
|
|
@ -172,21 +172,21 @@ TEST(lagi_audio, save_audio_clip_out_of_audio_range) {
|
||||||
|
|
||||||
TEST(lagi_audio, get_with_volume) {
|
TEST(lagi_audio, get_with_volume) {
|
||||||
TestAudioProvider<> provider;
|
TestAudioProvider<> provider;
|
||||||
uint16_t buff[4];
|
int16_t buff[4];
|
||||||
|
|
||||||
provider.GetAudioWithVolume(buff, 0, 4, 1.0);
|
provider.GetInt16MonoAudioWithVolume(buff, 0, 4, 1.0);
|
||||||
EXPECT_EQ(0, buff[0]);
|
EXPECT_EQ(0, buff[0]);
|
||||||
EXPECT_EQ(1, buff[1]);
|
EXPECT_EQ(1, buff[1]);
|
||||||
EXPECT_EQ(2, buff[2]);
|
EXPECT_EQ(2, buff[2]);
|
||||||
EXPECT_EQ(3, buff[3]);
|
EXPECT_EQ(3, buff[3]);
|
||||||
|
|
||||||
provider.GetAudioWithVolume(buff, 0, 4, 0.0);
|
provider.GetInt16MonoAudioWithVolume(buff, 0, 4, 0.0);
|
||||||
EXPECT_EQ(0, buff[0]);
|
EXPECT_EQ(0, buff[0]);
|
||||||
EXPECT_EQ(0, buff[1]);
|
EXPECT_EQ(0, buff[1]);
|
||||||
EXPECT_EQ(0, buff[2]);
|
EXPECT_EQ(0, buff[2]);
|
||||||
EXPECT_EQ(0, buff[3]);
|
EXPECT_EQ(0, buff[3]);
|
||||||
|
|
||||||
provider.GetAudioWithVolume(buff, 0, 4, 2.0);
|
provider.GetInt16MonoAudioWithVolume(buff, 0, 4, 2.0);
|
||||||
EXPECT_EQ(0, buff[0]);
|
EXPECT_EQ(0, buff[0]);
|
||||||
EXPECT_EQ(2, buff[1]);
|
EXPECT_EQ(2, buff[1]);
|
||||||
EXPECT_EQ(4, buff[2]);
|
EXPECT_EQ(4, buff[2]);
|
||||||
|
@ -195,8 +195,8 @@ TEST(lagi_audio, get_with_volume) {
|
||||||
|
|
||||||
TEST(lagi_audio, volume_should_clamp_rather_than_wrap) {
|
TEST(lagi_audio, volume_should_clamp_rather_than_wrap) {
|
||||||
TestAudioProvider<> provider;
|
TestAudioProvider<> provider;
|
||||||
uint16_t buff[1];
|
int16_t buff[1];
|
||||||
provider.GetAudioWithVolume(buff, 30000, 1, 2.0);
|
provider.GetInt16MonoAudioWithVolume(buff, 30000, 1, 2.0);
|
||||||
EXPECT_EQ(SHRT_MAX, buff[0]);
|
EXPECT_EQ(SHRT_MAX, buff[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ TEST(lagi_audio, convert_8bit) {
|
||||||
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<TestAudioProvider<uint8_t>>());
|
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<TestAudioProvider<uint8_t>>());
|
||||||
|
|
||||||
int16_t data[256];
|
int16_t data[256];
|
||||||
provider->GetAudio(data, 0, 256);
|
provider->GetInt16MonoAudio(data, 0, 256);
|
||||||
for (int i = 0; i < 256; ++i)
|
for (int i = 0; i < 256; ++i)
|
||||||
ASSERT_EQ((i - 128) * 256, data[i]);
|
ASSERT_EQ((i - 128) * 256, data[i]);
|
||||||
}
|
}
|
||||||
|
@ -243,13 +243,13 @@ TEST(lagi_audio, convert_32bit) {
|
||||||
auto provider = agi::CreateConvertAudioProvider(std::move(src));
|
auto provider = agi::CreateConvertAudioProvider(std::move(src));
|
||||||
|
|
||||||
int16_t sample;
|
int16_t sample;
|
||||||
provider->GetAudio(&sample, 0, 1);
|
provider->GetInt16MonoAudio(&sample, 0, 1);
|
||||||
EXPECT_EQ(SHRT_MIN, sample);
|
EXPECT_EQ(SHRT_MIN, sample);
|
||||||
|
|
||||||
provider->GetAudio(&sample, 1LL << 31, 1);
|
provider->GetInt16MonoAudio(&sample, 1LL << 31, 1);
|
||||||
EXPECT_EQ(0, sample);
|
EXPECT_EQ(0, sample);
|
||||||
|
|
||||||
provider->GetAudio(&sample, (1LL << 32) - 1, 1);
|
provider->GetInt16MonoAudio(&sample, (1LL << 32) - 1, 1);
|
||||||
EXPECT_EQ(SHRT_MAX, sample);
|
EXPECT_EQ(SHRT_MAX, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,10 +310,10 @@ TEST(lagi_audio, stereo_downmix) {
|
||||||
};
|
};
|
||||||
|
|
||||||
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<AudioProvider>());
|
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<AudioProvider>());
|
||||||
EXPECT_EQ(1, provider->GetChannels());
|
EXPECT_EQ(2, provider->GetChannels());
|
||||||
|
|
||||||
int16_t samples[100];
|
int16_t samples[100];
|
||||||
provider->GetAudio(samples, 0, 100);
|
provider->GetInt16MonoAudio(samples, 0, 100);
|
||||||
for (int i = 0; i < 100; ++i)
|
for (int i = 0; i < 100; ++i)
|
||||||
EXPECT_EQ(i, samples[i]);
|
EXPECT_EQ(i, samples[i]);
|
||||||
}
|
}
|
||||||
|
@ -333,27 +333,27 @@ struct FloatAudioProvider : agi::AudioProvider {
|
||||||
auto out = static_cast<Float *>(buf);
|
auto out = static_cast<Float *>(buf);
|
||||||
for (int64_t end = start + count; start < end; ++start) {
|
for (int64_t end = start + count; start < end; ++start) {
|
||||||
auto shifted = start + SHRT_MIN;
|
auto shifted = start + SHRT_MIN;
|
||||||
*out++ = (Float)(1.0 * shifted / (shifted < 0 ? -SHRT_MIN : SHRT_MAX));
|
*out++ = (Float)(shifted) / (-SHRT_MIN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(lagi_audio, float_conversion) {
|
TEST(lagi_audio, float_conversion) {
|
||||||
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<FloatAudioProvider<float>>());
|
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<FloatAudioProvider<float>>());
|
||||||
EXPECT_FALSE(provider->AreSamplesFloat());
|
EXPECT_TRUE(provider->AreSamplesFloat());
|
||||||
|
|
||||||
int16_t samples[1 << 16];
|
int16_t samples[1 << 16];
|
||||||
provider->GetAudio(samples, 0, 1 << 16);
|
provider->GetInt16MonoAudio(samples, 0, 1 << 16);
|
||||||
for (int i = 0; i < (1 << 16); ++i)
|
for (int i = 0; i < (1 << 16); ++i)
|
||||||
ASSERT_EQ(i + SHRT_MIN, samples[i]);
|
ASSERT_EQ(i + SHRT_MIN, samples[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(lagi_audio, double_conversion) {
|
TEST(lagi_audio, double_conversion) {
|
||||||
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<FloatAudioProvider<double>>());
|
auto provider = agi::CreateConvertAudioProvider(agi::make_unique<FloatAudioProvider<double>>());
|
||||||
EXPECT_FALSE(provider->AreSamplesFloat());
|
EXPECT_TRUE(provider->AreSamplesFloat());
|
||||||
|
|
||||||
int16_t samples[1 << 16];
|
int16_t samples[1 << 16];
|
||||||
provider->GetAudio(samples, 0, 1 << 16);
|
provider->GetInt16MonoAudio(samples, 0, 1 << 16);
|
||||||
for (int i = 0; i < (1 << 16); ++i)
|
for (int i = 0; i < (1 << 16); ++i)
|
||||||
ASSERT_EQ(i + SHRT_MIN, samples[i]);
|
ASSERT_EQ(i + SHRT_MIN, samples[i]);
|
||||||
}
|
}
|
||||||
|
@ -551,4 +551,4 @@ TEST(lagi_audio, wave64_truncated) {
|
||||||
}
|
}
|
||||||
|
|
||||||
agi::fs::Remove(path);
|
agi::fs::Remove(path);
|
||||||
}
|
}
|
Loading…
Reference in New Issue