From 3dfea0c315e9113f908478245b9d698a41cb030f Mon Sep 17 00:00:00 2001 From: Ristellise Date: Wed, 10 Aug 2022 16:31:02 +0800 Subject: [PATCH 1/5] Add XAudio2 --- CMakeLists.txt | 2 +- meson.build | 41 ++- meson_options.txt | 3 +- src/audio_player.cpp | 4 + src/audio_player_xaudio2.cpp | 694 +++++++++++++++++++++++++++++++++++ 5 files changed, 730 insertions(+), 14 deletions(-) create mode 100644 src/audio_player_xaudio2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4647a9147..01e89b529 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,7 +497,7 @@ set_property(SOURCE src/subtitles_provider_csri.cpp PROPERTY INCLUDE_DIRECTORIES if(MSVC) target_link_libraries (Aegisub dsound) add_definitions("-DWITH_DIRECTSOUND") - target_sources(Aegisub PRIVATE src/audio_player_dsound.cpp src/audio_player_dsound2.cpp) + target_sources(Aegisub PRIVATE src/audio_player_dsound.cpp src/audio_player_dsound2.cpp src/audio_player_xaudio2.cpp) endif(MSVC) find_package(FFMS2) diff --git a/meson.build b/meson.build index 616c86053..5527cfbe8 100644 --- a/meson.build +++ b/meson.build @@ -237,20 +237,37 @@ if host_machine.system() == 'windows' and get_option('avisynth').enabled() deps_inc += avs.include_directories('AviSynth-Headers') endif -if host_machine.system() == 'windows' and not get_option('directsound').disabled() - dsound_dep = cc.find_library('dsound', required: get_option('directsound')) - winmm_dep = cc.find_library('winmm', required: get_option('directsound')) - ole32_dep = cc.find_library('ole32', required: get_option('directsound')) - have_dsound_h = cc.has_header('dsound.h') - if not have_dsound_h and get_option('directsound').enabled() - error('DirectSound enabled but dsound.h not found') +if host_machine.system() == 'windows' + + if not get_option('directsound').disabled() + dsound_dep = cc.find_library('dsound', required: get_option('directsound')) + winmm_dep = cc.find_library('winmm', required: get_option('directsound')) + ole32_dep = cc.find_library('ole32', required: get_option('directsound')) + have_dsound_h = cc.has_header('dsound.h') + if not have_dsound_h and get_option('directsound').enabled() + error('DirectSound enabled but dsound.h not found') + endif + + dxguid_dep = cc.find_library('dxguid', required: true) + if dsound_dep.found() and winmm_dep.found() and ole32_dep.found() and dxguid_dep.found() and have_dsound_h + deps += [dsound_dep, winmm_dep, ole32_dep, dxguid_dep] + conf.set('WITH_DIRECTSOUND', 1) + dep_avail += 'DirectSound' + endif endif - dxguid_dep = cc.find_library('dxguid', required: true) - if dsound_dep.found() and winmm_dep.found() and ole32_dep.found() and dxguid_dep.found() and have_dsound_h - deps += [dsound_dep, winmm_dep, ole32_dep, dxguid_dep] - conf.set('WITH_DIRECTSOUND', 1) - dep_avail += 'DirectSound' + 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 + deps += [xaudio2_dep] + conf.set('WITH_XAUDIO2', 1) + dep_avail += 'XAudio2' + endif + + if not have_dsound_h and get_option('xaudio2').enabled() + error('xaudio2 enabled but xaudio2.h not found') + endif endif endif diff --git a/meson_options.txt b/meson_options.txt index 3bc0461cd..97a183f22 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,7 +3,8 @@ option('openal', type: 'feature', description: 'OpenAL audio output') option('libpulse', type: 'feature', description: 'PulseAudio audio output') option('portaudio', type: 'feature', description: 'PortAudio audio output') option('directsound', type: 'feature', description: 'DirectSound audio output') -option('default_audio_output', type: 'combo', choices: ['auto', 'ALSA', 'OpenAL', 'PulseAudio', 'PortAudio', 'DirectSound'], description: 'Default audio output') +option('xaudio2', type: 'feature', description: 'XAudio2 audio output') +option('default_audio_output', type: 'combo', choices: ['auto', 'ALSA', 'OpenAL', 'PulseAudio', 'PortAudio', 'DirectSound', 'XAudio2'], description: 'Default audio output') option('ffms2', type: 'feature', description: 'FFMS2 video source') option('avisynth', type: 'feature', description: 'AviSynth video source') diff --git a/src/audio_player.cpp b/src/audio_player.cpp index f5a8327ca..c46ba0a40 100644 --- a/src/audio_player.cpp +++ b/src/audio_player.cpp @@ -43,6 +43,7 @@ std::unique_ptr CreateAlsaPlayer(agi::AudioProvider *providers, wxWindow *window); std::unique_ptr CreateDirectSoundPlayer(agi::AudioProvider *providers, wxWindow *window); std::unique_ptr CreateDirectSound2Player(agi::AudioProvider *providers, wxWindow *window); +std::unique_ptr CreateXAudio2Player(agi::AudioProvider *providers, wxWindow *window); std::unique_ptr CreateOpenALPlayer(agi::AudioProvider *providers, wxWindow *window); std::unique_ptr CreatePortAudioPlayer(agi::AudioProvider *providers, wxWindow *window); std::unique_ptr CreatePulseAudioPlayer(agi::AudioProvider *providers, wxWindow *window); @@ -63,6 +64,9 @@ namespace { {"DirectSound-old", CreateDirectSoundPlayer, false}, {"DirectSound", CreateDirectSound2Player, false}, #endif +#ifdef WITH_XAUDIO2 + {"Xaudio2", CreateXAudio2Player, false}, +#endif #ifdef WITH_OPENAL {"OpenAL", CreateOpenALPlayer, false}, #endif diff --git a/src/audio_player_xaudio2.cpp b/src/audio_player_xaudio2.cpp new file mode 100644 index 000000000..a7cbf0f28 --- /dev/null +++ b/src/audio_player_xaudio2.cpp @@ -0,0 +1,694 @@ +// Copyright (c) 2019, Qirui Wang +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * 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. +// +// Aegisub Project http://www.aegisub.org/ + +#ifdef WITH_XAUDIO2 +#include "include/aegisub/audio_player.h" + +#include "options.h" + +#include +#include +#include +#include + +#ifndef XAUDIO2_REDIST +#include +#else +#include +#endif + +namespace { +class XAudio2Thread; + +/// @class XAudio2Player +/// @brief XAudio2-based audio player +/// +/// The core design idea is to have a playback thread that performs all playback operations, and use the player object as a proxy to send commands to the playback thread. +class XAudio2Player final : public AudioPlayer { + /// The playback thread + std::unique_ptr thread; + + /// Desired length in milliseconds to write ahead of the playback cursor + int WantedLatency; + + /// Multiplier for WantedLatency to get total buffer length + int BufferLength; + + /// @brief Tell whether playback thread is alive + /// @return True if there is a playback thread and it's ready + bool IsThreadAlive(); + +public: + /// @brief Constructor + XAudio2Player(agi::AudioProvider* provider); + /// @brief Destructor + ~XAudio2Player() = default; + + /// @brief Start playback + /// @param start First audio frame to play + /// @param count Number of audio frames to play + void Play(int64_t start, int64_t count); + + /// @brief Stop audio playback + /// @param timerToo Whether to also stop the playback update timer + void Stop(); + + /// @brief Tell whether playback is active + /// @return True if audio is playing back + bool IsPlaying(); + + /// @brief Get playback end position + /// @return Audio frame index + /// + /// Returns 0 if playback is stopped or there is no playback thread + int64_t GetEndPosition(); + /// @brief Get approximate playback position + /// @return Index of audio frame user is currently hearing + /// + /// Returns 0 if playback is stopped or there is no playback thread + int64_t GetCurrentPosition(); + + /// @brief Change playback end position + /// @param pos New end position + void SetEndPosition(int64_t pos); + + /// @brief Change playback volume + /// @param vol Amplification factor + void SetVolume(double vol); +}; + +/// @brief RAII support class to init and de-init the COM library +struct COMInitialization { + + /// Flag set if an inited COM library is managed + bool inited = false; + + /// @brief Destructor, de-inits COM if it is inited + ~COMInitialization() { + if (inited) CoUninitialize(); + } + + /// @brief Initialise the COM library as single-threaded apartment if isn't already inited by us + bool Init() { + if (!inited && SUCCEEDED(CoInitialize(nullptr))) + inited = true; + return inited; + } +}; + +struct ReleaseCOMObject { + void operator()(IUnknown* obj) { + if (obj) obj->Release(); + } +}; + +/// @brief RAII wrapper around Win32 HANDLE type +struct Win32KernelHandle final : public agi::scoped_holder { + /// @brief Create with a managed handle + /// @param handle Win32 handle to manage + Win32KernelHandle(HANDLE handle = 0) :scoped_holder(handle, CloseHandle) {} + + Win32KernelHandle& operator=(HANDLE new_handle) { + scoped_holder::operator=(new_handle); + return *this; + } +}; + +/// @class XAudio2Thread +/// @brief Playback thread class for XAudio2Player +/// +/// Not based on wxThread, but uses Win32 threads directly +class XAudio2Thread :public IXAudio2VoiceCallback { + /// @brief Win32 thread entry point + /// @param parameter Pointer to our thread object + /// @return Thread return value, always 0 here + static unsigned int __stdcall ThreadProc(void* parameter); + /// @brief Thread entry point + void Run(); + + /// @brief Check for error state and throw exception if one occurred + void CheckError(); + + /// Win32 handle to the thread + Win32KernelHandle thread_handle; + + /// Event object, world to thread, set to start playback + Win32KernelHandle event_start_playback; + + /// Event object, world to thread, set to stop playback + Win32KernelHandle event_stop_playback; + + /// Event object, world to thread, set if playback end time was updated + Win32KernelHandle event_update_end_time; + + /// Event object, world to thread, set if the volume was changed + Win32KernelHandle event_set_volume; + + /// Event object, world to thread, set if the thread should end as soon as possible + Win32KernelHandle event_buffer_end; + + /// Event object, world to thread, set if the thread should end as soon as possible + Win32KernelHandle event_kill_self; + + /// Event object, thread to world, set when the thread has entered its main loop + Win32KernelHandle thread_running; + + /// Event object, thread to world, set when playback is ongoing + Win32KernelHandle is_playing; + + /// Event object, thread to world, set if an error state has occurred (implies thread is dying) + Win32KernelHandle error_happened; + + /// Statically allocated error message text describing reason for error_happened being set + const char* error_message = nullptr; + + /// Playback volume, 1.0 is "unchanged" + double volume = 1.0; + + /// Audio frame to start playback at + int64_t start_frame = 0; + + /// Audio frame to end playback at + int64_t end_frame = 0; + + /// Desired length in milliseconds to write ahead of the playback cursor + int wanted_latency; + + /// Multiplier for WantedLatency to get total buffer length + int buffer_length; + + /// System millisecond timestamp of last playback start, used to calculate playback position + ULONGLONG last_playback_restart; + + /// Audio provider to take sample data from + agi::AudioProvider* provider; + + /// Buffer occupied indicator + std::vector buffer_occupied; + +public: + /// @brief Constructor, creates and starts playback thread + /// @param provider Audio provider to take sample data from + /// @param WantedLatency Desired length in milliseconds to write ahead of the playback cursor + /// @param BufferLength Multiplier for WantedLatency to get total buffer length + XAudio2Thread(agi::AudioProvider* provider, int WantedLatency, int BufferLength); + /// @brief Destructor, waits for thread to have died + ~XAudio2Thread(); + + // IXAudio2VoiceCallback + void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32 BytesRequired) override {} + void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() override {} + void STDMETHODCALLTYPE OnStreamEnd() override {} + void STDMETHODCALLTYPE OnBufferStart(void* pBufferContext) override {} + void STDMETHODCALLTYPE OnBufferEnd(void* pBufferContext) override { + intptr_t i = reinterpret_cast(pBufferContext); + buffer_occupied[i] = false; + SetEvent(event_buffer_end); + } + void STDMETHODCALLTYPE OnLoopEnd(void* pBufferContext) override {} + void STDMETHODCALLTYPE OnVoiceError(void* pBufferContext, HRESULT Error) override {} + + /// @brief Start audio playback + /// @param start Audio frame to start playback at + /// @param count Number of audio frames to play + void Play(int64_t start, int64_t count); + + /// @brief Stop audio playback + void Stop(); + + /// @brief Change audio playback end point + /// @param new_end_frame New last audio frame to play + /// + /// Playback stops instantly if new_end_frame is before the current playback position + void SetEndFrame(int64_t new_end_frame); + + /// @brief Change audio playback volume + /// @param new_volume New playback amplification factor, 1.0 is "unchanged" + void SetVolume(double new_volume); + + /// @brief Tell whether audio playback is active + /// @return True if audio is being played back, false if it is not + bool IsPlaying(); + + /// @brief Get approximate current audio frame being heard by the user + /// @return Audio frame index + /// + /// Returns 0 if not playing + int64_t GetCurrentFrame(); + + /// @brief Get audio playback end point + /// @return Audio frame index + int64_t GetEndFrame(); + + /// @brief Tell whether playback thread has died + /// @return True if thread is no longer running + bool IsDead(); +}; + +unsigned int __stdcall XAudio2Thread::ThreadProc(void* parameter) { + static_cast(parameter)->Run(); + return 0; +} + +/// Macro used to set error_message, error_happened and end the thread +#define REPORT_ERROR(msg) \ +{ \ + ResetEvent(is_playing); \ + error_message = "XAudio2Thread: " msg; \ + SetEvent(error_happened); \ + return; \ +} + +void XAudio2Thread::Run() { + COMInitialization COM_library; + if (!COM_library.Init()) { + REPORT_ERROR("Could not initialise COM") + } + IXAudio2* pXAudio2; + IXAudio2SourceVoice* pSourceVoice; + HRESULT hr; + if (FAILED(hr = XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR))) { + REPORT_ERROR("Failed initializing XAudio2") + } + IXAudio2MasteringVoice* pMasterVoice = NULL; + if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasterVoice))) { + REPORT_ERROR("Failed initializing XAudio2 MasteringVoice") + } + + // Describe the wave format + WAVEFORMATEX wfx; + wfx.nSamplesPerSec = provider->GetSampleRate(); + wfx.cbSize = 0; + bool original = true; + wfx.wFormatTag = provider->AreSamplesFloat() ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; + wfx.nChannels = provider->GetChannels(); + wfx.wBitsPerSample = provider->GetBytesPerSample() * 8; + wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + + if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx, 0, 2, this))) { + if (hr == XAUDIO2_E_INVALID_CALL) { + // Retry with 16bit mono + original = false; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.wBitsPerSample = sizeof(int16_t) * 8; + wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx, 0, 2, this))) { + REPORT_ERROR("Failed initializing XAudio2 SourceVoice") + } + } + else { + REPORT_ERROR("Failed initializing XAudio2 SourceVoice") + } + } + + // Now we're ready to roll! + SetEvent(thread_running); + bool running = true; + + HANDLE events_to_wait[] = { + event_start_playback, + event_stop_playback, + event_update_end_time, + event_set_volume, + event_buffer_end, + event_kill_self + }; + + int64_t next_input_frame = 0; + DWORD buffer_offset = 0; + bool playback_should_be_running = false; + int current_latency = wanted_latency; + const int wanted_frames = wanted_latency * wfx.nSamplesPerSec / 1000; + const DWORD wanted_latency_bytes = wanted_frames * wfx.nBlockAlign; + std::vector > buff(buffer_length); + for (auto& i : buff) + i.resize(wanted_latency_bytes); + + while (running) { + DWORD wait_result = WaitForMultipleObjects(sizeof(events_to_wait) / sizeof(HANDLE), events_to_wait, FALSE, INFINITE); + + switch (wait_result) { + case WAIT_OBJECT_0 + 0: + // Start or restart playback + pSourceVoice->Stop(); + pSourceVoice->FlushSourceBuffers(); + + next_input_frame = start_frame; + playback_should_be_running = true; + pSourceVoice->Start(); + SetEvent(is_playing); + goto do_fill_buffer; + + case WAIT_OBJECT_0 + 1: + stop_playback: + // Stop playing + ResetEvent(is_playing); + pSourceVoice->Stop(); + pSourceVoice->FlushSourceBuffers(); + playback_should_be_running = false; + break; + + case WAIT_OBJECT_0 + 2: + // Set end frame + if (end_frame <= next_input_frame) + goto stop_playback; + goto do_fill_buffer; + + case WAIT_OBJECT_0 + 3: + // Change volume + pSourceVoice->SetVolume(volume); + break; + + case WAIT_OBJECT_0 + 4: + // Buffer end + do_fill_buffer: + // Time to fill more into buffer + if (!playback_should_be_running) + break; + + for (int i = 0; i < buffer_length; ++i) { + if (!buffer_occupied[i]) { + int fill_len = std::min(end_frame - next_input_frame, wanted_frames); + if (fill_len <= 0) + break; + buffer_occupied[i] = true; + if (original) + provider->GetAudio(buff[i].data(), next_input_frame, fill_len); + else + provider->GetInt16MonoAudio(reinterpret_cast(buff[i].data()), next_input_frame, fill_len); + next_input_frame += fill_len; + XAUDIO2_BUFFER xbf; + xbf.Flags = fill_len + next_input_frame == end_frame ? XAUDIO2_END_OF_STREAM : 0; + xbf.AudioBytes = fill_len * wfx.nBlockAlign; + xbf.pAudioData = buff[i].data(); + xbf.PlayBegin = 0; + xbf.PlayLength = 0; + xbf.LoopBegin = 0; + xbf.LoopLength = 0; + xbf.LoopCount = 0; + xbf.pContext = reinterpret_cast(static_cast(i)); + if (FAILED(hr = pSourceVoice->SubmitSourceBuffer(&xbf))) { + REPORT_ERROR("Failed initializing Submit Buffer") + } + } + } + break; + + case WAIT_OBJECT_0 + 5: + // Perform suicide + running = false; + pXAudio2->Release(); + ResetEvent(is_playing); + playback_should_be_running = false; + break; + + default: + REPORT_ERROR("Something bad happened while waiting on events in playback loop, either the wait failed or an event object was abandoned.") + break; + } + } +} + +#undef REPORT_ERROR + +void XAudio2Thread::CheckError() +{ + try { + switch (WaitForSingleObject(error_happened, 0)) + { + case WAIT_OBJECT_0: + throw error_message; + + case WAIT_ABANDONED: + throw "The XAudio2Thread error signal event was abandoned, somehow. This should not happen."; + + case WAIT_FAILED: + throw "Failed checking state of XAudio2Thread error signal event."; + + case WAIT_TIMEOUT: + default: + return; + } + } + catch (...) { + ResetEvent(is_playing); + ResetEvent(thread_running); + throw; + } +} + +XAudio2Thread::XAudio2Thread(agi::AudioProvider* provider, int WantedLatency, int BufferLength) + : event_start_playback(CreateEvent(0, FALSE, FALSE, 0)) + , event_stop_playback(CreateEvent(0, FALSE, FALSE, 0)) + , event_update_end_time(CreateEvent(0, FALSE, FALSE, 0)) + , event_set_volume(CreateEvent(0, FALSE, FALSE, 0)) + , event_buffer_end(CreateEvent(0, FALSE, FALSE, 0)) + , event_kill_self(CreateEvent(0, FALSE, FALSE, 0)) + , thread_running(CreateEvent(0, TRUE, FALSE, 0)) + , is_playing(CreateEvent(0, TRUE, FALSE, 0)) + , error_happened(CreateEvent(0, FALSE, FALSE, 0)) + , wanted_latency(WantedLatency) + , buffer_length(BufferLength < XAUDIO2_MAX_QUEUED_BUFFERS ? BufferLength : XAUDIO2_MAX_QUEUED_BUFFERS) + , provider(provider) + , buffer_occupied(BufferLength) +{ + if (!(thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0))) { + throw AudioPlayerOpenError("Failed creating playback thread in XAudio2Player. This is bad."); + } + + HANDLE running_or_error[] = { thread_running, error_happened }; + switch (WaitForMultipleObjects(2, running_or_error, FALSE, INFINITE)) { + case WAIT_OBJECT_0: + // running, all good + return; + + case WAIT_OBJECT_0 + 1: + // error happened, we fail + throw AudioPlayerOpenError(error_message ? error_message : "Failed wait for thread start or thread error in XAudio2Player. This is bad."); + + default: + throw AudioPlayerOpenError("Failed wait for thread start or thread error in XAudio2Player. This is bad."); + } +} + +XAudio2Thread::~XAudio2Thread() { + SetEvent(event_kill_self); + WaitForSingleObject(thread_handle, INFINITE); +} + +void XAudio2Thread::Play(int64_t start, int64_t count) +{ + CheckError(); + + start_frame = start; + end_frame = start + count; + SetEvent(event_start_playback); + + last_playback_restart = GetTickCount64(); + + // Block until playback actually begins to avoid race conditions with + // checking if playback is in progress + HANDLE events_to_wait[] = { is_playing, error_happened }; + switch (WaitForMultipleObjects(2, events_to_wait, FALSE, INFINITE)) { + case WAIT_OBJECT_0 + 0: // Playing + LOG_D("audio/player/xaudio2") << "Playback begun"; + break; + case WAIT_OBJECT_0 + 1: // Error + throw error_message; + default: + throw agi::InternalError("Unexpected result from WaitForMultipleObjects in XAudio2Thread::Play"); + } +} + +void XAudio2Thread::Stop() { + CheckError(); + + SetEvent(event_stop_playback); +} + +void XAudio2Thread::SetEndFrame(int64_t new_end_frame) { + CheckError(); + + end_frame = new_end_frame; + SetEvent(event_update_end_time); +} + +void XAudio2Thread::SetVolume(double new_volume) { + CheckError(); + + volume = new_volume; + SetEvent(event_set_volume); +} + +bool XAudio2Thread::IsPlaying() { + CheckError(); + + switch (WaitForSingleObject(is_playing, 0)) + { + case WAIT_ABANDONED: + throw "The XAudio2Thread playback state event was abandoned, somehow. This should not happen."; + + case WAIT_FAILED: + throw "Failed checking state of XAudio2Thread playback state event."; + + case WAIT_OBJECT_0: + return true; + + case WAIT_TIMEOUT: + default: + return false; + } +} + +int64_t XAudio2Thread::GetCurrentFrame() { + CheckError(); + if (!IsPlaying()) return 0; + ULONGLONG milliseconds_elapsed = GetTickCount64() - last_playback_restart; + return start_frame + milliseconds_elapsed * provider->GetSampleRate() / 1000; +} + +int64_t XAudio2Thread::GetEndFrame() { + CheckError(); + return end_frame; +} + +bool XAudio2Thread::IsDead() { + switch (WaitForSingleObject(thread_running, 0)) + { + case WAIT_OBJECT_0: + return false; + default: + return true; + } +} + +XAudio2Player::XAudio2Player(agi::AudioProvider* provider) :AudioPlayer(provider) { + // The buffer will hold BufferLength times WantedLatency milliseconds of audio + WantedLatency = OPT_GET("Player/Audio/DirectSound/Buffer Latency")->GetInt(); + BufferLength = OPT_GET("Player/Audio/DirectSound/Buffer Length")->GetInt(); + + // sanity checking + if (WantedLatency <= 0) + WantedLatency = 100; + if (BufferLength <= 0) + BufferLength = 5; + + try { + thread = agi::make_unique(provider, WantedLatency, BufferLength); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + throw AudioPlayerOpenError(msg); + } +} + +bool XAudio2Player::IsThreadAlive() { + if (thread && thread->IsDead()) + thread.reset(); + return static_cast(thread); +} + +void XAudio2Player::Play(int64_t start, int64_t count) { + try { + thread->Play(start, count); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + } +} + +void XAudio2Player::Stop() { + try { + if (IsThreadAlive()) thread->Stop(); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + } +} + +bool XAudio2Player::IsPlaying() { + try { + if (!IsThreadAlive()) return false; + return thread->IsPlaying(); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + return false; + } +} + +int64_t XAudio2Player::GetEndPosition() { + try { + if (!IsThreadAlive()) return 0; + return thread->GetEndFrame(); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + return 0; + } +} + +int64_t XAudio2Player::GetCurrentPosition() { + try { + if (!IsThreadAlive()) return 0; + return thread->GetCurrentFrame(); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + return 0; + } +} + +void XAudio2Player::SetEndPosition(int64_t pos) { + try { + if (IsThreadAlive()) thread->SetEndFrame(pos); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + } +} + +void XAudio2Player::SetVolume(double vol) { + try { + if (IsThreadAlive()) thread->SetVolume(vol); + } + catch (const char* msg) { + LOG_E("audio/player/xaudio2") << msg; + } +} +} + +std::unique_ptr CreateXAudio2Player(agi::AudioProvider* provider, wxWindow*) { + return agi::make_unique(provider); +} + +#endif // WITH_XAUDIO2 From fd28458ed81a08fe92d0aa9405b4e6c6a994bbed Mon Sep 17 00:00:00 2001 From: Ristellise Date: Wed, 10 Aug 2022 21:09:41 +0800 Subject: [PATCH 2/5] 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 From 994e50048a21755d84ef46a25c8b5f7919881cab Mon Sep 17 00:00:00 2001 From: Ristellise Date: Wed, 10 Aug 2022 21:23:56 +0800 Subject: [PATCH 3/5] Finish up XAudio with a round of bugfixes --- libaegisub/audio/provider.cpp | 5 ++--- libaegisub/audio/provider_lock.cpp | 5 +++++ libaegisub/audio/provider_ram.cpp | 6 +++--- src/audio_provider_ffmpegsource.cpp | 13 ++++++++----- src/audio_renderer_spectrum.cpp | 4 ++-- src/audio_renderer_waveform.cpp | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/libaegisub/audio/provider.cpp b/libaegisub/audio/provider.cpp index b7ad7847c..0abb67eeb 100644 --- a/libaegisub/audio/provider.cpp +++ b/libaegisub/audio/provider.cpp @@ -73,7 +73,6 @@ public: } 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); @@ -242,7 +241,7 @@ void SaveAudioClip(AudioProvider const& provider, fs::path const& path, int star out.write("WAVEfmt "); out.write(16); // Size of chunk - out.write(1); // compression format (PCM) + out.write(provider.AreSamplesFloat() ? 3 : 1); // compression format (1: WAVE_FORMAT_PCM, 3: WAVE_FORMAT_IEEE_FLOAT) out.write(provider.GetChannels()); out.write(provider.GetSampleRate()); out.write(provider.GetSampleRate() * provider.GetChannels() * provider.GetBytesPerSample()); @@ -262,4 +261,4 @@ void SaveAudioClip(AudioProvider const& provider, fs::path const& path, int star out.write(buf); } } -} +} \ No newline at end of file diff --git a/libaegisub/audio/provider_lock.cpp b/libaegisub/audio/provider_lock.cpp index eb397e410..e405487a1 100644 --- a/libaegisub/audio/provider_lock.cpp +++ b/libaegisub/audio/provider_lock.cpp @@ -29,6 +29,11 @@ class LockAudioProvider final : public agi::AudioProviderWrapper { source->GetAudio(buf, start, count); } + void FillBufferInt16Mono(int16_t *buf, int64_t start, int64_t count) const override { + std::unique_lock lock(mutex); + source->GetInt16MonoAudio(buf, start, count); + } + public: LockAudioProvider(std::unique_ptr src) : AudioProviderWrapper(std::move(src)) diff --git a/libaegisub/audio/provider_ram.cpp b/libaegisub/audio/provider_ram.cpp index 3f070c87e..e708b0112 100644 --- a/libaegisub/audio/provider_ram.cpp +++ b/libaegisub/audio/provider_ram.cpp @@ -46,14 +46,14 @@ public: decoded_samples = 0; try { - blockcache.resize((source->GetNumSamples() * source->GetBytesPerSample() + CacheBlockSize - 1) >> CacheBits); + blockcache.resize((num_samples * bytes_per_sample * channels + CacheBlockSize - 1) >> CacheBits); } catch (std::bad_alloc const&) { throw AudioProviderError("Not enough memory available to cache in RAM"); } decoder = std::thread([&] { - int64_t readsize = CacheBlockSize / source->GetBytesPerSample(); + int64_t readsize = CacheBlockSize / bytes_per_sample / channels; for (size_t i = 0; i < blockcache.size(); i++) { if (cancelled) break; auto actual_read = std::min(readsize, num_samples - i * readsize); @@ -95,4 +95,4 @@ namespace agi { std::unique_ptr CreateRAMAudioProvider(std::unique_ptr src) { return agi::make_unique(std::move(src)); } -} +} \ No newline at end of file diff --git a/src/audio_provider_ffmpegsource.cpp b/src/audio_provider_ffmpegsource.cpp index 46e1412f5..1c109220f 100644 --- a/src/audio_provider_ffmpegsource.cpp +++ b/src/audio_provider_ffmpegsource.cpp @@ -126,8 +126,10 @@ void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) { // reindex if the error handling mode has changed FFMS_IndexErrorHandling ErrorHandling = GetErrorHandlingMode(); +#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (2 << 8) | 0) if (Index && FFMS_GetErrorHandling(Index) != ErrorHandling) Index = nullptr; +#endif // moment of truth if (!Index) { @@ -165,22 +167,23 @@ void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) { throw agi::AudioProviderError("unknown or unsupported sample format"); } +#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (4 << 8) | 0) if (OPT_GET("Provider/Audio/FFmpegSource/Downmix")->GetBool()) { - if (channels > 2 || bytes_per_sample != 2 || float_samples) { + if (channels > 1 || bytes_per_sample != 2 || float_samples) { std::unique_ptr opt(FFMS_CreateResampleOptions(AudioSource), FFMS_DestroyResampleOptions); - if (channels > 2) - opt->ChannelLayout = FFMS_CH_FRONT_LEFT | FFMS_CH_FRONT_RIGHT; + opt->ChannelLayout = FFMS_CH_FRONT_CENTER; opt->SampleFormat = FFMS_FMT_S16; // Might fail if FFMS2 wasn't built with libavresample if (!FFMS_SetOutputFormatA(AudioSource, opt.get(), nullptr)) { - channels = channels > 2 ? 2 : channels; + channels = 1; bytes_per_sample = 2; float_samples = false; } } } +#endif } } @@ -189,4 +192,4 @@ std::unique_ptr CreateFFmpegSourceAudioProvider(agi::fs::pat return agi::make_unique(file, br); } -#endif /* WITH_FFMS2 */ +#endif /* WITH_FFMS2 */ \ No newline at end of file diff --git a/src/audio_renderer_spectrum.cpp b/src/audio_renderer_spectrum.cpp index 217090a6f..7190c448a 100644 --- a/src/audio_renderer_spectrum.cpp +++ b/src/audio_renderer_spectrum.cpp @@ -208,8 +208,8 @@ void AudioSpectrumRenderer::FillBlock(size_t block_index, float *block) assert(block); int64_t first_sample = (((int64_t)block_index) << derivation_dist) - ((int64_t)1 << derivation_size); - provider->GetAudio(&audio_scratch[0], first_sample, 2 << derivation_size); - + provider->GetInt16MonoAudio(audio_scratch.data(), first_sample, 2 << derivation_size); + // Because the FFTs used here are unnormalized DFTs, we have to compensate // the possible length difference between derivation_size used in the // calculations and its user-provided counterpart. Thus, the display is diff --git a/src/audio_renderer_waveform.cpp b/src/audio_renderer_waveform.cpp index d5bb802fb..789dca024 100644 --- a/src/audio_renderer_waveform.cpp +++ b/src/audio_renderer_waveform.cpp @@ -88,7 +88,7 @@ void AudioWaveformRenderer::Render(wxBitmap &bmp, int start, AudioRenderingStyle for (int x = 0; x < rect.width; ++x) { - provider->GetAudio(audio_buffer.get(), (int64_t)cur_sample, (int64_t)pixel_samples); + provider->GetInt16MonoAudio(reinterpret_cast(audio_buffer.get()), (int64_t)cur_sample, (int64_t)pixel_samples); cur_sample += pixel_samples; int peak_min = 0, peak_max = 0; From 6906b6e5d7c2bfa3c6fdacc1b2bc7682cecfa1d5 Mon Sep 17 00:00:00 2001 From: Ristellise Date: Thu, 11 Aug 2022 12:28:35 +0800 Subject: [PATCH 4/5] [Shinon] Enable Directsound2 player to use more than 1 channel audio. - DS2 Player has a similar structure to XAudio, so I don't see any reason why not to enable 1 channel+ audio. - Haven't tried with 5.1 channel sources but I believe it should be the same as 2 channel (As in, Directsound will downmix the audio to 2 channel) - Moved the volume setting to using the player directly and from some quick audio tests, -10000 is too soft. I tried with -5000 instead which seems to be alright. --- src/audio_player_dsound2.cpp | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/audio_player_dsound2.cpp b/src/audio_player_dsound2.cpp index e0d585818..e8a144eb2 100644 --- a/src/audio_player_dsound2.cpp +++ b/src/audio_player_dsound2.cpp @@ -48,7 +48,6 @@ #include #include #include -#include namespace { class DirectSoundPlayer2Thread; @@ -318,15 +317,14 @@ void DirectSoundPlayer2Thread::Run() // Describe the wave format WAVEFORMATEX waveFormat; - waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nSamplesPerSec = provider->GetSampleRate(); - //waveFormat.nChannels = provider->GetChannels(); - //waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8; - waveFormat.nChannels = 1; - waveFormat.wBitsPerSample = sizeof(int16_t) * 8; + waveFormat.cbSize = 0; + waveFormat.wFormatTag = provider->AreSamplesFloat() ? 3 : WAVE_FORMAT_PCM; // Eh fuck it. + waveFormat.nChannels = provider->GetChannels(); + waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8; waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = sizeof(waveFormat); + //waveFormat.cbSize = sizeof(waveFormat); // And the buffer itself int aim = waveFormat.nAvgBytesPerSec * (wanted_latency*buffer_length)/1000; @@ -335,7 +333,7 @@ void DirectSoundPlayer2Thread::Run() DWORD bufSize = mid(min,aim,max); // size of entire playback buffer DSBUFFERDESC desc; desc.dwSize = sizeof(DSBUFFERDESC); - desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; + desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; desc.dwBufferBytes = bufSize; desc.dwReserved = 0; desc.lpwfxFormat = &waveFormat; @@ -372,7 +370,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()*/sizeof(int16_t)/1000; + const DWORD wanted_latency_bytes = wanted_latency*waveFormat.nSamplesPerSec*provider->GetBytesPerSample()/1000; while (running) { @@ -425,7 +423,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()*/sizeof(int16_t)); + current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample()); if (FAILED(bfr->Play(0, 0, 0))) REPORT_ERROR("Could not start single-buffer playback.") } @@ -464,6 +462,16 @@ stop_playback: goto do_fill_buffer; case WAIT_OBJECT_0+3: + { + LONG invert_volume = (LONG)((this->volume - 1.0) * 5000.0); // Hrmm weirdly it's half? + // Look, I would have used a min max but it just errored out for me lol. + if (invert_volume > DSBVOLUME_MAX) + invert_volume = DSBVOLUME_MAX; + else if (invert_volume < DSBVOLUME_MIN / 2) + invert_volume = DSBVOLUME_MIN / 2; + LOG_I("DS2") << "Earrape vlume: " <SetVolume(invert_volume); + } // Change volume // We aren't thread safe right now, filling the buffers grabs volume directly // from the field set by the controlling thread, but it shouldn't be a major @@ -556,7 +564,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()*/sizeof(int16_t)); + current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample()); } else { @@ -580,7 +588,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()*/sizeof(int16_t); + DWORD bytes_per_frame = provider->GetChannels() * provider->GetBytesPerSample(); DWORD buf1szf = buf1sz / bytes_per_frame; DWORD buf2szf = buf2sz / bytes_per_frame; @@ -611,7 +619,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v buf2sz = 0; } - provider->GetInt16MonoAudioWithVolume(reinterpret_cast(buf1), input_frame, buf1szf, volume); + provider->GetAudio(buf1, input_frame, buf1szf); input_frame += buf1szf; } @@ -624,7 +632,7 @@ DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, v buf2sz = buf2szf * bytes_per_frame; } - provider->GetInt16MonoAudioWithVolume(reinterpret_cast(buf2), input_frame, buf2szf, volume); + provider->GetAudio(buf2, input_frame, buf2szf); input_frame += buf2szf; } From 0b8f5ad8fa0b75c341e128e5b9569e536b998284 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Tue, 16 Aug 2022 20:34:00 +0200 Subject: [PATCH 5/5] XAudio PR: Revert some changes and fix on linux Mostly synchronizing with the updates made to the ffmpeg audio provider and removing the CMakeLists.txt --- CMakeLists.txt | 542 ------------------ .../include/libaegisub/audio/provider.h | 3 +- meson.build | 3 - src/audio_player_dsound2.cpp | 1 - src/audio_provider_ffmpegsource.cpp | 13 +- 5 files changed, 7 insertions(+), 555 deletions(-) delete mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 75182bbe8..000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,542 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -cmake_policy(SET CMP0074 NEW) - -project(Aegisub) - -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) - -include_directories("build") -include_directories("libaegisub/include") -include_directories("vendor/luajit/include") - -add_library(libaegisub STATIC - libaegisub/common/parser.cpp - libaegisub/ass/dialogue_parser.cpp - libaegisub/ass/time.cpp - libaegisub/ass/uuencode.cpp - libaegisub/audio/provider.cpp - libaegisub/audio/provider_convert.cpp - libaegisub/audio/provider_dummy.cpp - libaegisub/audio/provider_hd.cpp - libaegisub/audio/provider_lock.cpp - libaegisub/audio/provider_pcm.cpp - libaegisub/audio/provider_ram.cpp - libaegisub/common/cajun/elements.cpp - libaegisub/common/cajun/reader.cpp - libaegisub/common/cajun/writer.cpp - libaegisub/lua/modules/lfs.cpp - libaegisub/lua/modules/re.cpp - libaegisub/lua/modules/unicode.cpp - libaegisub/lua/modules/lpeg.c - libaegisub/lua/modules.cpp - libaegisub/lua/script_reader.cpp - libaegisub/lua/utils.cpp - libaegisub/common/calltip_provider.cpp - libaegisub/common/character_count.cpp - libaegisub/common/charset.cpp - libaegisub/common/charset_6937.cpp - libaegisub/common/charset_conv.cpp - libaegisub/common/color.cpp - libaegisub/common/file_mapping.cpp - libaegisub/common/format.cpp - libaegisub/common/fs.cpp - libaegisub/common/hotkey.cpp - libaegisub/common/io.cpp - libaegisub/common/json.cpp - libaegisub/common/kana_table.cpp - libaegisub/common/karaoke_matcher.cpp - libaegisub/common/keyframe.cpp - libaegisub/common/line_iterator.cpp - libaegisub/common/log.cpp - libaegisub/common/mru.cpp - libaegisub/common/option.cpp - libaegisub/common/option_value.cpp - libaegisub/common/path.cpp - libaegisub/common/thesaurus.cpp - libaegisub/common/util.cpp - libaegisub/common/vfr.cpp - libaegisub/common/ycbcr_conv.cpp - libaegisub/common/dispatch.cpp -) -if (UNIX) - target_sources(libaegisub PRIVATE - libaegisub/unix/access.cpp - libaegisub/unix/fs.cpp - libaegisub/unix/log.cpp - libaegisub/unix/path.cpp - libaegisub/unix/util.cpp - ) -elseif(WIN32) - target_sources(libaegisub PRIVATE - libaegisub/windows/access.cpp - libaegisub/windows/charset_conv_win.cpp - libaegisub/windows/fs.cpp - libaegisub/windows/lagi_pre.cpp - libaegisub/windows/log_win.cpp - libaegisub/windows/path_win.cpp - libaegisub/windows/util_win.cpp - ) -endif(UNIX) -SET_TARGET_PROPERTIES(libaegisub PROPERTIES PREFIX "") - -add_library(luabins STATIC - vendor/luabins/src/fwrite.c - vendor/luabins/src/load.c - vendor/luabins/src/luabins.c - vendor/luabins/src/luainternals.c - vendor/luabins/src/save.c - vendor/luabins/src/savebuffer.c - vendor/luabins/src/write.c -) - -add_executable(luajit-minilua vendor/luajit/src/host/minilua.c) -if (NOT MSVC) -target_link_libraries(luajit-minilua m) -endif(NOT MSVC) -add_custom_command(TARGET luajit-minilua POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_SOURCE_DIR}/vendor/luajit/src/gen - COMMAND luajit-minilua ../dynasm/dynasm.lua -D P64 -D JIT -D FFI -D FPU -D HFABI -D VER= -o gen/buildvm_arch.h vm_x86.dasc - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src -) -add_custom_command(TARGET luajit-minilua POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/src/libresrc/default_config_win.json ${PROJECT_SOURCE_DIR}/src/libresrc/default_config_platform.json - COMMAND luajit-minilua ../../tools/respack.lua manifest.respack default_config.cpp default_config.h - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/libresrc - BYPRODUCTS ${PROJECT_SOURCE_DIR}/src/libresrc/default_config.cpp ${PROJECT_SOURCE_DIR}/src/libresrc/default_config.h -) -add_custom_command(TARGET luajit-minilua POST_BUILD - COMMAND luajit-minilua ../../tools/respack.lua manifest.respack ../libresrc/bitmap.cpp ../libresrc/bitmap.h - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/bitmaps - BYPRODUCTS ${PROJECT_SOURCE_DIR}/src/libresrc/bitmap.cpp ${PROJECT_SOURCE_DIR}/src/libresrc/bitmap.h -) - -add_executable(luajit-buildvm - vendor/luajit/src/host/buildvm.c - vendor/luajit/src/host/buildvm_asm.c - vendor/luajit/src/host/buildvm_peobj.c - vendor/luajit/src/host/buildvm_lib.c - vendor/luajit/src/host/buildvm_fold.c -) -target_include_directories(luajit-buildvm PRIVATE vendor/luajit/src vendor/luajit/src/gen) -add_dependencies(luajit-buildvm luajit-minilua) -if(UNIX) - add_custom_command(TARGET luajit-buildvm POST_BUILD - COMMAND luajit-buildvm -m elfasm -o lj_vm.s - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src - BYPRODUCTS ${PROJECT_SOURCE_DIR}/vendor/luajit/src/lj_vm.s - ) - set_property(SOURCE vendor/luajit/src/lj_vm.s PROPERTY LANGUAGE C) -elseif(MSVC) - add_custom_command(TARGET luajit-buildvm POST_BUILD - COMMAND luajit-buildvm -m peobj -o lj_vm.obj - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src - BYPRODUCTS ${PROJECT_SOURCE_DIR}/vendor/luajit/src/lj_vm.obj - ) -endif(UNIX) -add_custom_command(TARGET luajit-buildvm POST_BUILD - COMMAND luajit-buildvm -m ffdef -o gen/lj_ffdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c - COMMAND luajit-buildvm -m bcdef -o gen/lj_bcdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c - COMMAND luajit-buildvm -m folddef -o gen/lj_folddef.h lj_opt_fold.c - COMMAND luajit-buildvm -m recdef -o gen/lj_recdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c - COMMAND luajit-buildvm -m libdef -o gen/lj_libdef.h lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c - COMMAND luajit-buildvm -m vmdef -o jit/vmdef.lua lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor/luajit/src -) - - -add_library(luajit STATIC - vendor/luajit/src/lj_gc.c - vendor/luajit/src/lj_err.c - vendor/luajit/src/lj_char.c - vendor/luajit/src/lj_bc.c - vendor/luajit/src/lj_obj.c - vendor/luajit/src/lj_str.c - vendor/luajit/src/lj_tab.c - vendor/luajit/src/lj_func.c - vendor/luajit/src/lj_udata.c - vendor/luajit/src/lj_meta.c - vendor/luajit/src/lj_debug.c - vendor/luajit/src/lj_state.c - vendor/luajit/src/lj_dispatch.c - vendor/luajit/src/lj_vmevent.c - vendor/luajit/src/lj_vmmath.c - vendor/luajit/src/lj_strscan.c - vendor/luajit/src/lj_api.c - vendor/luajit/src/lj_lex.c - vendor/luajit/src/lj_parse.c - vendor/luajit/src/lj_bcread.c - vendor/luajit/src/lj_bcwrite.c - vendor/luajit/src/lj_load.c - vendor/luajit/src/lj_ir.c - vendor/luajit/src/lj_opt_mem.c - vendor/luajit/src/lj_opt_fold.c - vendor/luajit/src/lj_opt_narrow.c - vendor/luajit/src/lj_opt_dce.c - vendor/luajit/src/lj_opt_loop.c - vendor/luajit/src/lj_opt_split.c - vendor/luajit/src/lj_opt_sink.c - vendor/luajit/src/lj_mcode.c - vendor/luajit/src/lj_snap.c - vendor/luajit/src/lj_record.c - vendor/luajit/src/lj_crecord.c - vendor/luajit/src/lj_ffrecord.c - vendor/luajit/src/lj_asm.c - vendor/luajit/src/lj_trace.c - vendor/luajit/src/lj_gdbjit.c - vendor/luajit/src/lj_ctype.c - vendor/luajit/src/lj_cdata.c - vendor/luajit/src/lj_cconv.c - vendor/luajit/src/lj_ccall.c - vendor/luajit/src/lj_ccallback.c - vendor/luajit/src/lj_carith.c - vendor/luajit/src/lj_clib.c - vendor/luajit/src/lj_cparse.c - vendor/luajit/src/lj_lib.c - vendor/luajit/src/lj_alloc.c - vendor/luajit/src/lib_aux.c - vendor/luajit/src/lib_base.c - vendor/luajit/src/lib_math.c - vendor/luajit/src/lib_bit.c - vendor/luajit/src/lib_string.c - vendor/luajit/src/lib_table.c - vendor/luajit/src/lib_io.c - vendor/luajit/src/lib_os.c - vendor/luajit/src/lib_package.c - vendor/luajit/src/lib_debug.c - vendor/luajit/src/lib_jit.c - vendor/luajit/src/lib_ffi.c - vendor/luajit/src/lib_init.c -) -if(MSVC) - target_sources(luajit PRIVATE vendor/luajit/src/lj_vm.obj) -else(MSVC) - target_sources(luajit PRIVATE vendor/luajit/src/lj_vm.s) - set_property(SOURCE vendor/luajit/src/lj_vm.s PROPERTY LANGUAGE C) - target_link_libraries(luajit dl) -endif(MSVC) -target_include_directories(luajit PRIVATE vendor/luajit/src/gen) -add_dependencies(luajit luajit-buildvm) -target_compile_definitions(luajit PRIVATE LUAJIT_ENABLE_LUA52COMPAT) - -add_library(resrc STATIC - src/libresrc/bitmap.cpp - src/libresrc/default_config.cpp - src/libresrc/libresrc.cpp -) -add_dependencies(resrc luajit-minilua) - -add_library(csri STATIC - vendor/csri/lib/list.c - vendor/csri/lib/wrap.c - vendor/csri/subhelp/logging.c -) -target_include_directories(csri PRIVATE "vendor/csri/include") -IF (WIN32) - target_include_directories(csri PRIVATE "vendor/csri/lib/win32") - target_sources(csri PRIVATE vendor/csri/lib/win32/enumerate.c) -ELSE() - target_include_directories(csri PRIVATE "vendor/csri/lib/posix") - target_sources(csri PRIVATE vendor/csri/lib/posix/enumerate.c) -ENDIF() - -add_executable(Aegisub WIN32 - src/command/app.cpp - src/command/audio.cpp - src/command/automation.cpp - src/command/command.cpp - src/command/edit.cpp - src/command/grid.cpp - src/command/help.cpp - src/command/keyframe.cpp - src/command/recent.cpp - src/command/subtitle.cpp - src/command/time.cpp - src/command/timecode.cpp - src/command/tool.cpp - src/command/video.cpp - src/command/vis_tool.cpp - src/dialog_about.cpp - src/dialog_align.cpp - src/dialog_attachments.cpp - src/dialog_automation.cpp - src/dialog_autosave.cpp - src/dialog_colorpicker.cpp - src/dialog_detached_video.cpp - src/dialog_dummy_video.cpp - src/dialog_export.cpp - src/dialog_export_ebu3264.cpp - src/dialog_fonts_collector.cpp - src/dialog_jumpto.cpp - src/dialog_kara_timing_copy.cpp - src/dialog_log.cpp - src/dialog_paste_over.cpp - src/dialog_progress.cpp - src/dialog_properties.cpp - src/dialog_resample.cpp - src/dialog_search_replace.cpp - src/dialog_selected_choices.cpp - src/dialog_selection.cpp - src/dialog_shift_times.cpp - src/dialog_spellchecker.cpp - src/dialog_style_editor.cpp - src/dialog_style_manager.cpp - src/dialog_styling_assistant.cpp - src/dialog_text_import.cpp - src/dialog_timing_processor.cpp - src/dialog_translation.cpp - src/dialog_version_check.cpp - src/dialog_video_details.cpp - src/dialog_video_properties.cpp - src/subtitle_format.cpp - src/subtitle_format_ass.cpp - src/subtitle_format_ebu3264.cpp - src/subtitle_format_encore.cpp - src/subtitle_format_microdvd.cpp - src/subtitle_format_mkv.cpp - src/subtitle_format_srt.cpp - src/subtitle_format_ssa.cpp - src/subtitle_format_transtation.cpp - src/subtitle_format_ttxt.cpp - src/subtitle_format_txt.cpp - src/visual_tool.cpp - src/visual_tool_clip.cpp - src/visual_tool_cross.cpp - src/visual_tool_drag.cpp - src/visual_tool_rotatexy.cpp - src/visual_tool_rotatez.cpp - src/visual_tool_scale.cpp - src/visual_tool_vector_clip.cpp - src/MatroskaParser.c - src/aegisublocale.cpp - src/ass_attachment.cpp - src/ass_dialogue.cpp - src/ass_entry.cpp - src/ass_export_filter.cpp - src/ass_exporter.cpp - src/ass_file.cpp - src/ass_karaoke.cpp - src/ass_override.cpp - src/ass_parser.cpp - src/ass_style.cpp - src/ass_style_storage.cpp - src/async_video_provider.cpp - src/audio_box.cpp - src/audio_colorscheme.cpp - src/audio_controller.cpp - src/audio_display.cpp - src/audio_karaoke.cpp - src/audio_marker.cpp - src/audio_player.cpp - src/audio_provider_factory.cpp - src/audio_renderer.cpp - src/audio_renderer_spectrum.cpp - src/audio_renderer_waveform.cpp - src/audio_timing_dialogue.cpp - src/audio_timing_karaoke.cpp - src/auto4_base.cpp - src/auto4_lua.cpp - src/auto4_lua_assfile.cpp - src/auto4_lua_dialog.cpp - src/auto4_lua_progresssink.cpp - src/base_grid.cpp - src/charset_detect.cpp - src/colorspace.cpp - src/colour_button.cpp - src/compat.cpp - src/context.cpp - src/export_fixstyle.cpp - src/export_framerate.cpp - src/fft.cpp - src/font_file_lister.cpp - src/frame_main.cpp - src/gl_text.cpp - src/gl_wrap.cpp - src/grid_column.cpp - src/help_button.cpp - src/hotkey.cpp - src/hotkey_data_view_model.cpp - src/image_position_picker.cpp - src/initial_line_state.cpp - src/main.cpp - src/menu.cpp - src/mkv_wrap.cpp - src/pen.cpp - src/persist_location.cpp - src/preferences.cpp - src/preferences_base.cpp - src/project.cpp - src/resolution_resampler.cpp - src/search_replace_engine.cpp - src/selection_controller.cpp - src/spellchecker.cpp - src/spline.cpp - src/spline_curve.cpp - src/string_codec.cpp - src/subs_controller.cpp - src/subs_edit_box.cpp - src/subs_edit_ctrl.cpp - src/subs_preview.cpp - src/subtitles_provider.cpp - src/subtitles_provider_libass.cpp - src/text_file_reader.cpp - src/text_file_writer.cpp - src/text_selection_controller.cpp - src/thesaurus.cpp - src/timeedit_ctrl.cpp - src/toggle_bitmap.cpp - src/toolbar.cpp - src/tooltip_manager.cpp - src/utils.cpp - src/validators.cpp - src/vector2d.cpp - src/version.cpp - src/video_box.cpp - src/video_controller.cpp - src/video_display.cpp - src/video_frame.cpp - src/video_out_gl.cpp - src/video_provider_cache.cpp - src/video_provider_dummy.cpp - src/video_provider_manager.cpp - src/video_provider_yuv4mpeg.cpp - src/video_slider.cpp - src/visual_feature.cpp -) -target_link_libraries(Aegisub ${CMAKE_DL_LIBS} libaegisub luabins luajit resrc csri) - -if (MSVC) - set_target_properties(libaegisub PROPERTIES COMPILE_FLAGS "/Yu${PROJECT_SOURCE_DIR}/libaegisub/lagi_pre.h" COMPILE_FLAGS "/FI${PROJECT_SOURCE_DIR}/libaegisub/lagi_pre.h") -else(MSVC) - target_compile_options(libaegisub PRIVATE -include "${PROJECT_SOURCE_DIR}/libaegisub/lagi_pre.h") -endif(MSVC) - -set_property( - SOURCE libaegisub/unix/path.cpp - PROPERTY COMPILE_DEFINITIONS - P_DATA="${CMAKE_INSTALL_PREFIX}/share/aegisub/" -) - -if (MSVC) - add_definitions("-DNOMINMAX -MP -DINITGUID") - set_target_properties(Aegisub PROPERTIES COMPILE_FLAGS "/Yu${PROJECT_SOURCE_DIR}/src/agi_pre.h" COMPILE_FLAGS "/FI${PROJECT_SOURCE_DIR}/src/agi_pre.h") - target_link_libraries (Aegisub Usp10) - #target_sources(Aegisub PRIVATE src/res/res.rc src/res/strings.rc src/crash_writer_minidump.cpp) - target_sources(Aegisub PRIVATE src/res/res.rc src/res/strings.rc src/crash_writer.cpp src/dpi_aware.manifest) - set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Aegisub) -else(MSVC) - target_sources(Aegisub PRIVATE src/crash_writer.cpp) - target_compile_options(Aegisub PRIVATE -include "${PROJECT_SOURCE_DIR}/src/agi_pre.h") -endif(MSVC) - -if (WIN32) - target_sources(Aegisub PRIVATE src/font_file_lister_gdi.cpp) -else (WIN32) - find_package(Fontconfig REQUIRED) - target_link_libraries (Aegisub ${Fontconfig_LIBRARIES}) - target_sources(Aegisub PRIVATE src/font_file_lister_fontconfig.cpp) - set_property(SOURCE src/font_file_lister_fontconfig.cpp PROPERTY INCLUDE_DIRECTORIES "${Fontconfig_INCLUDE_DIRS}") -endif (WIN32) - -find_package(ass REQUIRED) -include_directories(${ass_INCLUDE_DIRS}) -target_link_libraries (Aegisub ${ass_LIBRARIES}) - -find_package(Boost REQUIRED chrono filesystem locale regex system thread) -include_directories(${Boost_INCLUDE_DIRS}) -target_link_directories(Aegisub PRIVATE ${Boost_LIBRARY_DIRS}) -target_link_libraries(Aegisub ${Boost_LIBRARIES}) - -find_package(OpenGL REQUIRED) -include_directories(${OPENGL_INCLUDE_DIR}) -target_link_libraries (Aegisub ${OPENGL_LIBRARIES}) - -find_package(Hunspell REQUIRED) -include_directories(${HUNSPELL_INCLUDE_DIR}) -target_link_libraries (Aegisub ${HUNSPELL_LIBRARIES}) -add_definitions("-DWITH_HUNSPELL") -target_sources(Aegisub PRIVATE src/spellchecker_hunspell.cpp) - -find_package(Iconv REQUIRED) -include_directories(${Iconv_INCLUDE_DIRS}) -target_link_libraries (Aegisub ${Iconv_LIBRARIES}) -add_definitions("-DHAVE_ICONV") -if (NOT Iconv_IS_BUILT_IN) -set_property( - SOURCE libaegisub/common/charset_conv.cpp - PROPERTY COMPILE_DEFINITIONS AGI_ICONV_CONST -) -endif (NOT Iconv_IS_BUILT_IN) - -find_package(ICU REQUIRED uc dt in) -include_directories(${ICU_INCLUDE_DIRS}) -target_link_libraries (Aegisub ${ICU_LIBRARIES}) - -find_package(wxWidgets REQUIRED adv base core gl stc xml) -include(${wxWidgets_USE_FILE}) -target_link_libraries(Aegisub ${wxWidgets_LIBRARIES}) - -find_package(ZLIB REQUIRED) -include_directories(${ZLIB_INCLUDE_DIRS}) -target_link_libraries (Aegisub ${ZLIB_LIBRARIES}) - -find_package(ALSA) -if (ALSA_FOUND) - include_directories(${ALSA_INCLUDE_DIRS}) - target_link_libraries (Aegisub ${ALSA_LIBRARIES}) - add_definitions("-DWITH_ALSA") - target_sources(Aegisub PRIVATE src/audio_player_alsa.cpp) -endif(ALSA_FOUND) - -# target_compile_definitions(Aegisub PRIVATE "WITH_AVISYNTH") -# target_sources(Aegisub PRIVATE src/audio_provider_avs.cpp src/avisynth_wrap.cpp src/video_provider_avs.cpp) - -target_compile_definitions(Aegisub PRIVATE "WITH_CSRI") -target_sources(Aegisub PRIVATE src/subtitles_provider_csri.cpp) -set_property(SOURCE src/subtitles_provider_csri.cpp PROPERTY INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/vendor/csri/include") - -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) - -find_package(FFMS2) -if (FFMS2_FOUND) - include_directories(${FFMS2_INCLUDE_DIRS}) - target_link_libraries (Aegisub ${FFMS2_LIBRARIES}) - add_definitions("-DWITH_FFMS2") - target_sources(Aegisub PRIVATE src/audio_provider_ffmpegsource.cpp src/ffmpegsource_common.cpp src/video_provider_ffmpegsource.cpp) -endif(FFMS2_FOUND) - -find_package(FFTW) -if (FFTW_FOUND) - include_directories(${FFTW_INCLUDES}) - target_link_libraries (Aegisub ${FFTW_LIBRARIES}) - add_definitions("-DWITH_FFTW3") -endif(FFTW_FOUND) - -#ifdef WITH_LIBPULSE -#add_definitions("-DWITH_LIBPULSE") -#target_sources(Aegisub PRIVATE src/audio_player_pulse.cpp) - -find_package(OpenAL) -if (OPENAL_FOUND) - include_directories(${OPENAL_INCLUDE_DIR}) - target_link_libraries (Aegisub ${OPENAL_LIBRARY}) - add_definitions("-DWITH_OPENAL") - target_sources(Aegisub PRIVATE src/audio_player_openal.cpp) -endif(OPENAL_FOUND) - -#ifdef WITH_OSS -#ifdef WITH_PORTAUDIO -#ifdef WITH_STARTUPLOG - -find_package(uchardet) -if (uchardet_FOUND) - include_directories(${uchardet_INCLUDE_DIRS}) - target_link_libraries (Aegisub ${uchardet_LIBRARIES}) - add_definitions("-DWITH_UCHARDET") -endif(uchardet_FOUND) - -#ifdef WITH_UPDATE_CHECKER diff --git a/libaegisub/include/libaegisub/audio/provider.h b/libaegisub/include/libaegisub/audio/provider.h index fc2e0ddb3..741979b12 100644 --- a/libaegisub/include/libaegisub/audio/provider.h +++ b/libaegisub/include/libaegisub/audio/provider.h @@ -21,6 +21,7 @@ #include #include +#include namespace agi { class AudioProvider { @@ -94,4 +95,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 64cc9d3ca..01f5e3935 100644 --- a/meson.build +++ b/meson.build @@ -238,7 +238,6 @@ if host_machine.system() == 'windows' and get_option('avisynth').enabled() endif if host_machine.system() == 'windows' - if not get_option('directsound').disabled() dsound_dep = cc.find_library('dsound', required: get_option('directsound')) winmm_dep = cc.find_library('winmm', required: get_option('directsound')) @@ -277,8 +276,6 @@ if host_machine.system() == 'windows' # 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_dsound2.cpp b/src/audio_player_dsound2.cpp index e8a144eb2..ad9149803 100644 --- a/src/audio_player_dsound2.cpp +++ b/src/audio_player_dsound2.cpp @@ -469,7 +469,6 @@ stop_playback: invert_volume = DSBVOLUME_MAX; else if (invert_volume < DSBVOLUME_MIN / 2) invert_volume = DSBVOLUME_MIN / 2; - LOG_I("DS2") << "Earrape vlume: " <SetVolume(invert_volume); } // Change volume diff --git a/src/audio_provider_ffmpegsource.cpp b/src/audio_provider_ffmpegsource.cpp index 1c109220f..46e1412f5 100644 --- a/src/audio_provider_ffmpegsource.cpp +++ b/src/audio_provider_ffmpegsource.cpp @@ -126,10 +126,8 @@ void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) { // reindex if the error handling mode has changed FFMS_IndexErrorHandling ErrorHandling = GetErrorHandlingMode(); -#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (2 << 8) | 0) if (Index && FFMS_GetErrorHandling(Index) != ErrorHandling) Index = nullptr; -#endif // moment of truth if (!Index) { @@ -167,23 +165,22 @@ void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) { throw agi::AudioProviderError("unknown or unsupported sample format"); } -#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (4 << 8) | 0) if (OPT_GET("Provider/Audio/FFmpegSource/Downmix")->GetBool()) { - if (channels > 1 || bytes_per_sample != 2 || float_samples) { + if (channels > 2 || bytes_per_sample != 2 || float_samples) { std::unique_ptr opt(FFMS_CreateResampleOptions(AudioSource), FFMS_DestroyResampleOptions); - opt->ChannelLayout = FFMS_CH_FRONT_CENTER; + if (channels > 2) + opt->ChannelLayout = FFMS_CH_FRONT_LEFT | FFMS_CH_FRONT_RIGHT; opt->SampleFormat = FFMS_FMT_S16; // Might fail if FFMS2 wasn't built with libavresample if (!FFMS_SetOutputFormatA(AudioSource, opt.get(), nullptr)) { - channels = 1; + channels = channels > 2 ? 2 : channels; bytes_per_sample = 2; float_samples = false; } } } -#endif } } @@ -192,4 +189,4 @@ std::unique_ptr CreateFFmpegSourceAudioProvider(agi::fs::pat return agi::make_unique(file, br); } -#endif /* WITH_FFMS2 */ \ No newline at end of file +#endif /* WITH_FFMS2 */