From fd28458ed81a08fe92d0aa9405b4e6c6a994bbed Mon Sep 17 00:00:00 2001 From: Ristellise Date: Wed, 10 Aug 2022 21:09:41 +0800 Subject: [PATCH] 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. --- CMakeLists.txt | 1 + libaegisub/audio/provider.cpp | 138 +++++++++++++++++- libaegisub/audio/provider_convert.cpp | 127 ++-------------- libaegisub/audio/provider_hd.cpp | 8 +- libaegisub/audio/provider_ram.cpp | 12 +- .../include/libaegisub/audio/provider.h | 7 +- meson.build | 14 +- src/audio_player_alsa.cpp | 13 +- src/audio_player_dsound.cpp | 17 ++- src/audio_player_dsound2.cpp | 21 +-- src/audio_player_openal.cpp | 6 +- src/audio_player_oss.cpp | 10 +- src/audio_player_portaudio.cpp | 6 +- src/audio_player_pulse.cpp | 8 +- src/meson.build | 2 +- src/res/res.rc | 5 + tests/tests/audio.cpp | 36 ++--- 17 files changed, 241 insertions(+), 190 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e89b529..75182bbe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,6 +497,7 @@ set_property(SOURCE src/subtitles_provider_csri.cpp PROPERTY INCLUDE_DIRECTORIES if(MSVC) target_link_libraries (Aegisub dsound) 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) endif(MSVC) diff --git a/libaegisub/audio/provider.cpp b/libaegisub/audio/provider.cpp index 961e5bf89..b7ad7847c 100644 --- a/libaegisub/audio/provider.cpp +++ b/libaegisub/audio/provider.cpp @@ -21,13 +21,108 @@ #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); +namespace { +template +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(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(reinterpret_cast(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 +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(reinterpret_cast(buff))[i]; + else if (bytes_per_sample == sizeof(double)) + for (int64_t i = 0; i < count; ++i) + buf[i] = ConvertFloatToInt16(reinterpret_cast(buff))[i]; + } + else { + if (bytes_per_sample == sizeof(uint8_t)) + for (int64_t i = 0; i < count; ++i) + buf[i] = ConvertUInt8ToInt16(reinterpret_cast(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(reinterpret_cast(buff)), channels)[i]; + else if (bytes_per_sample == sizeof(double)) + for (int64_t i = 0; i < count; ++i) + buf[i] = DownmixToMono >(ConvertFloatToInt16(reinterpret_cast(buff)), channels)[i]; + } + else { + if (bytes_per_sample == sizeof(uint8_t)) + for (int64_t i = 0; i < count; ++i) + buf[i] = DownmixToMono(ConvertUInt8ToInt16(reinterpret_cast(buff)), channels)[i]; + else + for (int64_t i = 0; i < count; ++i) + buf[i] = DownmixToMono(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 (bytes_per_sample != 2) - throw agi::InternalError("GetAudioWithVolume called on unconverted audio stream"); auto buffer = static_cast(buf); 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 { class writer { io::Save outfile; diff --git a/libaegisub/audio/provider_convert.cpp b/libaegisub/audio/provider_convert.cpp index b45d8a852..21603c28a 100644 --- a/libaegisub/audio/provider_convert.cpp +++ b/libaegisub/audio/provider_convert.cpp @@ -22,119 +22,19 @@ #include using namespace agi; - -/// Anything integral -> 16 bit signed machine-endian audio converter namespace { -template -class BitdepthConvertAudioProvider final : public AudioProviderWrapper { - int src_bytes_per_sample; - mutable std::vector src_buf; - +class ConvertAudioProvider final : public AudioProviderWrapper { public: - BitdepthConvertAudioProvider(std::unique_ptr 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(count64); - assert(count == count64); - - src_buf.resize(count * src_bytes_per_sample * channels); - source->GetAudio(src_buf.data(), start, count); - - auto dest = static_cast(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(src_bytes_per_sample) > sizeof(Target)) - sample /= 1LL << (src_bytes_per_sample - sizeof(Target)) * 8; - else if (static_cast(src_bytes_per_sample) < sizeof(Target)) - sample *= 1LL << (sizeof(Target) - src_bytes_per_sample ) * 8; - - dest[i] = static_cast(sample); - } - } -}; - -/// Floating point -> 16 bit signed machine-endian audio converter -template -class FloatConvertAudioProvider final : public AudioProviderWrapper { - mutable std::vector src_buf; - -public: - FloatConvertAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) { - bytes_per_sample = sizeof(Target); + ConvertAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) { float_samples = false; - } - - void FillBuffer(void *buf, int64_t start, int64_t count64) const override { - auto count = static_cast(count64); - assert(count == count64); - - src_buf.resize(count * channels); - source->GetAudio(&src_buf[0], start, count); - - auto dest = static_cast(buf); - - for (size_t i = 0; i < static_cast(count * channels); ++i) { - Source expanded; - if (src_buf[i] < 0) - expanded = static_cast(-src_buf[i] * std::numeric_limits::min()); - else - expanded = static_cast(src_buf[i] * std::numeric_limits::max()); - - dest[i] = expanded < std::numeric_limits::min() ? std::numeric_limits::min() : - expanded > std::numeric_limits::max() ? std::numeric_limits::max() : - static_cast(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 src_buf; - -public: - DownmixAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) { - src_channels = channels; channels = 1; + bytes_per_sample = sizeof(int16_t); } - void FillBuffer(void *buf, int64_t start, int64_t count64) const override { - auto count = static_cast(count64); - assert(count == count64); - - src_buf.resize(count * src_channels); - source->GetAudio(&src_buf[0], start, count); - - auto dst = static_cast(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(sum / src_channels); - } + void FillBuffer(void *buf, int64_t start, int64_t count) const override { + source->GetInt16MonoAudio(reinterpret_cast(buf), start, count); } }; - /// Sample doubler with linear interpolation for the samples provider /// Requires 16-bit mono input class SampleDoublingAudioProvider final : public AudioProviderWrapper { @@ -177,26 +77,23 @@ std::unique_ptr CreateConvertAudioProvider(std::unique_ptrAreSamplesFloat()) { LOG_D("audio_provider") << "Converting float to S16"; - if (provider->GetBytesPerSample() == sizeof(float)) - provider = agi::make_unique>(std::move(provider)); - else - provider = agi::make_unique>(std::move(provider)); } if (provider->GetBytesPerSample() != 2) { - LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample or wrong endian to S16"; - provider = agi::make_unique>(std::move(provider)); + LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample to S16"; } // We currently only support mono audio if (provider->GetChannels() != 1) { LOG_D("audio_provider") << "Downmixing to mono from " << provider->GetChannels() << " channels"; - provider = agi::make_unique(std::move(provider)); } // Some players don't like low sample rate audio - while (provider->GetSampleRate() < 32000) { - LOG_D("audio_provider") << "Doubling sample rate"; - provider = agi::make_unique(std::move(provider)); + if (provider->GetSampleRate() < 32000) { + provider = agi::make_unique(std::move(provider)); + while (provider->GetSampleRate() < 32000) { + LOG_D("audio_provider") << "Doubling sample rate"; + provider = agi::make_unique(std::move(provider)); + } } return provider; diff --git a/libaegisub/audio/provider_hd.cpp b/libaegisub/audio/provider_hd.cpp index 19e33eeed..2969b244a 100644 --- a/libaegisub/audio/provider_hd.cpp +++ b/libaegisub/audio/provider_hd.cpp @@ -43,15 +43,15 @@ class HDAudioProvider final : public AudioProviderWrapper { } if (count > 0) { - start *= bytes_per_sample; - count *= bytes_per_sample; + start *= bytes_per_sample * channels; + count *= bytes_per_sample * channels; 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)) + 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"); return format("audio-%lld-%lld", time(nullptr), @@ -61,7 +61,7 @@ class HDAudioProvider final : public AudioProviderWrapper { public: HDAudioProvider(std::unique_ptr src, agi::fs::path const& dir) : 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; decoder = std::thread([&] { diff --git a/libaegisub/audio/provider_ram.cpp b/libaegisub/audio/provider_ram.cpp index 0c1da546c..3f070c87e 100644 --- a/libaegisub/audio/provider_ram.cpp +++ b/libaegisub/audio/provider_ram.cpp @@ -71,20 +71,22 @@ public: void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const { auto charbuf = static_cast(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) { memset(charbuf, 0, bytes_remaining); break; } - const int i = (start * bytes_per_sample) >> CacheBits; - const int start_offset = (start * bytes_per_sample) & (CacheBlockSize-1); - const int read_size = std::min(bytes_remaining, CacheBlockSize - start_offset); + const int64_t samples_per_block = CacheBlockSize / bytes_per_sample / channels; + + 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(bytes_remaining, samples_per_block * bytes_per_sample * channels - start_offset); memcpy(charbuf, &blockcache[i][start_offset], read_size); charbuf += read_size; bytes_remaining -= read_size; - start += read_size / bytes_per_sample; + start += read_size / bytes_per_sample / channels; } } } diff --git a/libaegisub/include/libaegisub/audio/provider.h b/libaegisub/include/libaegisub/audio/provider.h index 70460a723..fc2e0ddb3 100644 --- a/libaegisub/include/libaegisub/audio/provider.h +++ b/libaegisub/include/libaegisub/audio/provider.h @@ -20,7 +20,6 @@ #include #include -#include #include namespace agi { @@ -37,6 +36,7 @@ protected: bool float_samples = false; 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; @@ -44,7 +44,8 @@ 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; + 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 GetDecodedSamples() const { return decoded_samples; } @@ -93,4 +94,4 @@ std::unique_ptr CreateHDAudioProvider(std::unique_ptr CreateRAMAudioProvider(std::unique_ptr source_provider); void SaveAudioClip(AudioProvider const& provider, fs::path const& path, int start_time, int end_time); -} +} \ No newline at end of file diff --git a/meson.build b/meson.build index 5527cfbe8..64cc9d3ca 100644 --- a/meson.build +++ b/meson.build @@ -7,7 +7,7 @@ project('Aegisub', ['c', 'cpp'], cmake = import('cmake') 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() add_global_arguments('-DCSRI_NO_EXPORT', language: 'c') @@ -259,16 +259,26 @@ if host_machine.system() == 'windows' if not get_option('xaudio2').disabled() have_xaudio_h = cc.has_header('xaudio2.h') xaudio2_dep = cc.find_library('xaudio2', required: true) - if have_xaudio_h + if have_xaudio_h and xaudio2_dep.found() deps += [xaudio2_dep] conf.set('WITH_XAUDIO2', 1) 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 if not have_dsound_h and get_option('xaudio2').enabled() error('xaudio2 enabled but xaudio2.h not found') endif + else + # Windows 8 not required if XAudio2 is disabled. revert for compat. + add_project_arguments('-D_WIN32_WINNT=0x0601', language: 'cpp') endif + + endif if host_machine.system() == 'darwin' diff --git a/src/audio_player_alsa.cpp b/src/audio_player_alsa.cpp index 5a1705622..5a98d9ed7 100644 --- a/src/audio_player_alsa.cpp +++ b/src/audio_player_alsa.cpp @@ -127,7 +127,7 @@ void AlsaPlayer::PlaybackThread() do_setup: snd_pcm_format_t pcm_format; - switch (provider->GetBytesPerSample()) + switch (/*provider->GetBytesPerSample()*/ sizeof(int16_t)) { case 1: LOG_D("audio/player/alsa") << "format U8"; @@ -143,7 +143,7 @@ do_setup: if (snd_pcm_set_params(pcm, pcm_format, SND_PCM_ACCESS_RW_INTERLEAVED, - provider->GetChannels(), + /*provider->GetChannels()*/ 1, provider->GetSampleRate(), 1, // allow resample 100*1000 // 100 milliseconds latency @@ -151,7 +151,8 @@ do_setup: return; 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) { @@ -175,7 +176,7 @@ do_setup: { auto avail = std::min(snd_pcm_avail(pcm), (snd_pcm_sframes_t)(end_position-position)); decode_buffer.resize(avail * framesize); - provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume); + provider->GetInt16MonoAudioWithVolume(reinterpret_cast(decode_buffer.data()), position, avail, volume); snd_pcm_sframes_t written = 0; while (written <= 0) @@ -235,7 +236,7 @@ do_setup: { decode_buffer.resize(avail * framesize); - provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume); + provider->GetInt16MonoAudioWithVolume(reinterpret_cast(decode_buffer.data()), position, avail, volume); snd_pcm_sframes_t written = 0; while (written <= 0) { @@ -352,4 +353,4 @@ std::unique_ptr CreateAlsaPlayer(agi::AudioProvider *provider, wxWi return agi::make_unique(provider); } -#endif // WITH_ALSA +#endif // WITH_ALSA \ No newline at end of file diff --git a/src/audio_player_dsound.cpp b/src/audio_player_dsound.cpp index 01b47b354..8b50492a0 100644 --- a/src/audio_player_dsound.cpp +++ b/src/audio_player_dsound.cpp @@ -45,6 +45,7 @@ #include #include +#include namespace { class DirectSoundPlayer; @@ -111,8 +112,10 @@ DirectSoundPlayer::DirectSoundPlayer(agi::AudioProvider *provider, wxWindow *par WAVEFORMATEX waveFormat; waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nSamplesPerSec = provider->GetSampleRate(); - waveFormat.nChannels = provider->GetChannels(); - waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8; + //waveFormat.nChannels = provider->GetChannels(); + //waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8; + waveFormat.nChannels = 1; + waveFormat.wBitsPerSample = sizeof(int16_t) * 8; waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; waveFormat.cbSize = sizeof(waveFormat); @@ -160,7 +163,7 @@ bool DirectSoundPlayer::FillBuffer(bool fill) { HRESULT res; void *ptr1, *ptr2; unsigned long int size1, size2; - int bytesps = provider->GetBytesPerSample(); + int bytesps = /*provider->GetBytesPerSample()*/ sizeof(int16_t); // To write length int toWrite = 0; @@ -223,8 +226,8 @@ RetryLock: LOG_D_IF(!count1 && !count2, "audio/player/dsound1") << "DS fill: nothing"; // Get source wave - if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume); - if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume); + if (count1) provider->GetInt16MonoAudioWithVolume(reinterpret_cast(ptr1), playPos, count1, volume); + if (count2) provider->GetInt16MonoAudioWithVolume(reinterpret_cast(ptr2), playPos+count1, count2, volume); playPos += count1+count2; buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps); @@ -254,7 +257,7 @@ void DirectSoundPlayer::Play(int64_t start,int64_t count) { FillBuffer(true); DWORD play_flag = 0; - if (count*provider->GetBytesPerSample() > bufSize) { + if (count*/*provider->GetBytesPerSample()*/sizeof(int16_t) > bufSize) { // Start thread thread = new DirectSoundPlayerThread(this); thread->Create(); @@ -371,4 +374,4 @@ std::unique_ptr CreateDirectSoundPlayer(agi::AudioProvider *provide return agi::make_unique(provider, parent); } -#endif // WITH_DIRECTSOUND +#endif // WITH_DIRECTSOUND \ No newline at end of file diff --git a/src/audio_player_dsound2.cpp b/src/audio_player_dsound2.cpp index dd7bf8680..e0d585818 100644 --- a/src/audio_player_dsound2.cpp +++ b/src/audio_player_dsound2.cpp @@ -48,6 +48,7 @@ #include #include #include +#include namespace { class DirectSoundPlayer2Thread; @@ -319,8 +320,10 @@ void DirectSoundPlayer2Thread::Run() WAVEFORMATEX waveFormat; waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nSamplesPerSec = provider->GetSampleRate(); - waveFormat.nChannels = provider->GetChannels(); - waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8; + //waveFormat.nChannels = provider->GetChannels(); + //waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8; + waveFormat.nChannels = 1; + waveFormat.wBitsPerSample = sizeof(int16_t) * 8; waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; waveFormat.cbSize = sizeof(waveFormat); @@ -369,7 +372,7 @@ void DirectSoundPlayer2Thread::Run() DWORD buffer_offset = 0; bool playback_should_be_running = false; 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) { @@ -422,7 +425,7 @@ void DirectSoundPlayer2Thread::Run() if (bytes_filled < wanted_latency_bytes) { // 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))) REPORT_ERROR("Could not start single-buffer playback.") } @@ -553,7 +556,7 @@ do_fill_buffer: 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 - current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample()); + current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*/*provider->GetBytesPerSample()*/sizeof(int16_t)); } else { @@ -577,7 +580,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v { // 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 buf2szf = buf2sz / bytes_per_frame; @@ -608,7 +611,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v buf2sz = 0; } - provider->GetAudioWithVolume(buf1, input_frame, buf1szf, volume); + provider->GetInt16MonoAudioWithVolume(reinterpret_cast(buf1), input_frame, buf1szf, volume); input_frame += buf1szf; } @@ -621,7 +624,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v buf2sz = buf2szf * bytes_per_frame; } - provider->GetAudioWithVolume(buf2, input_frame, buf2szf, volume); + provider->GetInt16MonoAudioWithVolume(reinterpret_cast(buf2), input_frame, buf2szf, volume); input_frame += buf2szf; } @@ -932,4 +935,4 @@ std::unique_ptr CreateDirectSound2Player(agi::AudioProvider *provid return agi::make_unique(provider, parent); } -#endif // WITH_DIRECTSOUND +#endif // WITH_DIRECTSOUND \ No newline at end of file diff --git a/src/audio_player_openal.cpp b/src/audio_player_openal.cpp index b0f8372bd..d95d804ce 100644 --- a/src/audio_player_openal.cpp +++ b/src/audio_player_openal.cpp @@ -125,7 +125,7 @@ public: OpenALPlayer::OpenALPlayer(agi::AudioProvider *provider) : AudioPlayer(provider) , samplerate(provider->GetSampleRate()) -, bpf(provider->GetChannels() * provider->GetBytesPerSample()) +, bpf(/*provider->GetChannels() * provider->GetBytesPerSample()*/sizeof(int16_t)) { device = alcOpenDevice(nullptr); if (!device) throw AudioPlayerOpenError("Failed opening default OpenAL device"); @@ -241,7 +241,7 @@ void OpenALPlayer::FillBuffers(ALsizei count) if (fill_len > 0) // Get fill_len frames of audio - provider->GetAudioWithVolume(&decode_buffer[0], cur_frame, fill_len, volume); + provider->GetInt16MonoAudioWithVolume(reinterpret_cast(decode_buffer.data()), cur_frame, fill_len, volume); if ((size_t)fill_len * bpf < decode_buffer.size()) // And zerofill the rest memset(&decode_buffer[fill_len * bpf], 0, decode_buffer.size() - fill_len * bpf); @@ -308,4 +308,4 @@ std::unique_ptr CreateOpenALPlayer(agi::AudioProvider *provider, wx return agi::make_unique(provider); } -#endif // WITH_OPENAL +#endif // WITH_OPENAL \ No newline at end of file diff --git a/src/audio_player_oss.cpp b/src/audio_player_oss.cpp index 93950baef..0934ce197 100644 --- a/src/audio_player_oss.cpp +++ b/src/audio_player_oss.cpp @@ -131,7 +131,7 @@ public: while (!TestDestroy() && parent->cur_frame < parent->end_frame) { int rsize = std::min(wsize, parent->end_frame - parent->cur_frame); - parent->provider->GetAudioWithVolume(buf, parent->cur_frame, + parent->provider->GetInt16MonoAudioWithVolume(reinterpret_cast(buf), parent->cur_frame, rsize, parent->volume); int written = ::write(parent->dspdev, buf, rsize * parent->bpf); parent->cur_frame += written / parent->bpf; @@ -146,7 +146,7 @@ public: void OSSPlayer::OpenStream() { - bpf = provider->GetChannels() * provider->GetBytesPerSample(); + bpf = /*provider->GetChannels() * provider->GetBytesPerSample()*/sizeof(int16_t); // Open device wxString device = to_wx(OPT_GET("Player/Audio/OSS/Device")->GetString()); @@ -162,14 +162,14 @@ void OSSPlayer::OpenStream() #endif // Set number of channels - int channels = provider->GetChannels(); + int channels = /*provider->GetChannels()*/1; if (ioctl(dspdev, SNDCTL_DSP_CHANNELS, &channels) < 0) { throw AudioPlayerOpenError("OSS player: setting channels failed"); } // Set sample format int sample_format; - switch (provider->GetBytesPerSample()) { + switch (/*provider->GetBytesPerSample()*/sizeof(int16_t)) { case 1: sample_format = AFMT_S8; break; @@ -283,4 +283,4 @@ std::unique_ptr CreateOSSPlayer(agi::AudioProvider *provider, wxWin return agi::make_unique(provider); } -#endif // WITH_OSS +#endif // WITH_OSS \ No newline at end of file diff --git a/src/audio_player_portaudio.cpp b/src/audio_player_portaudio.cpp index 7a5babcdc..513b8b28b 100644 --- a/src/audio_player_portaudio.cpp +++ b/src/audio_player_portaudio.cpp @@ -140,7 +140,7 @@ void PortAudioPlayer::OpenStream() { const PaDeviceInfo *device_info = Pa_GetDeviceInfo((*device_ids)[i]); PaStreamParameters pa_output_p; 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.suggestedLatency = device_info->defaultLowOutputLatency; pa_output_p.hostApiSpecificStreamInfo = nullptr; @@ -222,7 +222,7 @@ int PortAudioPlayer::paCallback(const void *inputBuffer, void *outputBuffer, // Play something if (lenAvailable > 0) { - player->provider->GetAudioWithVolume(outputBuffer, player->current, lenAvailable, player->GetVolume()); + player->provider->GetInt16MonoAudioWithVolume(reinterpret_cast(outputBuffer), player->current, lenAvailable, player->GetVolume()); // Set play position player->current += lenAvailable; @@ -283,4 +283,4 @@ std::unique_ptr CreatePortAudioPlayer(agi::AudioProvider *provider, return agi::make_unique(provider); } -#endif // WITH_PORTAUDIO +#endif // WITH_PORTAUDIO \ No newline at end of file diff --git a/src/audio_player_pulse.cpp b/src/audio_player_pulse.cpp index 7174356bd..09882cb74 100644 --- a/src/audio_player_pulse.cpp +++ b/src/audio_player_pulse.cpp @@ -133,11 +133,11 @@ PulseAudioPlayer::PulseAudioPlayer(agi::AudioProvider *provider) : AudioPlayer(p } // Set up stream - bpf = provider->GetChannels() * provider->GetBytesPerSample(); + bpf = /*provider->GetChannels() * provider->GetBytesPerSample()*/sizeof(int16_t); pa_sample_spec ss; ss.format = PA_SAMPLE_S16LE; // FIXME ss.rate = provider->GetSampleRate(); - ss.channels = provider->GetChannels(); + ss.channels = /*provider->GetChannels()*/1; pa_channel_map map; 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; if (frames > maxframes) frames = maxframes; void *buf = malloc(frames * bpf); - thread->provider->GetAudioWithVolume(buf, thread->cur_frame, frames, thread->volume); + thread->provider->GetInt16MonoAudioWithVolume(reinterpret_cast(buf), thread->cur_frame, frames, thread->volume); ::pa_stream_write(p, buf, frames*bpf, free, 0, PA_SEEK_RELATIVE); thread->cur_frame += frames; } @@ -324,4 +324,4 @@ void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread) std::unique_ptr CreatePulseAudioPlayer(agi::AudioProvider *provider, wxWindow *) { return agi::make_unique(provider); } -#endif // WITH_LIBPULSE +#endif // WITH_LIBPULSE \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 5287fb198..4844a0ae8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -236,7 +236,7 @@ opt_src = [ ['OSS', 'audio_player_oss.cpp'], ['DirectSound', ['audio_player_dsound.cpp', 'audio_player_dsound2.cpp']], - + ['XAudio2', 'audio_player_xaudio2.cpp'], ['FFMS2', ['audio_provider_ffmpegsource.cpp', 'video_provider_ffmpegsource.cpp', 'ffmpegsource_common.cpp']], diff --git a/src/res/res.rc b/src/res/res.rc index 26f268165..0fdf8cb21 100644 --- a/src/res/res.rc +++ b/src/res/res.rc @@ -39,8 +39,13 @@ eyedropper_cursor CURSOR "../bitmaps/windows/eyedropper.cur" #endif VS_VERSION_INFO VERSIONINFO +#ifdef TAGGED_RELEASE FILEVERSION RESOURCE_BASE_VERSION, BUILD_GIT_VERSION_NUMBER 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 FILEFLAGS (AGI_RC_FLAG_DEBUG|AGI_RC_FLAG_PRERELEASE) FILEOS VOS__WINDOWS32 diff --git a/tests/tests/audio.cpp b/tests/tests/audio.cpp index 23255c042..300834e5a 100644 --- a/tests/tests/audio.cpp +++ b/tests/tests/audio.cpp @@ -172,21 +172,21 @@ TEST(lagi_audio, save_audio_clip_out_of_audio_range) { TEST(lagi_audio, get_with_volume) { 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(1, buff[1]); EXPECT_EQ(2, buff[2]); 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[1]); EXPECT_EQ(0, buff[2]); 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(2, buff[1]); EXPECT_EQ(4, buff[2]); @@ -195,8 +195,8 @@ TEST(lagi_audio, get_with_volume) { TEST(lagi_audio, volume_should_clamp_rather_than_wrap) { TestAudioProvider<> provider; - uint16_t buff[1]; - provider.GetAudioWithVolume(buff, 30000, 1, 2.0); + int16_t buff[1]; + provider.GetInt16MonoAudioWithVolume(buff, 30000, 1, 2.0); EXPECT_EQ(SHRT_MAX, buff[0]); } @@ -232,7 +232,7 @@ TEST(lagi_audio, convert_8bit) { auto provider = agi::CreateConvertAudioProvider(agi::make_unique>()); int16_t data[256]; - provider->GetAudio(data, 0, 256); + provider->GetInt16MonoAudio(data, 0, 256); for (int i = 0; i < 256; ++i) ASSERT_EQ((i - 128) * 256, data[i]); } @@ -243,13 +243,13 @@ TEST(lagi_audio, convert_32bit) { auto provider = agi::CreateConvertAudioProvider(std::move(src)); int16_t sample; - provider->GetAudio(&sample, 0, 1); + provider->GetInt16MonoAudio(&sample, 0, 1); EXPECT_EQ(SHRT_MIN, sample); - provider->GetAudio(&sample, 1LL << 31, 1); + provider->GetInt16MonoAudio(&sample, 1LL << 31, 1); EXPECT_EQ(0, sample); - provider->GetAudio(&sample, (1LL << 32) - 1, 1); + provider->GetInt16MonoAudio(&sample, (1LL << 32) - 1, 1); EXPECT_EQ(SHRT_MAX, sample); } @@ -310,10 +310,10 @@ TEST(lagi_audio, stereo_downmix) { }; auto provider = agi::CreateConvertAudioProvider(agi::make_unique()); - EXPECT_EQ(1, provider->GetChannels()); + EXPECT_EQ(2, provider->GetChannels()); int16_t samples[100]; - provider->GetAudio(samples, 0, 100); + provider->GetInt16MonoAudio(samples, 0, 100); for (int i = 0; i < 100; ++i) EXPECT_EQ(i, samples[i]); } @@ -333,27 +333,27 @@ struct FloatAudioProvider : agi::AudioProvider { auto out = static_cast(buf); for (int64_t end = start + count; start < end; ++start) { 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) { auto provider = agi::CreateConvertAudioProvider(agi::make_unique>()); - EXPECT_FALSE(provider->AreSamplesFloat()); + EXPECT_TRUE(provider->AreSamplesFloat()); 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) ASSERT_EQ(i + SHRT_MIN, samples[i]); } TEST(lagi_audio, double_conversion) { auto provider = agi::CreateConvertAudioProvider(agi::make_unique>()); - EXPECT_FALSE(provider->AreSamplesFloat()); + EXPECT_TRUE(provider->AreSamplesFloat()); 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) ASSERT_EQ(i + SHRT_MIN, samples[i]); } @@ -551,4 +551,4 @@ TEST(lagi_audio, wave64_truncated) { } agi::fs::Remove(path); -} +} \ No newline at end of file