From 25762b0c291317798e4988bdfdda1a9250bcb5aa Mon Sep 17 00:00:00 2001 From: Karutoh Date: Tue, 3 Sep 2024 05:37:23 -0700 Subject: [PATCH] Backup. --- .gitea/workflows/BuildRelease.yaml | 2 +- CMakeLists.txt | 8 +- include/ehs/Str.h | 3 + include/ehs/io/audio/AudioDevice_ALSA.h | 32 +- include/ehs/io/audio/BaseAudioDevice.h | 18 +- src/io/audio/AudioDevice_ALSA.cpp | 404 +++++++----------------- src/io/audio/BaseAudioDevice.cpp | 5 + 7 files changed, 163 insertions(+), 309 deletions(-) diff --git a/.gitea/workflows/BuildRelease.yaml b/.gitea/workflows/BuildRelease.yaml index 0c8f31e..3f807f7 100644 --- a/.gitea/workflows/BuildRelease.yaml +++ b/.gitea/workflows/BuildRelease.yaml @@ -19,7 +19,7 @@ jobs: - name: Building/Compiling/Installing Project run: | cd ${{ gitea.workspace }} - cmake -A x64 -DCMAKE_BUILD_TYPE=Release --preset=default . + cmake -A x64 -DCMAKE_BUILD_TYPE=Release . cd build cmake --build . --config Release cmake --install . diff --git a/CMakeLists.txt b/CMakeLists.txt index 59ccc15..e67bfc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,6 +252,12 @@ target_include_directories(EHS_Dyn PUBLIC ${PROJECT_SOURCE_DIR}/include) if (IS_OS_LINUX) set(CMAKE_INSTALL_PREFIX "${USER_HOME_DIRECTORY}/.local") install(TARGETS EHS_Dyn LIBRARY DESTINATION "bin") + + find_package(PkgConfig REQUIRED) + pkg_check_modules(PIPEWIRE REQUIRED libpipewire-0.3) + + target_include_directories(EHS_Stc PRIVATE ${PIPEWIRE_INCLUDE_DIRS}) + target_include_directories(EHS_Dyn PRIVATE ${PIPEWIRE_INCLUDE_DIRS}) elseif (IS_OS_WINDOWS) set(CMAKE_INSTALL_PREFIX "${USER_HOME_DIRECTORY}/EHS") install(TARGETS EHS_Dyn LIBRARY DESTINATION "lib") @@ -295,6 +301,6 @@ elseif (IS_OS_LINUX) target_link_libraries(StrToHash xcb xcb-cursor xcb-xfixes xcb-xinput) endif () - target_link_libraries(EHS_Dyn z asound) + target_link_libraries(EHS_Dyn z asound pipewire-0.3) target_link_libraries(StrToHash z asound EHS_Stc) endif () \ No newline at end of file diff --git a/include/ehs/Str.h b/include/ehs/Str.h index 63759b0..23b4a9e 100644 --- a/include/ehs/Str.h +++ b/include/ehs/Str.h @@ -1879,6 +1879,9 @@ namespace ehs /// @returns The character count. static N Len(const T* const str) { + if (!str) + return 0; + N count = 0; while (str[count]) ++count; diff --git a/include/ehs/io/audio/AudioDevice_ALSA.h b/include/ehs/io/audio/AudioDevice_ALSA.h index c600100..0f4d0cd 100644 --- a/include/ehs/io/audio/AudioDevice_ALSA.h +++ b/include/ehs/io/audio/AudioDevice_ALSA.h @@ -3,14 +3,32 @@ #include "ehs/EHS.h" #include "BaseAudioDevice.h" -#include +#include +#include +#include +#include +#include +#include namespace ehs { class EHS_LIB_IO AudioDevice : public BaseAudioDevice { private: - snd_pcm_t* hdl; + static Array devices; + static AudioDevice defOut; + static AudioDevice defIn; + + UInt_32 id; + Str_8 name; + pw_loop *loop; + pw_context *context; + pw_core *core; + pw_stream *stream; + + static void RegistryEventGlobal(void *user_data, UInt_32 id, UInt_32 permissions, const char *type, UInt_32 version, const spa_dict *props); + + static void RegistryEventGlobalRemove(void *user_data, UInt_32 id); public: ~AudioDevice() override; @@ -31,16 +49,12 @@ namespace ehs void CloseStream() override; - UInt_64 GetAvailFrames() const override; - - Byte* Map(UInt_64* offset, UInt_64* frames) override; - - void UnMap(const UInt_64 offset, const UInt_64 frames) override; + UInt_64 SendStream(void *data, UInt_64 size) override; bool IsValid() const override; - static AudioDevice GetDefault(const AudioDeviceType type); + static AudioDevice GetDefault(AudioDeviceType type); - static Array Get(const AudioDeviceType type, const AudioDeviceState state); + static Array Get(AudioDeviceType type, AudioDeviceState state); }; } diff --git a/include/ehs/io/audio/BaseAudioDevice.h b/include/ehs/io/audio/BaseAudioDevice.h index 17d143a..92fccd0 100644 --- a/include/ehs/io/audio/BaseAudioDevice.h +++ b/include/ehs/io/audio/BaseAudioDevice.h @@ -51,15 +51,17 @@ namespace ehs virtual void CloseStream(); + virtual UInt_64 SendStream(void *data, UInt_64 size); + virtual UInt_64 GetAvailFrames() const; virtual Byte* Map(UInt_64* offset, UInt_64* frames); - virtual void UnMap(const UInt_64 offset, const UInt_64 frames); + virtual void UnMap(UInt_64 offset, UInt_64 frames); AudioDeviceType GetType() const; - void SetDataType(const DataType newDataType); + void SetDataType(DataType newDataType); DataType GetDataType() const; @@ -67,21 +69,21 @@ namespace ehs UInt_16 GetBitDepth() const; - void SetSampleRate(const UInt_32 newSampleRate); + void SetSampleRate(UInt_32 newSampleRate); UInt_32 GetSampleRate() const; - void SetChannels(const UInt_32 newChannels); + void SetChannels(UInt_32 newChannels); UInt_16 GetChannels() const; UInt_32 GetFrameSize() const; - void SetPeriod(const UInt_32 newPeriod); + void SetPeriod(UInt_32 newPeriod); UInt_32 GetPeriod() const; - void SetLatency(const UInt_32 newLatency); + void SetLatency(UInt_32 newLatency); UInt_32 GetLatency() const; @@ -94,12 +96,12 @@ namespace ehs /// Retrieves the default audio input/output device. /// @param [in] type The audio device type to retrieve. /// @param [out] device The default audio device. - static BaseAudioDevice GetDefault(const AudioDeviceType type); + static BaseAudioDevice GetDefault(AudioDeviceType type); /// Retrieves a list of audio input/output devices. /// @param [in] type The audio device type to retrieve. /// @param [in] state The audio device state to retrieve. /// @param [out] devices The list of audio devices. - static Array Get(const AudioDeviceType type, const AudioDeviceState state); + static Array Get(AudioDeviceType type, AudioDeviceState state); }; } \ No newline at end of file diff --git a/src/io/audio/AudioDevice_ALSA.cpp b/src/io/audio/AudioDevice_ALSA.cpp index fcdadb8..eec3f11 100644 --- a/src/io/audio/AudioDevice_ALSA.cpp +++ b/src/io/audio/AudioDevice_ALSA.cpp @@ -1,32 +1,61 @@ #include "ehs/io/audio/AudioDevice_ALSA.h" #include "ehs/EHS.h" #include "ehs/Log.h" -#include "ehs/io/Console.h" +#include "ehs/system/Semaphore.h" + +#include +#include +#include + +#include "ehs/io/audio/AudioDevice.h" namespace ehs { + Array AudioDevice::devices; + + void AudioDevice::RegistryEventGlobal(void *user_data, UInt_32 id, UInt_32 permissions, const char *type, UInt_32 version, const spa_dict *props) + { + const Str_8 mediaClass = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); + + if (mediaClass.Size() && (mediaClass == "Audio/Sink" || mediaClass == "Audio/Source")) + { + AudioDevice device; + + if (mediaClass == "Audio/Sink") + device.type = AudioDeviceType::OUTPUT; + else if (mediaClass == "Audio/Source") + device.type = AudioDeviceType::INPUT; + + device.id = id; + device.name = spa_dict_lookup(props, PW_KEY_NODE_NAME); + + EHS_LOG_INT(LogType::INFO, 1, "\nDevice Name: " + device.name + "\nId: " + Str_8::FromNum(device.id)); + + const pw_metadata *metadata = pw_core_get_metadata(); + + devices.Push(device); + } + } + + void AudioDevice::RegistryEventGlobalRemove(void *user_data, UInt_32 id) + { + } + AudioDevice::~AudioDevice() { - if (!hdl) - return; - - snd_pcm_drain(hdl); - snd_pcm_close(hdl); } AudioDevice::AudioDevice() - : hdl(nullptr) { } AudioDevice::AudioDevice(AudioDevice&& device) noexcept - : BaseAudioDevice(device), hdl(device.hdl) + : BaseAudioDevice(device) { - device.hdl = nullptr; } AudioDevice::AudioDevice(const AudioDevice& device) - : BaseAudioDevice(device), hdl(nullptr) + : BaseAudioDevice(device) { } @@ -37,10 +66,6 @@ namespace ehs BaseAudioDevice::operator=(device); - hdl = device.hdl; - - device.hdl = nullptr; - return *this; } @@ -51,8 +76,6 @@ namespace ehs BaseAudioDevice::operator=(device); - hdl = nullptr; - return *this; } @@ -60,10 +83,6 @@ namespace ehs { if (!IsValid()) return; - - snd_pcm_drain(hdl); - snd_pcm_close(hdl); - hdl = nullptr; } void AudioDevice::OpenStream() @@ -71,198 +90,50 @@ namespace ehs if (streaming) return; - snd_pcm_hw_params_t* params; - snd_pcm_hw_params_alloca(¶ms); + pw_init(nullptr, nullptr); - snd_pcm_hw_params_any(hdl, params); + loop = pw_loop_new(nullptr); + context = pw_context_new(loop, nullptr, 0); + core = pw_context_connect(context, nullptr, 0); - if (snd_pcm_hw_params_set_access(hdl, params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) - { - EHS_LOG_INT(LogType::ERR, 0, "Failed to set access."); - return; - } - - if (bitDepth) - { - snd_pcm_format_t format; - switch (dataType) - { - case DataType::FLOAT: - { - format = SND_PCM_FORMAT_FLOAT; - break; - } - case DataType::SINT_32: - { - format = SND_PCM_FORMAT_S32; - break; - } - case DataType::SINT_24: - { - format = SND_PCM_FORMAT_S24; - break; - } - case DataType::SINT_16: - { - format = SND_PCM_FORMAT_S16; - break; - } - case DataType::SINT_8: - { - format = SND_PCM_FORMAT_S8; - break; - } - default: - { - EHS_LOG_INT(LogType::ERR, 0, "Invalid data type."); - return; - } + spa_audio_info_raw info = { + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 2, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_SL, + SPA_AUDIO_CHANNEL_SR, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR } + }; - snd_pcm_hw_params_set_format(hdl, params, format); - } + pw_properties *props = pw_properties_new( + PW_KEY_MEDIA_TYPE, "Audio", + PW_KEY_MEDIA_CATEGORY, "Playback", + PW_KEY_MEDIA_ROLE, "Game", + nullptr + ); - if (channels) - { - if (snd_pcm_hw_params_set_channels_near(hdl, params, &channels) < 0) - { - EHS_LOG_INT(LogType::ERR, 1, "Failed to set channels."); - return; - } - } + spa_pod_builder b = {}; + Byte buffer[1024]; - if (sampleRate) - { - if (snd_pcm_hw_params_set_rate_near(hdl, params, &sampleRate, nullptr) < 0) - { - EHS_LOG_INT(LogType::ERR, 2, "Failed to set sample rate."); - return; - } - } + spa_pod_builder_init(&b, buffer, sizeof(buffer)); - if (snd_pcm_hw_params_set_period_time_near(hdl, params, &period, nullptr) < 0) - { - EHS_LOG_INT(LogType::ERR, 3, "Failed to set period."); - return; - } + const spa_pod *pod[] = {spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)}; - if (snd_pcm_hw_params_set_buffer_time_near(hdl, params, &latency, nullptr) < 0) - { - EHS_LOG_INT(LogType::ERR, 4, "Failed to set latency."); - return; - } - - if (snd_pcm_hw_params(hdl, params) < 0) - { - EHS_LOG_INT(LogType::ERR, 5, "Failed to apply hardware parameters."); - return; - } - - if (!bitDepth) - { - snd_pcm_format_t format; - if (snd_pcm_hw_params_get_format(params, &format) < 0) - { - EHS_LOG_INT(LogType::ERR, 6, "Failed to retrieve audio device properties."); - return; - } - - switch (format) - { - case SND_PCM_FORMAT_FLOAT: - { - dataType = DataType::FLOAT; - bitDepth = 32; - break; - } - case SND_PCM_FORMAT_S32: - { - dataType = DataType::SINT_32; - bitDepth = 32; - break; - } - case SND_PCM_FORMAT_S24: - { - dataType = DataType::SINT_24; - bitDepth = 24; - break; - } - case SND_PCM_FORMAT_S16: - { - dataType = DataType::SINT_16; - bitDepth = 16; - break; - } - case SND_PCM_FORMAT_S8: - { - dataType = DataType::SINT_8; - bitDepth = 8; - break; - } - default: - { - EHS_LOG_INT(LogType::ERR, 7, "Format unsupported."); - break; - } - } - } - - if (!channels) - { - if (snd_pcm_hw_params_get_channels(params, &channels) < 0) - { - EHS_LOG_INT(LogType::ERR, 8, "Failed to retrieve channel count."); - return; - } - } - - if (!sampleRate) - { - int dir; - if (snd_pcm_hw_params_get_rate(params, &sampleRate, &dir) < 0) - { - EHS_LOG_INT(LogType::ERR, 9, "Failed to retrieve sample rate."); - return; - } - } - - if (snd_pcm_hw_params_get_buffer_size(params, &maxFrames) < 0) - { - EHS_LOG_INT(LogType::ERR, 10, "Failed to retrieve buffer size."); - } - - snd_pcm_sw_params_t* swParams = nullptr; - snd_pcm_sw_params_alloca(&swParams); - - if (snd_pcm_sw_params_current(hdl, swParams) < 0) - { - EHS_LOG_INT(LogType::ERR, 11, "Failed to retrieve software parameters."); - return; - } - - if (snd_pcm_sw_params_set_silence_threshold(hdl, swParams, maxFrames) < 0) - { - EHS_LOG_INT(LogType::ERR, 12, "Failed to set silence threshold."); - return; - } - - if (snd_pcm_sw_params_set_silence_size(hdl, swParams, maxFrames) < 0) - { - EHS_LOG_INT(LogType::ERR, 12, "Failed to set silence size."); - return; - } - - if (snd_pcm_sw_params(hdl, swParams) < 0) - { - EHS_LOG_INT(LogType::ERR, 13, "Failed to set software parameters."); - return; - } - - if (snd_pcm_prepare(hdl) < 0) - { - EHS_LOG_INT(LogType::ERR, 14, "Failed to prepare audio stream."); - return; - } + pw_stream_connect( + stream, + PW_DIRECTION_OUTPUT, + PW_ID_ANY, + (pw_stream_flags)(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS), + pod, + 1 + ); streaming = true; } @@ -272,117 +143,70 @@ namespace ehs if (!streaming) return; - snd_pcm_drain(hdl); + pw_stream_destroy(stream); + pw_core_disconnect(core); + pw_context_destroy(context); + pw_loop_destroy(loop); + pw_deinit(); streaming = false; } - UInt_64 AudioDevice::GetAvailFrames() const + UInt_64 AudioDevice::SendStream(void *data, const UInt_64 size) { - snd_pcm_state_t state = snd_pcm_state(hdl); - if (state == SND_PCM_STATE_XRUN) - { - EHS_LOG_INT(LogType::WARN, 0, "Buffer overrun/underrun occurred."); - if (snd_pcm_recover(hdl, -EPIPE, 0) < 0) - { - EHS_LOG_INT(LogType::ERR, 1, "Failed to recover from buffer overrun/underrun."); - return 0; - } - return GetAvailFrames(); - } - else if (state == SND_PCM_STATE_PREPARED) - { - if (snd_pcm_start(hdl) < 0) - { - EHS_LOG_INT(LogType::ERR, 2, "Failed to start audio stream."); - return 0; - } + while (pw_loop_iterate(loop, 0)); - return GetAvailFrames(); - } + if (pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING) + return 0; - snd_pcm_sframes_t frames = snd_pcm_avail_update(hdl); - if (frames < 0) - { - if (frames == -EPIPE) - { - EHS_LOG_INT(LogType::WARN, 3, "Buffer overrun/underrun occurred."); - if (snd_pcm_recover(hdl, -EPIPE, 1) < 0) - { - EHS_LOG_INT(LogType::ERR, 4, "Failed to recover from buffer overrun/underrun."); - return 0; - } + pw_buffer *buf = pw_stream_dequeue_buffer(stream); + if (!buf) + return 0; - return GetAvailFrames(); - } - else - { - EHS_LOG_INT(LogType::ERR, 5, "Failed to retrieve available frames with error #" + Str_8::FromNum(frames) + "."); - return 0; - } - } + UInt_64 out = 0; - return frames; - } + spa_data &result = buf->buffer->datas[0]; - Byte* AudioDevice::Map(UInt_64* offset, UInt_64* frames) - { - const snd_pcm_channel_area_t* areas; - if (snd_pcm_mmap_begin(hdl, &areas, offset, frames) < 0) - { - EHS_LOG_INT(LogType::ERR, 0, "Failed to map audio buffer."); - return nullptr; - } + if (size > result.maxsize) + out = result.maxsize; + else + out = size; - return (Byte*)areas->addr + areas->first / 8; - } + Util::Copy(result.data, data, out); - void AudioDevice::UnMap(const UInt_64 offset, const UInt_64 frames) - { - snd_pcm_sframes_t committed = snd_pcm_mmap_commit(hdl, offset, frames); - if (committed < 0) - { - EHS_LOG_INT(LogType::ERR, 0, "Failed to commit mapped audio buffer."); - return; - } + pw_stream_queue_buffer(stream, buf); + + return out; } bool AudioDevice::IsValid() const { - return hdl; + return true; } AudioDevice AudioDevice::GetDefault(const AudioDeviceType type) { - AudioDevice result; + pw_init(nullptr, nullptr); - snd_pcm_stream_t rType; - if (type == AudioDeviceType::INPUT) - { - rType = SND_PCM_STREAM_CAPTURE; - } - else if (type == AudioDeviceType::OUTPUT) - { - rType = SND_PCM_STREAM_PLAYBACK; - } - else - { - EHS_LOG_INT(LogType::ERR, 1, "Wrong value for the audio device type."); - return {}; - } + pw_loop *loop = pw_loop_new(nullptr); + pw_context *context = pw_context_new(loop, nullptr, 0); + pw_core *core = pw_context_connect(context, nullptr, 0); + pw_registry *registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, 0); + spa_hook listener = {}; - int err = snd_pcm_open(&result.hdl, "default", rType, SND_PCM_NONBLOCK); - if (err < 0) - { - EHS_LOG_INT(LogType::ERR, 2, "Failed to retrieve default audio device with error #" + Str_8::FromNum(err) + "."); - return {}; - } + constexpr pw_registry_events events = { + .version = PW_VERSION_REGISTRY_EVENTS, + .global = RegistryEventGlobal, + .global_remove = RegistryEventGlobalRemove + }; - result.type = AudioDeviceType::OUTPUT; + pw_registry_add_listener(registry, &listener, &events, nullptr); - EHS_LOG_SUCCESS(); + while (pw_loop_iterate(loop, 10)); - return result; + constexpr pw_metadata_events + + return {}; } Array AudioDevice::Get(const AudioDeviceType type, const AudioDeviceState state) diff --git a/src/io/audio/BaseAudioDevice.cpp b/src/io/audio/BaseAudioDevice.cpp index adc36ff..c551324 100644 --- a/src/io/audio/BaseAudioDevice.cpp +++ b/src/io/audio/BaseAudioDevice.cpp @@ -44,6 +44,11 @@ namespace ehs { } + UInt_64 BaseAudioDevice::SendStream(void *data, UInt_64 size) + { + return 0; + } + UInt_64 BaseAudioDevice::GetAvailFrames() const { return 0;