aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/backends
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2019-07-28 18:56:04 -0700
committerChris Robinson <[email protected]>2019-07-28 18:56:04 -0700
commitcb3e96e75640730b9391f0d2d922eecd9ee2ce79 (patch)
tree23520551bddb2a80354e44da47f54201fdc084f0 /Alc/backends
parent93e60919c8f387c36c267ca9faa1ac653254aea6 (diff)
Rename Alc to alc
Diffstat (limited to 'Alc/backends')
-rw-r--r--Alc/backends/alsa.cpp1288
-rw-r--r--Alc/backends/alsa.h19
-rw-r--r--Alc/backends/base.cpp58
-rw-r--r--Alc/backends/base.h78
-rw-r--r--Alc/backends/coreaudio.cpp709
-rw-r--r--Alc/backends/coreaudio.h19
-rw-r--r--Alc/backends/dsound.cpp938
-rw-r--r--Alc/backends/dsound.h19
-rw-r--r--Alc/backends/jack.cpp562
-rw-r--r--Alc/backends/jack.h19
-rw-r--r--Alc/backends/loopback.cpp80
-rw-r--r--Alc/backends/loopback.h19
-rw-r--r--Alc/backends/null.cpp184
-rw-r--r--Alc/backends/null.h19
-rw-r--r--Alc/backends/opensl.cpp936
-rw-r--r--Alc/backends/opensl.h19
-rw-r--r--Alc/backends/oss.cpp751
-rw-r--r--Alc/backends/oss.h19
-rw-r--r--Alc/backends/portaudio.cpp463
-rw-r--r--Alc/backends/portaudio.h19
-rw-r--r--Alc/backends/pulseaudio.cpp1532
-rw-r--r--Alc/backends/pulseaudio.h19
-rw-r--r--Alc/backends/qsa.cpp953
-rw-r--r--Alc/backends/qsa.h19
-rw-r--r--Alc/backends/sdl2.cpp223
-rw-r--r--Alc/backends/sdl2.h19
-rw-r--r--Alc/backends/sndio.cpp495
-rw-r--r--Alc/backends/sndio.h19
-rw-r--r--Alc/backends/solaris.cpp302
-rw-r--r--Alc/backends/solaris.h19
-rw-r--r--Alc/backends/wasapi.cpp1763
-rw-r--r--Alc/backends/wasapi.h19
-rw-r--r--Alc/backends/wave.cpp402
-rw-r--r--Alc/backends/wave.h19
-rw-r--r--Alc/backends/winmm.cpp640
-rw-r--r--Alc/backends/winmm.h19
36 files changed, 0 insertions, 12680 deletions
diff --git a/Alc/backends/alsa.cpp b/Alc/backends/alsa.cpp
deleted file mode 100644
index c133df68..00000000
--- a/Alc/backends/alsa.cpp
+++ /dev/null
@@ -1,1288 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/alsa.h"
-
-#include <algorithm>
-#include <atomic>
-#include <cassert>
-#include <cerrno>
-#include <chrono>
-#include <cstring>
-#include <exception>
-#include <functional>
-#include <memory>
-#include <string>
-#include <thread>
-#include <utility>
-
-#include "AL/al.h"
-
-#include "albyte.h"
-#include "alcmain.h"
-#include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "aloptional.h"
-#include "alu.h"
-#include "compat.h"
-#include "logging.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "vector.h"
-
-#include <alsa/asoundlib.h>
-
-
-namespace {
-
-constexpr ALCchar alsaDevice[] = "ALSA Default";
-
-
-#ifdef HAVE_DYNLOAD
-#define ALSA_FUNCS(MAGIC) \
- MAGIC(snd_strerror); \
- MAGIC(snd_pcm_open); \
- MAGIC(snd_pcm_close); \
- MAGIC(snd_pcm_nonblock); \
- MAGIC(snd_pcm_frames_to_bytes); \
- MAGIC(snd_pcm_bytes_to_frames); \
- MAGIC(snd_pcm_hw_params_malloc); \
- MAGIC(snd_pcm_hw_params_free); \
- MAGIC(snd_pcm_hw_params_any); \
- MAGIC(snd_pcm_hw_params_current); \
- MAGIC(snd_pcm_hw_params_set_access); \
- MAGIC(snd_pcm_hw_params_set_format); \
- MAGIC(snd_pcm_hw_params_set_channels); \
- MAGIC(snd_pcm_hw_params_set_periods_near); \
- MAGIC(snd_pcm_hw_params_set_rate_near); \
- MAGIC(snd_pcm_hw_params_set_rate); \
- MAGIC(snd_pcm_hw_params_set_rate_resample); \
- MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
- MAGIC(snd_pcm_hw_params_set_period_time_near); \
- MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
- MAGIC(snd_pcm_hw_params_set_period_size_near); \
- MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
- MAGIC(snd_pcm_hw_params_get_buffer_time_min); \
- MAGIC(snd_pcm_hw_params_get_buffer_time_max); \
- MAGIC(snd_pcm_hw_params_get_period_time_min); \
- MAGIC(snd_pcm_hw_params_get_period_time_max); \
- MAGIC(snd_pcm_hw_params_get_buffer_size); \
- MAGIC(snd_pcm_hw_params_get_period_size); \
- MAGIC(snd_pcm_hw_params_get_access); \
- MAGIC(snd_pcm_hw_params_get_periods); \
- MAGIC(snd_pcm_hw_params_test_format); \
- MAGIC(snd_pcm_hw_params_test_channels); \
- MAGIC(snd_pcm_hw_params); \
- MAGIC(snd_pcm_sw_params_malloc); \
- MAGIC(snd_pcm_sw_params_current); \
- MAGIC(snd_pcm_sw_params_set_avail_min); \
- MAGIC(snd_pcm_sw_params_set_stop_threshold); \
- MAGIC(snd_pcm_sw_params); \
- MAGIC(snd_pcm_sw_params_free); \
- MAGIC(snd_pcm_prepare); \
- MAGIC(snd_pcm_start); \
- MAGIC(snd_pcm_resume); \
- MAGIC(snd_pcm_reset); \
- MAGIC(snd_pcm_wait); \
- MAGIC(snd_pcm_delay); \
- MAGIC(snd_pcm_state); \
- MAGIC(snd_pcm_avail_update); \
- MAGIC(snd_pcm_areas_silence); \
- MAGIC(snd_pcm_mmap_begin); \
- MAGIC(snd_pcm_mmap_commit); \
- MAGIC(snd_pcm_readi); \
- MAGIC(snd_pcm_writei); \
- MAGIC(snd_pcm_drain); \
- MAGIC(snd_pcm_drop); \
- MAGIC(snd_pcm_recover); \
- MAGIC(snd_pcm_info_malloc); \
- MAGIC(snd_pcm_info_free); \
- MAGIC(snd_pcm_info_set_device); \
- MAGIC(snd_pcm_info_set_subdevice); \
- MAGIC(snd_pcm_info_set_stream); \
- MAGIC(snd_pcm_info_get_name); \
- MAGIC(snd_ctl_pcm_next_device); \
- MAGIC(snd_ctl_pcm_info); \
- MAGIC(snd_ctl_open); \
- MAGIC(snd_ctl_close); \
- MAGIC(snd_ctl_card_info_malloc); \
- MAGIC(snd_ctl_card_info_free); \
- MAGIC(snd_ctl_card_info); \
- MAGIC(snd_ctl_card_info_get_name); \
- MAGIC(snd_ctl_card_info_get_id); \
- MAGIC(snd_card_next); \
- MAGIC(snd_config_update_free_global)
-
-static void *alsa_handle;
-#define MAKE_FUNC(f) decltype(f) * p##f
-ALSA_FUNCS(MAKE_FUNC);
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define snd_strerror psnd_strerror
-#define snd_pcm_open psnd_pcm_open
-#define snd_pcm_close psnd_pcm_close
-#define snd_pcm_nonblock psnd_pcm_nonblock
-#define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
-#define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
-#define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
-#define snd_pcm_hw_params_free psnd_pcm_hw_params_free
-#define snd_pcm_hw_params_any psnd_pcm_hw_params_any
-#define snd_pcm_hw_params_current psnd_pcm_hw_params_current
-#define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
-#define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
-#define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
-#define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
-#define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
-#define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
-#define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
-#define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
-#define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
-#define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
-#define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
-#define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
-#define snd_pcm_hw_params_get_buffer_time_min psnd_pcm_hw_params_get_buffer_time_min
-#define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max
-#define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min
-#define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max
-#define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
-#define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
-#define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
-#define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
-#define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
-#define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
-#define snd_pcm_hw_params psnd_pcm_hw_params
-#define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
-#define snd_pcm_sw_params_current psnd_pcm_sw_params_current
-#define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
-#define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
-#define snd_pcm_sw_params psnd_pcm_sw_params
-#define snd_pcm_sw_params_free psnd_pcm_sw_params_free
-#define snd_pcm_prepare psnd_pcm_prepare
-#define snd_pcm_start psnd_pcm_start
-#define snd_pcm_resume psnd_pcm_resume
-#define snd_pcm_reset psnd_pcm_reset
-#define snd_pcm_wait psnd_pcm_wait
-#define snd_pcm_delay psnd_pcm_delay
-#define snd_pcm_state psnd_pcm_state
-#define snd_pcm_avail_update psnd_pcm_avail_update
-#define snd_pcm_areas_silence psnd_pcm_areas_silence
-#define snd_pcm_mmap_begin psnd_pcm_mmap_begin
-#define snd_pcm_mmap_commit psnd_pcm_mmap_commit
-#define snd_pcm_readi psnd_pcm_readi
-#define snd_pcm_writei psnd_pcm_writei
-#define snd_pcm_drain psnd_pcm_drain
-#define snd_pcm_drop psnd_pcm_drop
-#define snd_pcm_recover psnd_pcm_recover
-#define snd_pcm_info_malloc psnd_pcm_info_malloc
-#define snd_pcm_info_free psnd_pcm_info_free
-#define snd_pcm_info_set_device psnd_pcm_info_set_device
-#define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
-#define snd_pcm_info_set_stream psnd_pcm_info_set_stream
-#define snd_pcm_info_get_name psnd_pcm_info_get_name
-#define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
-#define snd_ctl_pcm_info psnd_ctl_pcm_info
-#define snd_ctl_open psnd_ctl_open
-#define snd_ctl_close psnd_ctl_close
-#define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
-#define snd_ctl_card_info_free psnd_ctl_card_info_free
-#define snd_ctl_card_info psnd_ctl_card_info
-#define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
-#define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
-#define snd_card_next psnd_card_next
-#define snd_config_update_free_global psnd_config_update_free_global
-#endif
-#endif
-
-
-struct DevMap {
- std::string name;
- std::string device_name;
-};
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-const char *prefix_name(snd_pcm_stream_t stream)
-{
- assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
- return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
-}
-
-al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
-{
- al::vector<DevMap> devlist;
-
- snd_ctl_card_info_t *info;
- snd_ctl_card_info_malloc(&info);
- snd_pcm_info_t *pcminfo;
- snd_pcm_info_malloc(&pcminfo);
-
- devlist.emplace_back(DevMap{alsaDevice,
- GetConfigValue(nullptr, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture",
- "default")});
-
- if(stream == SND_PCM_STREAM_PLAYBACK)
- {
- const char *customdevs;
- const char *next{GetConfigValue(nullptr, "alsa", "custom-devices", "")};
- while((customdevs=next) != nullptr && customdevs[0])
- {
- next = strchr(customdevs, ';');
- const char *sep{strchr(customdevs, '=')};
- if(!sep)
- {
- std::string spec{next ? std::string(customdevs, next++) : std::string(customdevs)};
- ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
- continue;
- }
-
- const char *oldsep{sep++};
- devlist.emplace_back(DevMap{std::string(customdevs, oldsep),
- next ? std::string(sep, next++) : std::string(sep)});
- const auto &entry = devlist.back();
- TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
- }
- }
-
- const std::string main_prefix{
- ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")};
-
- int card{-1};
- int err{snd_card_next(&card)};
- for(;err >= 0 && card >= 0;err = snd_card_next(&card))
- {
- std::string name{"hw:" + std::to_string(card)};
-
- snd_ctl_t *handle;
- if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0)
- {
- ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
- continue;
- }
- if((err=snd_ctl_card_info(handle, info)) < 0)
- {
- ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
- snd_ctl_close(handle);
- continue;
- }
-
- const char *cardname{snd_ctl_card_info_get_name(info)};
- const char *cardid{snd_ctl_card_info_get_id(info)};
- name = prefix_name(stream);
- name += '-';
- name += cardid;
- const std::string card_prefix{
- ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)};
-
- int dev{-1};
- while(1)
- {
- if(snd_ctl_pcm_next_device(handle, &dev) < 0)
- ERR("snd_ctl_pcm_next_device failed\n");
- if(dev < 0) break;
-
- snd_pcm_info_set_device(pcminfo, dev);
- snd_pcm_info_set_subdevice(pcminfo, 0);
- snd_pcm_info_set_stream(pcminfo, stream);
- if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0)
- {
- if(err != -ENOENT)
- ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
- continue;
- }
-
- /* "prefix-cardid-dev" */
- name = prefix_name(stream);
- name += '-';
- name += cardid;
- name += '-';
- name += std::to_string(dev);
- const std::string device_prefix{
- ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)};
-
- /* "CardName, PcmName (CARD=cardid,DEV=dev)" */
- name = cardname;
- name += ", ";
- name += snd_pcm_info_get_name(pcminfo);
- name += " (CARD=";
- name += cardid;
- name += ",DEV=";
- name += std::to_string(dev);
- name += ')';
-
- /* "devprefixCARD=cardid,DEV=dev" */
- std::string device{device_prefix};
- device += "CARD=";
- device += cardid;
- device += ",DEV=";
- device += std::to_string(dev);
-
- devlist.emplace_back(DevMap{std::move(name), std::move(device)});
- const auto &entry = devlist.back();
- TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
- }
- snd_ctl_close(handle);
- }
- if(err < 0)
- ERR("snd_card_next failed: %s\n", snd_strerror(err));
-
- snd_pcm_info_free(pcminfo);
- snd_ctl_card_info_free(info);
-
- return devlist;
-}
-
-
-int verify_state(snd_pcm_t *handle)
-{
- snd_pcm_state_t state{snd_pcm_state(handle)};
-
- int err;
- switch(state)
- {
- case SND_PCM_STATE_OPEN:
- case SND_PCM_STATE_SETUP:
- case SND_PCM_STATE_PREPARED:
- case SND_PCM_STATE_RUNNING:
- case SND_PCM_STATE_DRAINING:
- case SND_PCM_STATE_PAUSED:
- /* All Okay */
- break;
-
- case SND_PCM_STATE_XRUN:
- if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
- return err;
- break;
- case SND_PCM_STATE_SUSPENDED:
- if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
- return err;
- break;
- case SND_PCM_STATE_DISCONNECTED:
- return -ENODEV;
- }
-
- return state;
-}
-
-
-struct AlsaPlayback final : public BackendBase {
- AlsaPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~AlsaPlayback() override;
-
- int mixerProc();
- int mixerNoMMapProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- ClockLatency getClockLatency() override;
-
- snd_pcm_t *mPcmHandle{nullptr};
-
- al::vector<char> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(AlsaPlayback)
-};
-
-AlsaPlayback::~AlsaPlayback()
-{
- if(mPcmHandle)
- snd_pcm_close(mPcmHandle);
- mPcmHandle = nullptr;
-}
-
-
-int AlsaPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
- const snd_pcm_uframes_t num_updates{mDevice->BufferSize / update_size};
- while(!mKillNow.load(std::memory_order_acquire))
- {
- int state{verify_state(mPcmHandle)};
- if(state < 0)
- {
- ERR("Invalid state detected: %s\n", snd_strerror(state));
- aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state));
- break;
- }
-
- snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)};
- if(avail < 0)
- {
- ERR("available update failed: %s\n", snd_strerror(avail));
- continue;
- }
-
- if(static_cast<snd_pcm_uframes_t>(avail) > update_size*(num_updates+1))
- {
- WARN("available samples exceeds the buffer size\n");
- snd_pcm_reset(mPcmHandle);
- continue;
- }
-
- // make sure there's frames to process
- if(static_cast<snd_pcm_uframes_t>(avail) < update_size)
- {
- if(state != SND_PCM_STATE_RUNNING)
- {
- int err{snd_pcm_start(mPcmHandle)};
- if(err < 0)
- {
- ERR("start failed: %s\n", snd_strerror(err));
- continue;
- }
- }
- if(snd_pcm_wait(mPcmHandle, 1000) == 0)
- ERR("Wait timeout... buffer size too low?\n");
- continue;
- }
- avail -= avail%update_size;
-
- // it is possible that contiguous areas are smaller, thus we use a loop
- lock();
- while(avail > 0)
- {
- snd_pcm_uframes_t frames{static_cast<snd_pcm_uframes_t>(avail)};
-
- const snd_pcm_channel_area_t *areas{};
- snd_pcm_uframes_t offset{};
- int err{snd_pcm_mmap_begin(mPcmHandle, &areas, &offset, &frames)};
- if(err < 0)
- {
- ERR("mmap begin error: %s\n", snd_strerror(err));
- break;
- }
-
- char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
- aluMixData(mDevice, WritePtr, frames);
-
- snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)};
- if(commitres < 0 || (commitres-frames) != 0)
- {
- ERR("mmap commit error: %s\n",
- snd_strerror(commitres >= 0 ? -EPIPE : commitres));
- break;
- }
-
- avail -= frames;
- }
- unlock();
- }
-
- return 0;
-}
-
-int AlsaPlayback::mixerNoMMapProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
- const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
- while(!mKillNow.load(std::memory_order_acquire))
- {
- int state{verify_state(mPcmHandle)};
- if(state < 0)
- {
- ERR("Invalid state detected: %s\n", snd_strerror(state));
- aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state));
- break;
- }
-
- snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)};
- if(avail < 0)
- {
- ERR("available update failed: %s\n", snd_strerror(avail));
- continue;
- }
-
- if(static_cast<snd_pcm_uframes_t>(avail) > buffer_size)
- {
- WARN("available samples exceeds the buffer size\n");
- snd_pcm_reset(mPcmHandle);
- continue;
- }
-
- if(static_cast<snd_pcm_uframes_t>(avail) < update_size)
- {
- if(state != SND_PCM_STATE_RUNNING)
- {
- int err{snd_pcm_start(mPcmHandle)};
- if(err < 0)
- {
- ERR("start failed: %s\n", snd_strerror(err));
- continue;
- }
- }
- if(snd_pcm_wait(mPcmHandle, 1000) == 0)
- ERR("Wait timeout... buffer size too low?\n");
- continue;
- }
-
- lock();
- char *WritePtr{mBuffer.data()};
- avail = snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
- aluMixData(mDevice, WritePtr, avail);
- while(avail > 0)
- {
- snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr, avail)};
- switch(ret)
- {
- case -EAGAIN:
- continue;
-#if ESTRPIPE != EPIPE
- case -ESTRPIPE:
-#endif
- case -EPIPE:
- case -EINTR:
- ret = snd_pcm_recover(mPcmHandle, ret, 1);
- if(ret < 0)
- avail = 0;
- break;
- default:
- if(ret >= 0)
- {
- WritePtr += snd_pcm_frames_to_bytes(mPcmHandle, ret);
- avail -= ret;
- }
- break;
- }
- if(ret < 0)
- {
- ret = snd_pcm_prepare(mPcmHandle);
- if(ret < 0) break;
- }
- }
- unlock();
- }
-
- return 0;
-}
-
-
-ALCenum AlsaPlayback::open(const ALCchar *name)
-{
- const char *driver{};
- if(name)
- {
- if(PlaybackDevices.empty())
- PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
-
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- return ALC_INVALID_VALUE;
- driver = iter->device_name.c_str();
- }
- else
- {
- name = alsaDevice;
- driver = GetConfigValue(nullptr, "alsa", "device", "default");
- }
-
- TRACE("Opening device \"%s\"\n", driver);
- int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
- if(err < 0)
- {
- ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err));
- return ALC_OUT_OF_MEMORY;
- }
-
- /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
- snd_config_update_free_global();
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean AlsaPlayback::reset()
-{
- snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- format = SND_PCM_FORMAT_S8;
- break;
- case DevFmtUByte:
- format = SND_PCM_FORMAT_U8;
- break;
- case DevFmtShort:
- format = SND_PCM_FORMAT_S16;
- break;
- case DevFmtUShort:
- format = SND_PCM_FORMAT_U16;
- break;
- case DevFmtInt:
- format = SND_PCM_FORMAT_S32;
- break;
- case DevFmtUInt:
- format = SND_PCM_FORMAT_U32;
- break;
- case DevFmtFloat:
- format = SND_PCM_FORMAT_FLOAT;
- break;
- }
-
- bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", 1)};
- ALuint periodLen{static_cast<ALuint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
- ALuint bufferLen{static_cast<ALuint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
- ALuint rate{mDevice->Frequency};
-
- snd_pcm_uframes_t periodSizeInFrames{};
- snd_pcm_uframes_t bufferSizeInFrames{};
- snd_pcm_sw_params_t *sp{};
- snd_pcm_hw_params_t *hp{};
- snd_pcm_access_t access{};
- const char *funcerr{};
- int err{};
-
- snd_pcm_hw_params_malloc(&hp);
-#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_any(mPcmHandle, hp));
- /* set interleaved access */
- if(!allowmmap || snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
- {
- /* No mmap */
- CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
- }
- /* test and set format (implicitly sets sample bits) */
- if(snd_pcm_hw_params_test_format(mPcmHandle, hp, format) < 0)
- {
- static const struct {
- snd_pcm_format_t format;
- DevFmtType fmttype;
- } formatlist[] = {
- { SND_PCM_FORMAT_FLOAT, DevFmtFloat },
- { SND_PCM_FORMAT_S32, DevFmtInt },
- { SND_PCM_FORMAT_U32, DevFmtUInt },
- { SND_PCM_FORMAT_S16, DevFmtShort },
- { SND_PCM_FORMAT_U16, DevFmtUShort },
- { SND_PCM_FORMAT_S8, DevFmtByte },
- { SND_PCM_FORMAT_U8, DevFmtUByte },
- };
-
- for(const auto &fmt : formatlist)
- {
- format = fmt.format;
- if(snd_pcm_hw_params_test_format(mPcmHandle, hp, format) >= 0)
- {
- mDevice->FmtType = fmt.fmttype;
- break;
- }
- }
- }
- CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp, format));
- /* test and set channels (implicitly sets frame bits) */
- if(snd_pcm_hw_params_test_channels(mPcmHandle, hp, mDevice->channelsFromFmt()) < 0)
- {
- static const DevFmtChannels channellist[] = {
- DevFmtStereo,
- DevFmtQuad,
- DevFmtX51,
- DevFmtX71,
- DevFmtMono,
- };
-
- for(const auto &chan : channellist)
- {
- if(snd_pcm_hw_params_test_channels(mPcmHandle, hp, ChannelsFromDevFmt(chan, 0)) >= 0)
- {
- mDevice->FmtChans = chan;
- mDevice->mAmbiOrder = 0;
- break;
- }
- }
- }
- CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp, mDevice->channelsFromFmt()));
- /* set rate (implicitly constrains period/buffer parameters) */
- if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", 0) ||
- !mDevice->Flags.get<FrequencyRequest>())
- {
- if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp, 0) < 0)
- ERR("Failed to disable ALSA resampler\n");
- }
- else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp, 1) < 0)
- ERR("Failed to enable ALSA resampler\n");
- CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp, &rate, nullptr));
- /* set period time (implicitly constrains period/buffer parameters) */
- if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp, &periodLen, nullptr)) < 0)
- ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
- /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
- if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp, &bufferLen, nullptr)) < 0)
- ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
- /* install and prepare hardware configuration */
- CHECK(snd_pcm_hw_params(mPcmHandle, hp));
-
- /* retrieve configuration info */
- CHECK(snd_pcm_hw_params_get_access(hp, &access));
- CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, nullptr));
- CHECK(snd_pcm_hw_params_get_buffer_size(hp, &bufferSizeInFrames));
- snd_pcm_hw_params_free(hp);
- hp = nullptr;
-
- snd_pcm_sw_params_malloc(&sp);
- CHECK(snd_pcm_sw_params_current(mPcmHandle, sp));
- CHECK(snd_pcm_sw_params_set_avail_min(mPcmHandle, sp, periodSizeInFrames));
- CHECK(snd_pcm_sw_params_set_stop_threshold(mPcmHandle, sp, bufferSizeInFrames));
- CHECK(snd_pcm_sw_params(mPcmHandle, sp));
-#undef CHECK
- snd_pcm_sw_params_free(sp);
- sp = nullptr;
-
- mDevice->BufferSize = bufferSizeInFrames;
- mDevice->UpdateSize = periodSizeInFrames;
- mDevice->Frequency = rate;
-
- SetDefaultChannelOrder(mDevice);
-
- return ALC_TRUE;
-
-error:
- ERR("%s failed: %s\n", funcerr, snd_strerror(err));
- if(hp) snd_pcm_hw_params_free(hp);
- if(sp) snd_pcm_sw_params_free(sp);
- return ALC_FALSE;
-}
-
-ALCboolean AlsaPlayback::start()
-{
- snd_pcm_hw_params_t *hp{};
- snd_pcm_access_t access;
- const char *funcerr;
- int err;
-
- snd_pcm_hw_params_malloc(&hp);
-#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_current(mPcmHandle, hp));
- /* retrieve configuration info */
- CHECK(snd_pcm_hw_params_get_access(hp, &access));
-#undef CHECK
- if(0)
- {
- error:
- ERR("%s failed: %s\n", funcerr, snd_strerror(err));
- if(hp) snd_pcm_hw_params_free(hp);
- return ALC_FALSE;
- }
- snd_pcm_hw_params_free(hp);
- hp = nullptr;
-
- int (AlsaPlayback::*thread_func)(){};
- if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
- {
- mBuffer.resize(snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize));
- thread_func = &AlsaPlayback::mixerNoMMapProc;
- }
- else
- {
- err = snd_pcm_prepare(mPcmHandle);
- if(err < 0)
- {
- ERR("snd_pcm_prepare(data->mPcmHandle) failed: %s\n", snd_strerror(err));
- return ALC_FALSE;
- }
- thread_func = &AlsaPlayback::mixerProc;
- }
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(thread_func), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- mBuffer.clear();
- return ALC_FALSE;
-}
-
-void AlsaPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- mBuffer.clear();
-}
-
-ClockLatency AlsaPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- snd_pcm_sframes_t delay{};
- int err{snd_pcm_delay(mPcmHandle, &delay)};
- if(err < 0)
- {
- ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- delay = 0;
- }
- ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-struct AlsaCapture final : public BackendBase {
- AlsaCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~AlsaCapture() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
- ClockLatency getClockLatency() override;
-
- snd_pcm_t *mPcmHandle{nullptr};
-
- al::vector<char> mBuffer;
-
- bool mDoCapture{false};
- RingBufferPtr mRing{nullptr};
-
- snd_pcm_sframes_t mLastAvail{0};
-
- DEF_NEWDEL(AlsaCapture)
-};
-
-AlsaCapture::~AlsaCapture()
-{
- if(mPcmHandle)
- snd_pcm_close(mPcmHandle);
- mPcmHandle = nullptr;
-}
-
-
-ALCenum AlsaCapture::open(const ALCchar *name)
-{
- const char *driver{};
- if(name)
- {
- if(CaptureDevices.empty())
- CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
-
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- return ALC_INVALID_VALUE;
- driver = iter->device_name.c_str();
- }
- else
- {
- name = alsaDevice;
- driver = GetConfigValue(nullptr, "alsa", "capture", "default");
- }
-
- TRACE("Opening device \"%s\"\n", driver);
- int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
- if(err < 0)
- {
- ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
- return ALC_INVALID_VALUE;
- }
-
- /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
- snd_config_update_free_global();
-
- snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- format = SND_PCM_FORMAT_S8;
- break;
- case DevFmtUByte:
- format = SND_PCM_FORMAT_U8;
- break;
- case DevFmtShort:
- format = SND_PCM_FORMAT_S16;
- break;
- case DevFmtUShort:
- format = SND_PCM_FORMAT_U16;
- break;
- case DevFmtInt:
- format = SND_PCM_FORMAT_S32;
- break;
- case DevFmtUInt:
- format = SND_PCM_FORMAT_U32;
- break;
- case DevFmtFloat:
- format = SND_PCM_FORMAT_FLOAT;
- break;
- }
-
- snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)};
- snd_pcm_uframes_t periodSizeInFrames{minu(bufferSizeInFrames, 25*mDevice->Frequency/1000)};
-
- bool needring{false};
- const char *funcerr{};
- snd_pcm_hw_params_t *hp{};
- snd_pcm_hw_params_malloc(&hp);
-#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
- CHECK(snd_pcm_hw_params_any(mPcmHandle, hp));
- /* set interleaved access */
- CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
- /* set format (implicitly sets sample bits) */
- CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp, format));
- /* set channels (implicitly sets frame bits) */
- CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp, mDevice->channelsFromFmt()));
- /* set rate (implicitly constrains period/buffer parameters) */
- CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp, mDevice->Frequency, 0));
- /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
- if(snd_pcm_hw_params_set_buffer_size_min(mPcmHandle, hp, &bufferSizeInFrames) < 0)
- {
- TRACE("Buffer too large, using intermediate ring buffer\n");
- needring = true;
- CHECK(snd_pcm_hw_params_set_buffer_size_near(mPcmHandle, hp, &bufferSizeInFrames));
- }
- /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
- CHECK(snd_pcm_hw_params_set_period_size_near(mPcmHandle, hp, &periodSizeInFrames, nullptr));
- /* install and prepare hardware configuration */
- CHECK(snd_pcm_hw_params(mPcmHandle, hp));
- /* retrieve configuration info */
- CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, nullptr));
-#undef CHECK
- snd_pcm_hw_params_free(hp);
- hp = nullptr;
-
- if(needring)
- {
- mRing = CreateRingBuffer(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false);
- if(!mRing)
- {
- ERR("ring buffer create failed\n");
- goto error2;
- }
- }
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-
-error:
- ERR("%s failed: %s\n", funcerr, snd_strerror(err));
- if(hp) snd_pcm_hw_params_free(hp);
-
-error2:
- mRing = nullptr;
- snd_pcm_close(mPcmHandle);
- mPcmHandle = nullptr;
-
- return ALC_INVALID_VALUE;
-}
-
-
-ALCboolean AlsaCapture::start()
-{
- int err{snd_pcm_prepare(mPcmHandle)};
- if(err < 0)
- ERR("prepare failed: %s\n", snd_strerror(err));
- else
- {
- err = snd_pcm_start(mPcmHandle);
- if(err < 0)
- ERR("start failed: %s\n", snd_strerror(err));
- }
- if(err < 0)
- {
- aluHandleDisconnect(mDevice, "Capture state failure: %s", snd_strerror(err));
- return ALC_FALSE;
- }
-
- mDoCapture = true;
- return ALC_TRUE;
-}
-
-void AlsaCapture::stop()
-{
- /* OpenAL requires access to unread audio after stopping, but ALSA's
- * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
- * available now so it'll be available later after the drop.
- */
- ALCuint avail{availableSamples()};
- if(!mRing && avail > 0)
- {
- /* The ring buffer implicitly captures when checking availability.
- * Direct access needs to explicitly capture it into temp storage. */
- al::vector<char> temp(snd_pcm_frames_to_bytes(mPcmHandle, avail));
- captureSamples(temp.data(), avail);
- mBuffer = std::move(temp);
- }
- int err{snd_pcm_drop(mPcmHandle)};
- if(err < 0)
- ERR("drop failed: %s\n", snd_strerror(err));
- mDoCapture = false;
-}
-
-ALCenum AlsaCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- if(mRing)
- {
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
- }
-
- mLastAvail -= samples;
- while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
- {
- snd_pcm_sframes_t amt{0};
-
- if(!mBuffer.empty())
- {
- /* First get any data stored from the last stop */
- amt = snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
- if(static_cast<snd_pcm_uframes_t>(amt) > samples) amt = samples;
-
- amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
- memcpy(buffer, mBuffer.data(), amt);
-
- mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
- amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
- }
- else if(mDoCapture)
- amt = snd_pcm_readi(mPcmHandle, buffer, samples);
- if(amt < 0)
- {
- ERR("read error: %s\n", snd_strerror(amt));
-
- if(amt == -EAGAIN)
- continue;
- if((amt=snd_pcm_recover(mPcmHandle, amt, 1)) >= 0)
- {
- amt = snd_pcm_start(mPcmHandle);
- if(amt >= 0)
- amt = snd_pcm_avail_update(mPcmHandle);
- }
- if(amt < 0)
- {
- ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(amt));
- break;
- }
- /* If the amount available is less than what's asked, we lost it
- * during recovery. So just give silence instead. */
- if(static_cast<snd_pcm_uframes_t>(amt) < samples)
- break;
- continue;
- }
-
- buffer = static_cast<ALbyte*>(buffer) + amt;
- samples -= amt;
- }
- if(samples > 0)
- memset(buffer, ((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0),
- snd_pcm_frames_to_bytes(mPcmHandle, samples));
-
- return ALC_NO_ERROR;
-}
-
-ALCuint AlsaCapture::availableSamples()
-{
- snd_pcm_sframes_t avail{0};
- if(mDevice->Connected.load(std::memory_order_acquire) && mDoCapture)
- avail = snd_pcm_avail_update(mPcmHandle);
- if(avail < 0)
- {
- ERR("avail update failed: %s\n", snd_strerror(avail));
-
- if((avail=snd_pcm_recover(mPcmHandle, avail, 1)) >= 0)
- {
- if(mDoCapture)
- avail = snd_pcm_start(mPcmHandle);
- if(avail >= 0)
- avail = snd_pcm_avail_update(mPcmHandle);
- }
- if(avail < 0)
- {
- ERR("restore error: %s\n", snd_strerror(avail));
- aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(avail));
- }
- }
-
- if(!mRing)
- {
- if(avail < 0) avail = 0;
- avail += snd_pcm_bytes_to_frames(mPcmHandle, mBuffer.size());
- if(avail > mLastAvail) mLastAvail = avail;
- return mLastAvail;
- }
-
- while(avail > 0)
- {
- auto vec = mRing->getWriteVector();
- if(vec.first.len == 0) break;
-
- snd_pcm_sframes_t amt{std::min<snd_pcm_sframes_t>(vec.first.len, avail)};
- amt = snd_pcm_readi(mPcmHandle, vec.first.buf, amt);
- if(amt < 0)
- {
- ERR("read error: %s\n", snd_strerror(amt));
-
- if(amt == -EAGAIN)
- continue;
- if((amt=snd_pcm_recover(mPcmHandle, amt, 1)) >= 0)
- {
- if(mDoCapture)
- amt = snd_pcm_start(mPcmHandle);
- if(amt >= 0)
- amt = snd_pcm_avail_update(mPcmHandle);
- }
- if(amt < 0)
- {
- ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(mDevice, "Capture recovery failure: %s", snd_strerror(amt));
- break;
- }
- avail = amt;
- continue;
- }
-
- mRing->writeAdvance(amt);
- avail -= amt;
- }
-
- return mRing->readSpace();
-}
-
-ClockLatency AlsaCapture::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- snd_pcm_sframes_t delay{};
- int err{snd_pcm_delay(mPcmHandle, &delay)};
- if(err < 0)
- {
- ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- delay = 0;
- }
- ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-} // namespace
-
-
-bool AlsaBackendFactory::init()
-{
- bool error{false};
-
-#ifdef HAVE_DYNLOAD
- if(!alsa_handle)
- {
- std::string missing_funcs;
-
- alsa_handle = LoadLib("libasound.so.2");
- if(!alsa_handle)
- {
- WARN("Failed to load %s\n", "libasound.so.2");
- return ALC_FALSE;
- }
-
- error = ALC_FALSE;
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
- if(p##f == nullptr) { \
- error = true; \
- missing_funcs += "\n" #f; \
- } \
-} while(0)
- ALSA_FUNCS(LOAD_FUNC);
-#undef LOAD_FUNC
-
- if(error)
- {
- WARN("Missing expected functions:%s\n", missing_funcs.c_str());
- CloseLib(alsa_handle);
- alsa_handle = nullptr;
- }
- }
-#endif
-
- return !error;
-}
-
-bool AlsaBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void AlsaBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
- switch(type)
- {
- case DevProbe::Playback:
- PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr AlsaBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new AlsaPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new AlsaCapture{device}};
- return nullptr;
-}
-
-BackendFactory &AlsaBackendFactory::getFactory()
-{
- static AlsaBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/alsa.h b/Alc/backends/alsa.h
deleted file mode 100644
index fb9de006..00000000
--- a/Alc/backends/alsa.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_ALSA_H
-#define BACKENDS_ALSA_H
-
-#include "backends/base.h"
-
-struct AlsaBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_ALSA_H */
diff --git a/Alc/backends/base.cpp b/Alc/backends/base.cpp
deleted file mode 100644
index a7d47c6d..00000000
--- a/Alc/backends/base.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-
-#include "config.h"
-
-#include <cstdlib>
-
-#include <thread>
-
-#include "alcmain.h"
-#include "alu.h"
-
-#include "backends/base.h"
-
-
-ClockLatency GetClockLatency(ALCdevice *device)
-{
- BackendBase *backend{device->Backend.get()};
- ClockLatency ret{backend->getClockLatency()};
- ret.Latency += device->FixedLatency;
- return ret;
-}
-
-
-/* BackendBase method implementations. */
-BackendBase::BackendBase(ALCdevice *device) noexcept : mDevice{device}
-{ }
-
-BackendBase::~BackendBase() = default;
-
-ALCboolean BackendBase::reset()
-{ return ALC_FALSE; }
-
-ALCenum BackendBase::captureSamples(void*, ALCuint)
-{ return ALC_INVALID_DEVICE; }
-
-ALCuint BackendBase::availableSamples()
-{ return 0; }
-
-ClockLatency BackendBase::getClockLatency()
-{
- ClockLatency ret;
-
- ALuint refcount;
- do {
- while(((refcount=mDevice->MixCount.load(std::memory_order_acquire))&1))
- std::this_thread::yield();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- std::atomic_thread_fence(std::memory_order_acquire);
- } while(refcount != mDevice->MixCount.load(std::memory_order_relaxed));
-
- /* NOTE: The device will generally have about all but one periods filled at
- * any given time during playback. Without a more accurate measurement from
- * the output, this is an okay approximation.
- */
- ret.Latency = std::chrono::seconds{maxi(mDevice->BufferSize-mDevice->UpdateSize, 0)};
- ret.Latency /= mDevice->Frequency;
-
- return ret;
-}
diff --git a/Alc/backends/base.h b/Alc/backends/base.h
deleted file mode 100644
index 437e31d9..00000000
--- a/Alc/backends/base.h
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef ALC_BACKENDS_BASE_H
-#define ALC_BACKENDS_BASE_H
-
-#include <memory>
-#include <chrono>
-#include <string>
-#include <mutex>
-
-#include "alcmain.h"
-
-
-struct ClockLatency {
- std::chrono::nanoseconds ClockTime;
- std::chrono::nanoseconds Latency;
-};
-
-/* Helper to get the current clock time from the device's ClockBase, and
- * SamplesDone converted from the sample rate.
- */
-inline std::chrono::nanoseconds GetDeviceClockTime(ALCdevice *device)
-{
- using std::chrono::seconds;
- using std::chrono::nanoseconds;
-
- auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency;
- return device->ClockBase + ns;
-}
-
-ClockLatency GetClockLatency(ALCdevice *device);
-
-struct BackendBase {
- virtual ALCenum open(const ALCchar *name) = 0;
-
- virtual ALCboolean reset();
- virtual ALCboolean start() = 0;
- virtual void stop() = 0;
-
- virtual ALCenum captureSamples(void *buffer, ALCuint samples);
- virtual ALCuint availableSamples();
-
- virtual ClockLatency getClockLatency();
-
- virtual void lock() { mMutex.lock(); }
- virtual void unlock() { mMutex.unlock(); }
-
- ALCdevice *mDevice;
-
- std::recursive_mutex mMutex;
-
- BackendBase(ALCdevice *device) noexcept;
- virtual ~BackendBase();
-};
-using BackendPtr = std::unique_ptr<BackendBase>;
-using BackendUniqueLock = std::unique_lock<BackendBase>;
-using BackendLockGuard = std::lock_guard<BackendBase>;
-
-enum class BackendType {
- Playback,
- Capture
-};
-
-enum class DevProbe {
- Playback,
- Capture
-};
-
-
-struct BackendFactory {
- virtual bool init() = 0;
-
- virtual bool querySupport(BackendType type) = 0;
-
- virtual void probe(DevProbe type, std::string *outnames) = 0;
-
- virtual BackendPtr createBackend(ALCdevice *device, BackendType type) = 0;
-};
-
-#endif /* ALC_BACKENDS_BASE_H */
diff --git a/Alc/backends/coreaudio.cpp b/Alc/backends/coreaudio.cpp
deleted file mode 100644
index b4b46382..00000000
--- a/Alc/backends/coreaudio.cpp
+++ /dev/null
@@ -1,709 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/coreaudio.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "converter.h"
-#include "backends/base.h"
-
-#include <unistd.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-
-namespace {
-
-static const ALCchar ca_device[] = "CoreAudio Default";
-
-
-struct CoreAudioPlayback final : public BackendBase {
- CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~CoreAudioPlayback() override;
-
- static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList *ioData);
- OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList *ioData);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- AudioUnit mAudioUnit;
-
- ALuint mFrameSize{0u};
- AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
-
- DEF_NEWDEL(CoreAudioPlayback)
-};
-
-CoreAudioPlayback::~CoreAudioPlayback()
-{
- AudioUnitUninitialize(mAudioUnit);
- AudioComponentInstanceDispose(mAudioUnit);
-}
-
-
-OSStatus CoreAudioPlayback::MixerProcC(void *inRefCon,
- AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
- inBusNumber, inNumberFrames, ioData);
-}
-
-OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*,
- const AudioTimeStamp*, UInt32, UInt32, AudioBufferList *ioData)
-{
- lock();
- aluMixData(mDevice, ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize/mFrameSize);
- unlock();
- return noErr;
-}
-
-
-ALCenum CoreAudioPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = ca_device;
- else if(strcmp(name, ca_device) != 0)
- return ALC_INVALID_VALUE;
-
- /* open the default output unit */
- AudioComponentDescription desc{};
- desc.componentType = kAudioUnitType_Output;
-#if TARGET_OS_IOS
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
-#else
- desc.componentSubType = kAudioUnitSubType_DefaultOutput;
-#endif
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
- if(comp == nullptr)
- {
- ERR("AudioComponentFindNext failed\n");
- return ALC_INVALID_VALUE;
- }
-
- OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
- if(err != noErr)
- {
- ERR("AudioComponentInstanceNew failed\n");
- return ALC_INVALID_VALUE;
- }
-
- /* init and start the default audio unit... */
- err = AudioUnitInitialize(mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioUnitInitialize failed\n");
- AudioComponentInstanceDispose(mAudioUnit);
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean CoreAudioPlayback::reset()
-{
- OSStatus err{AudioUnitUninitialize(mAudioUnit)};
- if(err != noErr)
- ERR("-- AudioUnitUninitialize failed.\n");
-
- /* retrieve default output unit's properties (output side) */
- AudioStreamBasicDescription streamFormat{};
- auto size = static_cast<UInt32>(sizeof(AudioStreamBasicDescription));
- err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
- 0, &streamFormat, &size);
- if(err != noErr || size != sizeof(AudioStreamBasicDescription))
- {
- ERR("AudioUnitGetProperty failed\n");
- return ALC_FALSE;
- }
-
-#if 0
- TRACE("Output streamFormat of default output unit -\n");
- TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
- TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
- TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
- TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
- TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
- TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
-#endif
-
- /* set default output unit's input side to match output side */
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 0, &streamFormat, size);
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_FALSE;
- }
-
- if(mDevice->Frequency != streamFormat.mSampleRate)
- {
- mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} *
- streamFormat.mSampleRate / mDevice->Frequency);
- mDevice->Frequency = streamFormat.mSampleRate;
- }
-
- /* FIXME: How to tell what channels are what in the output device, and how
- * to specify what we're giving? eg, 6.0 vs 5.1 */
- switch(streamFormat.mChannelsPerFrame)
- {
- case 1:
- mDevice->FmtChans = DevFmtMono;
- break;
- case 2:
- mDevice->FmtChans = DevFmtStereo;
- break;
- case 4:
- mDevice->FmtChans = DevFmtQuad;
- break;
- case 6:
- mDevice->FmtChans = DevFmtX51;
- break;
- case 7:
- mDevice->FmtChans = DevFmtX61;
- break;
- case 8:
- mDevice->FmtChans = DevFmtX71;
- break;
- default:
- ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
- mDevice->FmtChans = DevFmtStereo;
- streamFormat.mChannelsPerFrame = 2;
- break;
- }
- SetDefaultWFXChannelOrder(mDevice);
-
- /* use channel count and sample rate from the default output unit's current
- * parameters, but reset everything else */
- streamFormat.mFramesPerPacket = 1;
- streamFormat.mFormatFlags = 0;
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- mDevice->FmtType = DevFmtByte;
- /* fall-through */
- case DevFmtByte:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
- streamFormat.mBitsPerChannel = 8;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
- streamFormat.mBitsPerChannel = 16;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- /* fall-through */
- case DevFmtInt:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
- streamFormat.mBitsPerChannel = 32;
- break;
- case DevFmtFloat:
- streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
- streamFormat.mBitsPerChannel = 32;
- break;
- }
- streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
- streamFormat.mBitsPerChannel / 8;
- streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
- streamFormat.mFormatID = kAudioFormatLinearPCM;
- streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
- kLinearPCMFormatFlagIsPacked;
-
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 0, &streamFormat, sizeof(AudioStreamBasicDescription));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_FALSE;
- }
-
- /* setup callback */
- mFrameSize = mDevice->frameSizeFromFmt();
- AURenderCallbackStruct input{};
- input.inputProc = CoreAudioPlayback::MixerProcC;
- input.inputProcRefCon = this;
-
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_FALSE;
- }
-
- /* init the default audio unit... */
- err = AudioUnitInitialize(mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioUnitInitialize failed\n");
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-ALCboolean CoreAudioPlayback::start()
-{
- OSStatus err{AudioOutputUnitStart(mAudioUnit)};
- if(err != noErr)
- {
- ERR("AudioOutputUnitStart failed\n");
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void CoreAudioPlayback::stop()
-{
- OSStatus err{AudioOutputUnitStop(mAudioUnit)};
- if(err != noErr)
- ERR("AudioOutputUnitStop failed\n");
-}
-
-
-struct CoreAudioCapture final : public BackendBase {
- CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~CoreAudioCapture() override;
-
- static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList *ioData);
- OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
- UInt32 inNumberFrames, AudioBufferList *ioData);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- AudioUnit mAudioUnit{0};
-
- ALuint mFrameSize{0u};
- AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
-
- SampleConverterPtr mConverter;
-
- RingBufferPtr mRing{nullptr};
-
- DEF_NEWDEL(CoreAudioCapture)
-};
-
-CoreAudioCapture::~CoreAudioCapture()
-{
- if(mAudioUnit)
- AudioComponentInstanceDispose(mAudioUnit);
- mAudioUnit = 0;
-}
-
-
-OSStatus CoreAudioCapture::RecordProcC(void *inRefCon,
- AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
- inBusNumber, inNumberFrames, ioData);
-}
-
-OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags*,
- const AudioTimeStamp *inTimeStamp, UInt32, UInt32 inNumberFrames,
- AudioBufferList*)
-{
- AudioUnitRenderActionFlags flags = 0;
- union {
- ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)*2];
- AudioBufferList list;
- } audiobuf = { { 0 } };
-
- auto rec_vec = mRing->getWriteVector();
- inNumberFrames = minz(inNumberFrames, rec_vec.first.len+rec_vec.second.len);
-
- // Fill the ringbuffer's two segments with data from the input device
- if(rec_vec.first.len >= inNumberFrames)
- {
- audiobuf.list.mNumberBuffers = 1;
- audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
- audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
- audiobuf.list.mBuffers[0].mDataByteSize = inNumberFrames * mFormat.mBytesPerFrame;
- }
- else
- {
- const size_t remaining{inNumberFrames-rec_vec.first.len};
- audiobuf.list.mNumberBuffers = 2;
- audiobuf.list.mBuffers[0].mNumberChannels = mFormat.mChannelsPerFrame;
- audiobuf.list.mBuffers[0].mData = rec_vec.first.buf;
- audiobuf.list.mBuffers[0].mDataByteSize = rec_vec.first.len * mFormat.mBytesPerFrame;
- audiobuf.list.mBuffers[1].mNumberChannels = mFormat.mChannelsPerFrame;
- audiobuf.list.mBuffers[1].mData = rec_vec.second.buf;
- audiobuf.list.mBuffers[1].mDataByteSize = remaining * mFormat.mBytesPerFrame;
- }
- OSStatus err{AudioUnitRender(mAudioUnit, &flags, inTimeStamp, audiobuf.list.mNumberBuffers,
- inNumberFrames, &audiobuf.list)};
- if(err != noErr)
- {
- ERR("AudioUnitRender error: %d\n", err);
- return err;
- }
-
- mRing->writeAdvance(inNumberFrames);
- return noErr;
-}
-
-
-ALCenum CoreAudioCapture::open(const ALCchar *name)
-{
- AudioStreamBasicDescription requestedFormat; // The application requested format
- AudioStreamBasicDescription hardwareFormat; // The hardware format
- AudioStreamBasicDescription outputFormat; // The AudioUnit output format
- AURenderCallbackStruct input;
- AudioComponentDescription desc;
- UInt32 outputFrameCount;
- UInt32 propertySize;
- AudioObjectPropertyAddress propertyAddress;
- UInt32 enableIO;
- AudioComponent comp;
- OSStatus err;
-
- if(!name)
- name = ca_device;
- else if(strcmp(name, ca_device) != 0)
- return ALC_INVALID_VALUE;
-
- desc.componentType = kAudioUnitType_Output;
-#if TARGET_OS_IOS
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
-#else
- desc.componentSubType = kAudioUnitSubType_HALOutput;
-#endif
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- // Search for component with given description
- comp = AudioComponentFindNext(NULL, &desc);
- if(comp == NULL)
- {
- ERR("AudioComponentFindNext failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Open the component
- err = AudioComponentInstanceNew(comp, &mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioComponentInstanceNew failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Turn off AudioUnit output
- enableIO = 0;
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Turn on AudioUnit input
- enableIO = 1;
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
-#if !TARGET_OS_IOS
- {
- // Get the default input device
- AudioDeviceID inputDevice = kAudioDeviceUnknown;
-
- propertySize = sizeof(AudioDeviceID);
- propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
- propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
- propertyAddress.mElement = kAudioObjectPropertyElementMaster;
-
- err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
- if(err != noErr)
- {
- ERR("AudioObjectGetPropertyData failed\n");
- return ALC_INVALID_VALUE;
- }
- if(inputDevice == kAudioDeviceUnknown)
- {
- ERR("No input device found\n");
- return ALC_INVALID_VALUE;
- }
-
- // Track the input device
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
- }
-#endif
-
- // set capture callback
- input.inputProc = CoreAudioCapture::RecordProcC;
- input.inputProcRefCon = this;
-
- err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
- kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Initialize the device
- err = AudioUnitInitialize(mAudioUnit);
- if(err != noErr)
- {
- ERR("AudioUnitInitialize failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Get the hardware format
- propertySize = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 1, &hardwareFormat, &propertySize);
- if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
- {
- ERR("AudioUnitGetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Set up the requested format description
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- requestedFormat.mBitsPerChannel = 8;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
- break;
- case DevFmtShort:
- requestedFormat.mBitsPerChannel = 16;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
- break;
- case DevFmtInt:
- requestedFormat.mBitsPerChannel = 32;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
- break;
- case DevFmtFloat:
- requestedFormat.mBitsPerChannel = 32;
- requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
- break;
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- requestedFormat.mChannelsPerFrame = 1;
- break;
- case DevFmtStereo:
- requestedFormat.mChannelsPerFrame = 2;
- break;
-
- case DevFmtQuad:
- case DevFmtX51:
- case DevFmtX51Rear:
- case DevFmtX61:
- case DevFmtX71:
- case DevFmtAmbi3D:
- ERR("%s not supported\n", DevFmtChannelsString(mDevice->FmtChans));
- return ALC_INVALID_VALUE;
- }
-
- requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
- requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
- requestedFormat.mSampleRate = mDevice->Frequency;
- requestedFormat.mFormatID = kAudioFormatLinearPCM;
- requestedFormat.mReserved = 0;
- requestedFormat.mFramesPerPacket = 1;
-
- // save requested format description for later use
- mFormat = requestedFormat;
- mFrameSize = mDevice->frameSizeFromFmt();
-
- // Use intermediate format for sample rate conversion (outputFormat)
- // Set sample rate to the same as hardware for resampling later
- outputFormat = requestedFormat;
- outputFormat.mSampleRate = hardwareFormat.mSampleRate;
-
- // The output format should be the requested format, but using the hardware sample rate
- // This is because the AudioUnit will automatically scale other properties, except for sample rate
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
- 1, (void*)&outputFormat, sizeof(outputFormat));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed\n");
- return ALC_INVALID_VALUE;
- }
-
- // Set the AudioUnit output format frame count
- uint64_t FrameCount64{mDevice->UpdateSize};
- FrameCount64 = (FrameCount64*outputFormat.mSampleRate + mDevice->Frequency-1) /
- mDevice->Frequency;
- FrameCount64 += MAX_RESAMPLE_PADDING*2;
- if(FrameCount64 > std::numeric_limits<uint32_t>::max()/2)
- {
- ERR("FrameCount too large\n");
- return ALC_INVALID_VALUE;
- }
-
- outputFrameCount = static_cast<uint32_t>(FrameCount64);
- err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
- kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
- if(err != noErr)
- {
- ERR("AudioUnitSetProperty failed: %d\n", err);
- return ALC_INVALID_VALUE;
- }
-
- // Set up sample converter if needed
- if(outputFormat.mSampleRate != mDevice->Frequency)
- mConverter = CreateSampleConverter(mDevice->FmtType, mDevice->FmtType,
- mFormat.mChannelsPerFrame, hardwareFormat.mSampleRate, mDevice->Frequency,
- BSinc24Resampler);
-
- mRing = CreateRingBuffer(outputFrameCount, mFrameSize, false);
- if(!mRing) return ALC_INVALID_VALUE;
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-
-ALCboolean CoreAudioCapture::start()
-{
- OSStatus err{AudioOutputUnitStart(mAudioUnit)};
- if(err != noErr)
- {
- ERR("AudioOutputUnitStart failed\n");
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void CoreAudioCapture::stop()
-{
- OSStatus err{AudioOutputUnitStop(mAudioUnit)};
- if(err != noErr)
- ERR("AudioOutputUnitStop failed\n");
-}
-
-ALCenum CoreAudioCapture::captureSamples(void *buffer, ALCuint samples)
-{
- if(!mConverter)
- {
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
- }
-
- auto rec_vec = mRing->getReadVector();
- const void *src0{rec_vec.first.buf};
- auto src0len = static_cast<ALsizei>(rec_vec.first.len);
- auto got = static_cast<ALuint>(mConverter->convert(&src0, &src0len, buffer, samples));
- size_t total_read{rec_vec.first.len - src0len};
- if(got < samples && !src0len && rec_vec.second.len > 0)
- {
- const void *src1{rec_vec.second.buf};
- auto src1len = static_cast<ALsizei>(rec_vec.second.len);
- got += static_cast<ALuint>(mConverter->convert(&src1, &src1len,
- static_cast<char*>(buffer)+got, samples-got));
- total_read += rec_vec.second.len - src1len;
- }
-
- mRing->readAdvance(total_read);
- return ALC_NO_ERROR;
-}
-
-ALCuint CoreAudioCapture::availableSamples()
-{
- if(!mConverter) return mRing->readSpace();
- return mConverter->availableOut(mRing->readSpace());
-}
-
-} // namespace
-
-BackendFactory &CoreAudioBackendFactory::getFactory()
-{
- static CoreAudioBackendFactory factory{};
- return factory;
-}
-
-bool CoreAudioBackendFactory::init() { return true; }
-
-bool CoreAudioBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void CoreAudioBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(ca_device, sizeof(ca_device));
- break;
- }
-}
-
-BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new CoreAudioPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new CoreAudioCapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/coreaudio.h b/Alc/backends/coreaudio.h
deleted file mode 100644
index 37b9ebe5..00000000
--- a/Alc/backends/coreaudio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_COREAUDIO_H
-#define BACKENDS_COREAUDIO_H
-
-#include "backends/base.h"
-
-struct CoreAudioBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_COREAUDIO_H */
diff --git a/Alc/backends/dsound.cpp b/Alc/backends/dsound.cpp
deleted file mode 100644
index 5a156d54..00000000
--- a/Alc/backends/dsound.cpp
+++ /dev/null
@@ -1,938 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/dsound.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
-#include <cguid.h>
-#include <mmreg.h>
-#ifndef _WAVEFORMATEXTENSIBLE_
-#include <ks.h>
-#include <ksmedia.h>
-#endif
-
-#include <atomic>
-#include <cassert>
-#include <thread>
-#include <string>
-#include <vector>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "compat.h"
-#include "threads.h"
-
-/* MinGW-w64 needs this for some unknown reason now. */
-using LPCWAVEFORMATEX = const WAVEFORMATEX*;
-#include <dsound.h>
-
-
-#ifndef DSSPEAKER_5POINT1
-# define DSSPEAKER_5POINT1 0x00000006
-#endif
-#ifndef DSSPEAKER_5POINT1_BACK
-# define DSSPEAKER_5POINT1_BACK 0x00000006
-#endif
-#ifndef DSSPEAKER_7POINT1
-# define DSSPEAKER_7POINT1 0x00000007
-#endif
-#ifndef DSSPEAKER_7POINT1_SURROUND
-# define DSSPEAKER_7POINT1_SURROUND 0x00000008
-#endif
-#ifndef DSSPEAKER_5POINT1_SURROUND
-# define DSSPEAKER_5POINT1_SURROUND 0x00000009
-#endif
-
-
-/* Some headers seem to define these as macros for __uuidof, which is annoying
- * since some headers don't declare them at all. Hopefully the ifdef is enough
- * to tell if they need to be declared.
- */
-#ifndef KSDATAFORMAT_SUBTYPE_PCM
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-
-namespace {
-
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
-#ifdef HAVE_DYNLOAD
-void *ds_handle;
-HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
-HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
-HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
-HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
-
-#ifndef IN_IDE_PARSER
-#define DirectSoundCreate pDirectSoundCreate
-#define DirectSoundEnumerateW pDirectSoundEnumerateW
-#define DirectSoundCaptureCreate pDirectSoundCaptureCreate
-#define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
-#endif
-#endif
-
-
-#define MAX_UPDATES 128
-
-struct DevMap {
- std::string name;
- GUID guid;
-
- template<typename T0, typename T1>
- DevMap(T0&& name_, T1&& guid_)
- : name{std::forward<T0>(name_)}, guid{std::forward<T1>(guid_)}
- { }
-};
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, void *data)
-{
- if(!guid)
- return TRUE;
-
- auto& devices = *static_cast<al::vector<DevMap>*>(data);
- const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
-
- int count{1};
- std::string newname{basename};
- while(checkName(devices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- devices.emplace_back(std::move(newname), *guid);
- const DevMap &newentry = devices.back();
-
- OLECHAR *guidstr{nullptr};
- HRESULT hr{StringFromCLSID(*guid, &guidstr)};
- if(SUCCEEDED(hr))
- {
- TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry.name.c_str(), guidstr);
- CoTaskMemFree(guidstr);
- }
-
- return TRUE;
-}
-
-
-struct DSoundPlayback final : public BackendBase {
- DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~DSoundPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- IDirectSound *mDS{nullptr};
- IDirectSoundBuffer *mPrimaryBuffer{nullptr};
- IDirectSoundBuffer *mBuffer{nullptr};
- IDirectSoundNotify *mNotifies{nullptr};
- HANDLE mNotifyEvent{nullptr};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(DSoundPlayback)
-};
-
-DSoundPlayback::~DSoundPlayback()
-{
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
-
- if(mDS)
- mDS->Release();
- mDS = nullptr;
- if(mNotifyEvent)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-}
-
-
-FORCE_ALIGN int DSoundPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- DSBCAPS DSBCaps{};
- DSBCaps.dwSize = sizeof(DSBCaps);
- HRESULT err{mBuffer->GetCaps(&DSBCaps)};
- if(FAILED(err))
- {
- ERR("Failed to get buffer caps: 0x%lx\n", err);
- aluHandleDisconnect(mDevice, "Failure retrieving playback buffer info: 0x%lx", err);
- return 1;
- }
-
- ALsizei FrameSize{mDevice->frameSizeFromFmt()};
- DWORD FragSize{mDevice->UpdateSize * FrameSize};
-
- bool Playing{false};
- DWORD LastCursor{0u};
- mBuffer->GetCurrentPosition(&LastCursor, nullptr);
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- // Get current play cursor
- DWORD PlayCursor;
- mBuffer->GetCurrentPosition(&PlayCursor, nullptr);
- DWORD avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
-
- if(avail < FragSize)
- {
- if(!Playing)
- {
- err = mBuffer->Play(0, 0, DSBPLAY_LOOPING);
- if(FAILED(err))
- {
- ERR("Failed to play buffer: 0x%lx\n", err);
- aluHandleDisconnect(mDevice, "Failure starting playback: 0x%lx", err);
- return 1;
- }
- Playing = true;
- }
-
- avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE);
- if(avail != WAIT_OBJECT_0)
- ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
- continue;
- }
- avail -= avail%FragSize;
-
- // Lock output buffer
- void *WritePtr1, *WritePtr2;
- DWORD WriteCnt1{0u}, WriteCnt2{0u};
- err = mBuffer->Lock(LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
-
- // If the buffer is lost, restore it and lock
- if(err == DSERR_BUFFERLOST)
- {
- WARN("Buffer lost, restoring...\n");
- err = mBuffer->Restore();
- if(SUCCEEDED(err))
- {
- Playing = false;
- LastCursor = 0;
- err = mBuffer->Lock(0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1,
- &WritePtr2, &WriteCnt2, 0);
- }
- }
-
- if(SUCCEEDED(err))
- {
- lock();
- aluMixData(mDevice, WritePtr1, WriteCnt1/FrameSize);
- if(WriteCnt2 > 0)
- aluMixData(mDevice, WritePtr2, WriteCnt2/FrameSize);
- unlock();
-
- mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
- }
- else
- {
- ERR("Buffer lock error: %#lx\n", err);
- aluHandleDisconnect(mDevice, "Failed to lock output buffer: 0x%lx", err);
- return 1;
- }
-
- // Update old write cursor location
- LastCursor += WriteCnt1+WriteCnt2;
- LastCursor %= DSBCaps.dwBufferBytes;
- }
-
- return 0;
-}
-
-ALCenum DSoundPlayback::open(const ALCchar *name)
-{
- HRESULT hr;
- if(PlaybackDevices.empty())
- {
- /* Initialize COM to prevent name truncation */
- HRESULT hrcom{CoInitialize(nullptr)};
- hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
- if(SUCCEEDED(hrcom))
- CoUninitialize();
- }
-
- const GUID *guid{nullptr};
- if(!name && !PlaybackDevices.empty())
- {
- name = PlaybackDevices[0].name.c_str();
- guid = &PlaybackDevices[0].guid;
- }
- else
- {
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- return ALC_INVALID_VALUE;
- guid = &iter->guid;
- }
-
- hr = DS_OK;
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(!mNotifyEvent) hr = E_FAIL;
-
- //DirectSound Init code
- if(SUCCEEDED(hr))
- hr = DirectSoundCreate(guid, &mDS, nullptr);
- if(SUCCEEDED(hr))
- hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
- if(FAILED(hr))
- {
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean DSoundPlayback::reset()
-{
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- break;
- case DevFmtFloat:
- if(mDevice->Flags.get<SampleTypeRequest>())
- break;
- /* fall-through */
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- break;
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- break;
- }
-
- WAVEFORMATEXTENSIBLE OutputType{};
- DWORD speakers;
- HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
- if(SUCCEEDED(hr))
- {
- speakers = DSSPEAKER_CONFIG(speakers);
- if(!mDevice->Flags.get<ChannelsRequest>())
- {
- if(speakers == DSSPEAKER_MONO)
- mDevice->FmtChans = DevFmtMono;
- else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
- mDevice->FmtChans = DevFmtStereo;
- else if(speakers == DSSPEAKER_QUAD)
- mDevice->FmtChans = DevFmtQuad;
- else if(speakers == DSSPEAKER_5POINT1_SURROUND)
- mDevice->FmtChans = DevFmtX51;
- else if(speakers == DSSPEAKER_5POINT1_BACK)
- mDevice->FmtChans = DevFmtX51Rear;
- else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
- mDevice->FmtChans = DevFmtX71;
- else
- ERR("Unknown system speaker config: 0x%lx\n", speakers);
- }
- mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
- speakers == DSSPEAKER_HEADPHONE);
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtAmbi3D:
- mDevice->FmtChans = DevFmtStereo;
- /*fall-through*/
- case DevFmtStereo:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- }
-
-retry_open:
- hr = S_OK;
- OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.nChannels = mDevice->channelsFromFmt();
- OutputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
- OutputType.Format.nSamplesPerSec = mDevice->Frequency;
- OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
- OutputType.Format.cbSize = 0;
- }
-
- if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
- {
- OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
- OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- if(mDevice->FmtType == DevFmtFloat)
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
- }
- else
- {
- if(SUCCEEDED(hr) && !mPrimaryBuffer)
- {
- DSBUFFERDESC DSBDescription{};
- DSBDescription.dwSize = sizeof(DSBDescription);
- DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
- hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
- }
- if(SUCCEEDED(hr))
- hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
- }
-
- if(SUCCEEDED(hr))
- {
- ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- if(num_updates > MAX_UPDATES)
- num_updates = MAX_UPDATES;
- mDevice->BufferSize = mDevice->UpdateSize * num_updates;
-
- DSBUFFERDESC DSBDescription{};
- DSBDescription.dwSize = sizeof(DSBDescription);
- DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 |
- DSBCAPS_GLOBALFOCUS;
- DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
- DSBDescription.lpwfxFormat = &OutputType.Format;
-
- hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
- if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
- {
- mDevice->FmtType = DevFmtShort;
- goto retry_open;
- }
- }
-
- if(SUCCEEDED(hr))
- {
- void *ptr;
- hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
- if(SUCCEEDED(hr))
- {
- auto Notifies = static_cast<IDirectSoundNotify*>(ptr);
- mNotifies = Notifies;
-
- ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- assert(num_updates <= MAX_UPDATES);
-
- std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
- for(ALuint i{0};i < num_updates;++i)
- {
- nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
- nots[i].hEventNotify = mNotifyEvent;
- }
- if(Notifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK)
- hr = E_FAIL;
- }
- }
-
- if(FAILED(hr))
- {
- if(mNotifies)
- mNotifies->Release();
- mNotifies = nullptr;
- if(mBuffer)
- mBuffer->Release();
- mBuffer = nullptr;
- if(mPrimaryBuffer)
- mPrimaryBuffer->Release();
- mPrimaryBuffer = nullptr;
- return ALC_FALSE;
- }
-
- ResetEvent(mNotifyEvent);
- SetDefaultWFXChannelOrder(mDevice);
-
- return ALC_TRUE;
-}
-
-ALCboolean DSoundPlayback::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&DSoundPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void DSoundPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- mBuffer->Stop();
-}
-
-
-struct DSoundCapture final : public BackendBase {
- DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~DSoundCapture() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- IDirectSoundCapture *mDSC{nullptr};
- IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
- DWORD mBufferBytes{0u};
- DWORD mCursor{0u};
-
- RingBufferPtr mRing;
-
- DEF_NEWDEL(DSoundCapture)
-};
-
-DSoundCapture::~DSoundCapture()
-{
- if(mDSCbuffer)
- {
- mDSCbuffer->Stop();
- mDSCbuffer->Release();
- mDSCbuffer = nullptr;
- }
-
- if(mDSC)
- mDSC->Release();
- mDSC = nullptr;
-}
-
-
-ALCenum DSoundCapture::open(const ALCchar *name)
-{
- HRESULT hr;
- if(CaptureDevices.empty())
- {
- /* Initialize COM to prevent name truncation */
- HRESULT hrcom{CoInitialize(nullptr)};
- hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
- if(SUCCEEDED(hrcom))
- CoUninitialize();
- }
-
- const GUID *guid{nullptr};
- if(!name && !CaptureDevices.empty())
- {
- name = CaptureDevices[0].name.c_str();
- guid = &CaptureDevices[0].guid;
- }
- else
- {
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- return ALC_INVALID_VALUE;
- guid = &iter->guid;
- }
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_ENUM;
-
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- case DevFmtFloat:
- break;
- }
-
- WAVEFORMATEXTENSIBLE InputType{};
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtStereo:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtAmbi3D:
- WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
- return ALC_INVALID_ENUM;
- }
-
- InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- InputType.Format.nChannels = mDevice->channelsFromFmt();
- InputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
- InputType.Format.nSamplesPerSec = mDevice->Frequency;
- InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
- InputType.Format.cbSize = 0;
- InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
- if(mDevice->FmtType == DevFmtFloat)
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-
- if(InputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
- {
- InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- }
-
- ALuint samples{mDevice->BufferSize};
- samples = maxu(samples, 100 * mDevice->Frequency / 1000);
-
- DSCBUFFERDESC DSCBDescription{};
- DSCBDescription.dwSize = sizeof(DSCBDescription);
- DSCBDescription.dwFlags = 0;
- DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
- DSCBDescription.lpwfxFormat = &InputType.Format;
-
- //DirectSoundCapture Init code
- hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
- if(SUCCEEDED(hr))
- mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
- if(SUCCEEDED(hr))
- {
- mRing = CreateRingBuffer(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
- if(!mRing) hr = DSERR_OUTOFMEMORY;
- }
-
- if(FAILED(hr))
- {
- ERR("Device init failed: 0x%08lx\n", hr);
-
- mRing = nullptr;
- if(mDSCbuffer)
- mDSCbuffer->Release();
- mDSCbuffer = nullptr;
- if(mDSC)
- mDSC->Release();
- mDSC = nullptr;
-
- return ALC_INVALID_VALUE;
- }
-
- mBufferBytes = DSCBDescription.dwBufferBytes;
- SetDefaultWFXChannelOrder(mDevice);
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean DSoundCapture::start()
-{
- HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
- if(FAILED(hr))
- {
- ERR("start failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure starting capture: 0x%lx", hr);
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void DSoundCapture::stop()
-{
- HRESULT hr{mDSCbuffer->Stop()};
- if(FAILED(hr))
- {
- ERR("stop failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure stopping capture: 0x%lx", hr);
- }
-}
-
-ALCenum DSoundCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint DSoundCapture::availableSamples()
-{
- if(!mDevice->Connected.load(std::memory_order_acquire))
- return static_cast<ALCuint>(mRing->readSpace());
-
- ALsizei FrameSize{mDevice->frameSizeFromFmt()};
- DWORD BufferBytes{mBufferBytes};
- DWORD LastCursor{mCursor};
-
- DWORD ReadCursor;
- void *ReadPtr1, *ReadPtr2;
- DWORD ReadCnt1, ReadCnt2;
- HRESULT hr{mDSCbuffer->GetCurrentPosition(nullptr, &ReadCursor)};
- if(SUCCEEDED(hr))
- {
- DWORD NumBytes{(ReadCursor-LastCursor + BufferBytes) % BufferBytes};
- if(!NumBytes) return static_cast<ALCubyte>(mRing->readSpace());
- hr = mDSCbuffer->Lock(LastCursor, NumBytes, &ReadPtr1, &ReadCnt1, &ReadPtr2, &ReadCnt2, 0);
- }
- if(SUCCEEDED(hr))
- {
- mRing->write(ReadPtr1, ReadCnt1/FrameSize);
- if(ReadPtr2 != nullptr && ReadCnt2 > 0)
- mRing->write(ReadPtr2, ReadCnt2/FrameSize);
- hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
- mCursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
- }
-
- if(FAILED(hr))
- {
- ERR("update failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failure retrieving capture data: 0x%lx", hr);
- }
-
- return static_cast<ALCuint>(mRing->readSpace());
-}
-
-} // namespace
-
-
-BackendFactory &DSoundBackendFactory::getFactory()
-{
- static DSoundBackendFactory factory{};
- return factory;
-}
-
-bool DSoundBackendFactory::init()
-{
-#ifdef HAVE_DYNLOAD
- if(!ds_handle)
- {
- ds_handle = LoadLib("dsound.dll");
- if(!ds_handle)
- {
- ERR("Failed to load dsound.dll\n");
- return false;
- }
-
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
- if(!p##f) \
- { \
- CloseLib(ds_handle); \
- ds_handle = nullptr; \
- return false; \
- } \
-} while(0)
- LOAD_FUNC(DirectSoundCreate);
- LOAD_FUNC(DirectSoundEnumerateW);
- LOAD_FUNC(DirectSoundCaptureCreate);
- LOAD_FUNC(DirectSoundCaptureEnumerateW);
-#undef LOAD_FUNC
- }
-#endif
- return true;
-}
-
-bool DSoundBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void DSoundBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
-
- /* Initialize COM to prevent name truncation */
- HRESULT hr;
- HRESULT hrcom{CoInitialize(nullptr)};
- switch(type)
- {
- case DevProbe::Playback:
- PlaybackDevices.clear();
- hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- CaptureDevices.clear();
- hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
- if(FAILED(hr))
- ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
- if(SUCCEEDED(hrcom))
- CoUninitialize();
-}
-
-BackendPtr DSoundBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new DSoundPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new DSoundCapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/dsound.h b/Alc/backends/dsound.h
deleted file mode 100644
index 6bef0bfc..00000000
--- a/Alc/backends/dsound.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_DSOUND_H
-#define BACKENDS_DSOUND_H
-
-#include "backends/base.h"
-
-struct DSoundBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_DSOUND_H */
diff --git a/Alc/backends/jack.cpp b/Alc/backends/jack.cpp
deleted file mode 100644
index 3f81d08c..00000000
--- a/Alc/backends/jack.cpp
+++ /dev/null
@@ -1,562 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/jack.h"
-
-#include <cstdlib>
-#include <cstdio>
-#include <memory.h>
-
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "compat.h"
-
-#include <jack/jack.h>
-#include <jack/ringbuffer.h>
-
-
-namespace {
-
-constexpr ALCchar jackDevice[] = "JACK Default";
-
-
-#ifdef HAVE_DYNLOAD
-#define JACK_FUNCS(MAGIC) \
- MAGIC(jack_client_open); \
- MAGIC(jack_client_close); \
- MAGIC(jack_client_name_size); \
- MAGIC(jack_get_client_name); \
- MAGIC(jack_connect); \
- MAGIC(jack_activate); \
- MAGIC(jack_deactivate); \
- MAGIC(jack_port_register); \
- MAGIC(jack_port_unregister); \
- MAGIC(jack_port_get_buffer); \
- MAGIC(jack_port_name); \
- MAGIC(jack_get_ports); \
- MAGIC(jack_free); \
- MAGIC(jack_get_sample_rate); \
- MAGIC(jack_set_error_function); \
- MAGIC(jack_set_process_callback); \
- MAGIC(jack_set_buffer_size_callback); \
- MAGIC(jack_set_buffer_size); \
- MAGIC(jack_get_buffer_size);
-
-void *jack_handle;
-#define MAKE_FUNC(f) decltype(f) * p##f
-JACK_FUNCS(MAKE_FUNC);
-decltype(jack_error_callback) * pjack_error_callback;
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define jack_client_open pjack_client_open
-#define jack_client_close pjack_client_close
-#define jack_client_name_size pjack_client_name_size
-#define jack_get_client_name pjack_get_client_name
-#define jack_connect pjack_connect
-#define jack_activate pjack_activate
-#define jack_deactivate pjack_deactivate
-#define jack_port_register pjack_port_register
-#define jack_port_unregister pjack_port_unregister
-#define jack_port_get_buffer pjack_port_get_buffer
-#define jack_port_name pjack_port_name
-#define jack_get_ports pjack_get_ports
-#define jack_free pjack_free
-#define jack_get_sample_rate pjack_get_sample_rate
-#define jack_set_error_function pjack_set_error_function
-#define jack_set_process_callback pjack_set_process_callback
-#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
-#define jack_set_buffer_size pjack_set_buffer_size
-#define jack_get_buffer_size pjack_get_buffer_size
-#define jack_error_callback (*pjack_error_callback)
-#endif
-#endif
-
-
-jack_options_t ClientOptions = JackNullOption;
-
-ALCboolean jack_load()
-{
- ALCboolean error = ALC_FALSE;
-
-#ifdef HAVE_DYNLOAD
- if(!jack_handle)
- {
- std::string missing_funcs;
-
-#ifdef _WIN32
-#define JACKLIB "libjack.dll"
-#else
-#define JACKLIB "libjack.so.0"
-#endif
- jack_handle = LoadLib(JACKLIB);
- if(!jack_handle)
- {
- WARN("Failed to load %s\n", JACKLIB);
- return ALC_FALSE;
- }
-
- error = ALC_FALSE;
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
- if(p##f == nullptr) { \
- error = ALC_TRUE; \
- missing_funcs += "\n" #f; \
- } \
-} while(0)
- JACK_FUNCS(LOAD_FUNC);
-#undef LOAD_FUNC
- /* Optional symbols. These don't exist in all versions of JACK. */
-#define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f))
- LOAD_SYM(jack_error_callback);
-#undef LOAD_SYM
-
- if(error)
- {
- WARN("Missing expected functions:%s\n", missing_funcs.c_str());
- CloseLib(jack_handle);
- jack_handle = nullptr;
- }
- }
-#endif
-
- return !error;
-}
-
-
-struct JackPlayback final : public BackendBase {
- JackPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~JackPlayback() override;
-
- static int bufferSizeNotifyC(jack_nframes_t numframes, void *arg);
- int bufferSizeNotify(jack_nframes_t numframes);
-
- static int processC(jack_nframes_t numframes, void *arg);
- int process(jack_nframes_t numframes);
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- ClockLatency getClockLatency() override;
-
- jack_client_t *mClient{nullptr};
- jack_port_t *mPort[MAX_OUTPUT_CHANNELS]{};
-
- RingBufferPtr mRing;
- al::semaphore mSem;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(JackPlayback)
-};
-
-JackPlayback::~JackPlayback()
-{
- if(!mClient)
- return;
-
- std::for_each(std::begin(mPort), std::end(mPort),
- [this](jack_port_t *port) -> void
- { if(port) jack_port_unregister(mClient, port); }
- );
- std::fill(std::begin(mPort), std::end(mPort), nullptr);
- jack_client_close(mClient);
- mClient = nullptr;
-}
-
-
-int JackPlayback::bufferSizeNotifyC(jack_nframes_t numframes, void *arg)
-{ return static_cast<JackPlayback*>(arg)->bufferSizeNotify(numframes); }
-
-int JackPlayback::bufferSizeNotify(jack_nframes_t numframes)
-{
- std::lock_guard<std::mutex> _{mDevice->StateLock};
- mDevice->UpdateSize = numframes;
- mDevice->BufferSize = numframes*2;
-
- const char *devname{mDevice->DeviceName.c_str()};
- ALuint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
- bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
- mDevice->BufferSize = bufsize + mDevice->UpdateSize;
-
- TRACE("%u / %u buffer\n", mDevice->UpdateSize, mDevice->BufferSize);
-
- mRing = nullptr;
- mRing = CreateRingBuffer(bufsize, mDevice->frameSizeFromFmt(), true);
- if(!mRing)
- {
- ERR("Failed to reallocate ringbuffer\n");
- aluHandleDisconnect(mDevice, "Failed to reallocate %u-sample buffer", bufsize);
- }
- return 0;
-}
-
-
-int JackPlayback::processC(jack_nframes_t numframes, void *arg)
-{ return static_cast<JackPlayback*>(arg)->process(numframes); }
-
-int JackPlayback::process(jack_nframes_t numframes)
-{
- jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
- ALsizei numchans{0};
- for(auto port : mPort)
- {
- if(!port) break;
- out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
- }
-
- auto data = mRing->getReadVector();
- jack_nframes_t todo{minu(numframes, data.first.len)};
- std::transform(out, out+numchans, out,
- [&data,numchans,todo](ALfloat *outbuf) -> ALfloat*
- {
- const ALfloat *RESTRICT in = reinterpret_cast<ALfloat*>(data.first.buf);
- std::generate_n(outbuf, todo,
- [&in,numchans]() noexcept -> ALfloat
- {
- ALfloat ret{*in};
- in += numchans;
- return ret;
- }
- );
- data.first.buf += sizeof(ALfloat);
- return outbuf + todo;
- }
- );
- jack_nframes_t total{todo};
-
- todo = minu(numframes-total, data.second.len);
- if(todo > 0)
- {
- std::transform(out, out+numchans, out,
- [&data,numchans,todo](ALfloat *outbuf) -> ALfloat*
- {
- const ALfloat *RESTRICT in = reinterpret_cast<ALfloat*>(data.second.buf);
- std::generate_n(outbuf, todo,
- [&in,numchans]() noexcept -> ALfloat
- {
- ALfloat ret{*in};
- in += numchans;
- return ret;
- }
- );
- data.second.buf += sizeof(ALfloat);
- return outbuf + todo;
- }
- );
- total += todo;
- }
-
- mRing->readAdvance(total);
- mSem.post();
-
- if(numframes > total)
- {
- todo = numframes-total;
- std::transform(out, out+numchans, out,
- [todo](ALfloat *outbuf) -> ALfloat*
- {
- std::fill_n(outbuf, todo, 0.0f);
- return outbuf + todo;
- }
- );
- }
-
- return 0;
-}
-
-int JackPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- if(mRing->writeSpace() < mDevice->UpdateSize)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
-
- auto data = mRing->getWriteVector();
- auto todo = static_cast<ALuint>(data.first.len + data.second.len);
- todo -= todo%mDevice->UpdateSize;
-
- ALuint len1{minu(data.first.len, todo)};
- ALuint len2{minu(data.second.len, todo-len1)};
-
- aluMixData(mDevice, data.first.buf, len1);
- if(len2 > 0)
- aluMixData(mDevice, data.second.buf, len2);
- mRing->writeAdvance(todo);
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum JackPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = jackDevice;
- else if(strcmp(name, jackDevice) != 0)
- return ALC_INVALID_VALUE;
-
- const char *client_name{"alsoft"};
- jack_status_t status;
- mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
- if(mClient == nullptr)
- {
- ERR("jack_client_open() failed, status = 0x%02x\n", status);
- return ALC_INVALID_VALUE;
- }
- if((status&JackServerStarted))
- TRACE("JACK server started\n");
- if((status&JackNameNotUnique))
- {
- client_name = jack_get_client_name(mClient);
- TRACE("Client name not unique, got `%s' instead\n", client_name);
- }
-
- jack_set_process_callback(mClient, &JackPlayback::processC, this);
- jack_set_buffer_size_callback(mClient, &JackPlayback::bufferSizeNotifyC, this);
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean JackPlayback::reset()
-{
- std::for_each(std::begin(mPort), std::end(mPort),
- [this](jack_port_t *port) -> void
- { if(port) jack_port_unregister(mClient, port); }
- );
- std::fill(std::begin(mPort), std::end(mPort), nullptr);
-
- /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
- * ready for when requested.
- */
- mDevice->Frequency = jack_get_sample_rate(mClient);
- mDevice->UpdateSize = jack_get_buffer_size(mClient);
- mDevice->BufferSize = mDevice->UpdateSize * 2;
-
- const char *devname{mDevice->DeviceName.c_str()};
- ALuint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
- bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
- mDevice->BufferSize = bufsize + mDevice->UpdateSize;
-
- /* Force 32-bit float output. */
- mDevice->FmtType = DevFmtFloat;
-
- ALsizei numchans{mDevice->channelsFromFmt()};
- auto ports_end = std::begin(mPort) + numchans;
- auto bad_port = std::find_if_not(std::begin(mPort), ports_end,
- [this](jack_port_t *&port) -> bool
- {
- std::string name{"channel_" + std::to_string(&port - mPort + 1)};
- port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsOutput, 0);
- return port != nullptr;
- }
- );
- if(bad_port != ports_end)
- {
- ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(mDevice->FmtChans));
- if(bad_port == std::begin(mPort)) return ALC_FALSE;
-
- if(bad_port == std::begin(mPort)+1)
- mDevice->FmtChans = DevFmtMono;
- else
- {
- ports_end = mPort+2;
- while(bad_port != ports_end)
- {
- jack_port_unregister(mClient, *(--bad_port));
- *bad_port = nullptr;
- }
- mDevice->FmtChans = DevFmtStereo;
- }
- numchans = std::distance(std::begin(mPort), bad_port);
- }
-
- mRing = nullptr;
- mRing = CreateRingBuffer(bufsize, mDevice->frameSizeFromFmt(), true);
- if(!mRing)
- {
- ERR("Failed to allocate ringbuffer\n");
- return ALC_FALSE;
- }
-
- SetDefaultChannelOrder(mDevice);
-
- return ALC_TRUE;
-}
-
-ALCboolean JackPlayback::start()
-{
- if(jack_activate(mClient))
- {
- ERR("Failed to activate client\n");
- return ALC_FALSE;
- }
-
- const char **ports{jack_get_ports(mClient, nullptr, nullptr,
- JackPortIsPhysical|JackPortIsInput)};
- if(ports == nullptr)
- {
- ERR("No physical playback ports found\n");
- jack_deactivate(mClient);
- return ALC_FALSE;
- }
- std::mismatch(std::begin(mPort), std::end(mPort), ports,
- [this](const jack_port_t *port, const char *pname) -> bool
- {
- if(!port) return false;
- if(!pname)
- {
- ERR("No physical playback port for \"%s\"\n", jack_port_name(port));
- return false;
- }
- if(jack_connect(mClient, jack_port_name(port), pname))
- ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(port),
- pname);
- return true;
- }
- );
- jack_free(ports);
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- jack_deactivate(mClient);
- return ALC_FALSE;
-}
-
-void JackPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
-
- mSem.post();
- mThread.join();
-
- jack_deactivate(mClient);
-}
-
-
-ClockLatency JackPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- ret.Latency = std::chrono::seconds{mRing->readSpace()};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-void jack_msg_handler(const char *message)
-{
- WARN("%s\n", message);
-}
-
-} // namespace
-
-bool JackBackendFactory::init()
-{
- if(!jack_load())
- return false;
-
- if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
- ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
-
- void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
- jack_set_error_function(jack_msg_handler);
- jack_status_t status;
- jack_client_t *client{jack_client_open("alsoft", ClientOptions, &status, nullptr)};
- jack_set_error_function(old_error_cb);
- if(!client)
- {
- WARN("jack_client_open() failed, 0x%02x\n", status);
- if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
- ERR("Unable to connect to JACK server\n");
- return false;
- }
-
- jack_client_close(client);
- return true;
-}
-
-bool JackBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback); }
-
-void JackBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- /* Includes null char. */
- outnames->append(jackDevice, sizeof(jackDevice));
- break;
-
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr JackBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new JackPlayback{device}};
- return nullptr;
-}
-
-BackendFactory &JackBackendFactory::getFactory()
-{
- static JackBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/jack.h b/Alc/backends/jack.h
deleted file mode 100644
index 10beebfb..00000000
--- a/Alc/backends/jack.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_JACK_H
-#define BACKENDS_JACK_H
-
-#include "backends/base.h"
-
-struct JackBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_JACK_H */
diff --git a/Alc/backends/loopback.cpp b/Alc/backends/loopback.cpp
deleted file mode 100644
index 4a1c641a..00000000
--- a/Alc/backends/loopback.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by Chris Robinson
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/loopback.h"
-
-#include "alcmain.h"
-#include "alu.h"
-
-
-namespace {
-
-struct LoopbackBackend final : public BackendBase {
- LoopbackBackend(ALCdevice *device) noexcept : BackendBase{device} { }
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- DEF_NEWDEL(LoopbackBackend)
-};
-
-
-ALCenum LoopbackBackend::open(const ALCchar *name)
-{
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean LoopbackBackend::reset()
-{
- SetDefaultWFXChannelOrder(mDevice);
- return ALC_TRUE;
-}
-
-ALCboolean LoopbackBackend::start()
-{ return ALC_TRUE; }
-
-void LoopbackBackend::stop()
-{ }
-
-} // namespace
-
-
-bool LoopbackBackendFactory::init()
-{ return true; }
-
-bool LoopbackBackendFactory::querySupport(BackendType)
-{ return true; }
-
-void LoopbackBackendFactory::probe(DevProbe, std::string*)
-{ }
-
-BackendPtr LoopbackBackendFactory::createBackend(ALCdevice *device, BackendType)
-{ return BackendPtr{new LoopbackBackend{device}}; }
-
-BackendFactory &LoopbackBackendFactory::getFactory()
-{
- static LoopbackBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/loopback.h b/Alc/backends/loopback.h
deleted file mode 100644
index 09c085b8..00000000
--- a/Alc/backends/loopback.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_LOOPBACK_H
-#define BACKENDS_LOOPBACK_H
-
-#include "backends/base.h"
-
-struct LoopbackBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_LOOPBACK_H */
diff --git a/Alc/backends/null.cpp b/Alc/backends/null.cpp
deleted file mode 100644
index ae58cb8b..00000000
--- a/Alc/backends/null.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2010 by Chris Robinson
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/null.h"
-
-#include <exception>
-#include <atomic>
-#include <chrono>
-#include <cstdint>
-#include <cstring>
-#include <functional>
-#include <thread>
-
-#include "alcmain.h"
-#include "almalloc.h"
-#include "alu.h"
-#include "logging.h"
-#include "threads.h"
-
-
-namespace {
-
-using std::chrono::seconds;
-using std::chrono::milliseconds;
-using std::chrono::nanoseconds;
-
-constexpr ALCchar nullDevice[] = "No Output";
-
-
-struct NullBackend final : public BackendBase {
- NullBackend(ALCdevice *device) noexcept : BackendBase{device} { }
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(NullBackend)
-};
-
-int NullBackend::mixerProc()
-{
- const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
-
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- int64_t done{0};
- auto start = std::chrono::steady_clock::now();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto now = std::chrono::steady_clock::now();
-
- /* This converts from nanoseconds to nanosamples, then to samples. */
- int64_t avail{std::chrono::duration_cast<seconds>((now-start) * mDevice->Frequency).count()};
- if(avail-done < mDevice->UpdateSize)
- {
- std::this_thread::sleep_for(restTime);
- continue;
- }
- while(avail-done >= mDevice->UpdateSize)
- {
- lock();
- aluMixData(mDevice, nullptr, mDevice->UpdateSize);
- unlock();
- done += mDevice->UpdateSize;
- }
-
- /* For every completed second, increment the start time and reduce the
- * samples done. This prevents the difference between the start time
- * and current time from growing too large, while maintaining the
- * correct number of samples to render.
- */
- if(done >= mDevice->Frequency)
- {
- seconds s{done/mDevice->Frequency};
- start += s;
- done -= mDevice->Frequency*s.count();
- }
- }
-
- return 0;
-}
-
-
-ALCenum NullBackend::open(const ALCchar *name)
-{
- if(!name)
- name = nullDevice;
- else if(strcmp(name, nullDevice) != 0)
- return ALC_INVALID_VALUE;
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean NullBackend::reset()
-{
- SetDefaultWFXChannelOrder(mDevice);
- return ALC_TRUE;
-}
-
-ALCboolean NullBackend::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&NullBackend::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void NullBackend::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-}
-
-} // namespace
-
-
-bool NullBackendFactory::init()
-{ return true; }
-
-bool NullBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback); }
-
-void NullBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- /* Includes null char. */
- outnames->append(nullDevice, sizeof(nullDevice));
- break;
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr NullBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new NullBackend{device}};
- return nullptr;
-}
-
-BackendFactory &NullBackendFactory::getFactory()
-{
- static NullBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/null.h b/Alc/backends/null.h
deleted file mode 100644
index f19d5b4d..00000000
--- a/Alc/backends/null.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_NULL_H
-#define BACKENDS_NULL_H
-
-#include "backends/base.h"
-
-struct NullBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_NULL_H */
diff --git a/Alc/backends/opensl.cpp b/Alc/backends/opensl.cpp
deleted file mode 100644
index b34dc0cb..00000000
--- a/Alc/backends/opensl.cpp
+++ /dev/null
@@ -1,936 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* This is an OpenAL backend for Android using the native audio APIs based on
- * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app
- * bundled with NDK.
- */
-
-#include "config.h"
-
-#include "backends/opensl.h"
-
-#include <stdlib.h>
-#include <jni.h>
-
-#include <new>
-#include <array>
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "compat.h"
-
-#include <SLES/OpenSLES.h>
-#include <SLES/OpenSLES_Android.h>
-#include <SLES/OpenSLES_AndroidConfiguration.h>
-
-
-namespace {
-
-/* Helper macros */
-#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
-#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
-#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
-
-
-constexpr ALCchar opensl_device[] = "OpenSL";
-
-
-SLuint32 GetChannelMask(DevFmtChannels chans)
-{
- switch(chans)
- {
- case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
- case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
- case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
- case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtX51Rear: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
- case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_BACK_CENTER|
- SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
- SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
- SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
- SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtAmbi3D:
- break;
- }
- return 0;
-}
-
-#ifdef SL_ANDROID_DATAFORMAT_PCM_EX
-SLuint32 GetTypeRepresentation(DevFmtType type)
-{
- switch(type)
- {
- case DevFmtUByte:
- case DevFmtUShort:
- case DevFmtUInt:
- return SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT;
- case DevFmtByte:
- case DevFmtShort:
- case DevFmtInt:
- return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
- case DevFmtFloat:
- return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
- }
- return 0;
-}
-#endif
-
-const char *res_str(SLresult result)
-{
- switch(result)
- {
- case SL_RESULT_SUCCESS: return "Success";
- case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
- case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
- case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
- case SL_RESULT_RESOURCE_ERROR: return "Resource error";
- case SL_RESULT_RESOURCE_LOST: return "Resource lost";
- case SL_RESULT_IO_ERROR: return "I/O error";
- case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
- case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
- case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
- case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
- case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
- case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
- case SL_RESULT_INTERNAL_ERROR: return "Internal error";
- case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
- case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
- case SL_RESULT_CONTROL_LOST: return "Control lost";
-#ifdef SL_RESULT_READONLY
- case SL_RESULT_READONLY: return "ReadOnly";
-#endif
-#ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
- case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
-#endif
-#ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
- case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
-#endif
- }
- return "Unknown error code";
-}
-
-#define PRINTERR(x, s) do { \
- if(UNLIKELY((x) != SL_RESULT_SUCCESS)) \
- ERR("%s: %s\n", (s), res_str((x))); \
-} while(0)
-
-
-struct OpenSLPlayback final : public BackendBase {
- OpenSLPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OpenSLPlayback() override;
-
- static void processC(SLAndroidSimpleBufferQueueItf bq, void *context);
- void process(SLAndroidSimpleBufferQueueItf bq);
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- ClockLatency getClockLatency() override;
-
- /* engine interfaces */
- SLObjectItf mEngineObj{nullptr};
- SLEngineItf mEngine{nullptr};
-
- /* output mix interfaces */
- SLObjectItf mOutputMix{nullptr};
-
- /* buffer queue player interfaces */
- SLObjectItf mBufferQueueObj{nullptr};
-
- RingBufferPtr mRing{nullptr};
- al::semaphore mSem;
-
- ALsizei mFrameSize{0};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(OpenSLPlayback)
-};
-
-OpenSLPlayback::~OpenSLPlayback()
-{
- if(mBufferQueueObj)
- VCALL0(mBufferQueueObj,Destroy)();
- mBufferQueueObj = nullptr;
-
- if(mOutputMix)
- VCALL0(mOutputMix,Destroy)();
- mOutputMix = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-}
-
-
-/* this callback handler is called every time a buffer finishes playing */
-void OpenSLPlayback::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
-{ static_cast<OpenSLPlayback*>(context)->process(bq); }
-
-void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf)
-{
- /* A note on the ringbuffer usage: The buffer queue seems to hold on to the
- * pointer passed to the Enqueue method, rather than copying the audio.
- * Consequently, the ringbuffer contains the audio that is currently queued
- * and waiting to play. This process() callback is called when a buffer is
- * finished, so we simply move the read pointer up to indicate the space is
- * available for writing again, and wake up the mixer thread to mix and
- * queue more audio.
- */
- mRing->readAdvance(1);
-
- mSem.post();
-}
-
-int OpenSLPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- SLPlayItf player;
- SLAndroidSimpleBufferQueueItf bufferQueue;
- SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- &bufferQueue)};
- PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
- PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
- }
-
- lock();
- if(SL_RESULT_SUCCESS != result)
- aluHandleDisconnect(mDevice, "Failed to get playback buffer: 0x%08x", result);
-
- while(SL_RESULT_SUCCESS == result && !mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- if(mRing->writeSpace() == 0)
- {
- SLuint32 state{0};
-
- result = VCALL(player,GetPlayState)(&state);
- PRINTERR(result, "player->GetPlayState");
- if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING)
- {
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
- PRINTERR(result, "player->SetPlayState");
- }
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to start platback: 0x%08x", result);
- break;
- }
-
- if(mRing->writeSpace() == 0)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
- }
-
- auto data = mRing->getWriteVector();
- aluMixData(mDevice, data.first.buf, data.first.len*mDevice->UpdateSize);
- if(data.second.len > 0)
- aluMixData(mDevice, data.second.buf, data.second.len*mDevice->UpdateSize);
-
- size_t todo{data.first.len + data.second.len};
- mRing->writeAdvance(todo);
-
- for(size_t i{0};i < todo;i++)
- {
- if(!data.first.len)
- {
- data.first = data.second;
- data.second.buf = nullptr;
- data.second.len = 0;
- }
-
- result = VCALL(bufferQueue,Enqueue)(data.first.buf, mDevice->UpdateSize*mFrameSize);
- PRINTERR(result, "bufferQueue->Enqueue");
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to queue audio: 0x%08x", result);
- break;
- }
-
- data.first.len--;
- data.first.buf += mDevice->UpdateSize*mFrameSize;
- }
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum OpenSLPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = opensl_device;
- else if(strcmp(name, opensl_device) != 0)
- return ALC_INVALID_VALUE;
-
- // create engine
- SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
- PRINTERR(result, "slCreateEngine");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "engine->Realize");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
- PRINTERR(result, "engine->GetInterface");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngine,CreateOutputMix)(&mOutputMix, 0, nullptr, nullptr);
- PRINTERR(result, "engine->CreateOutputMix");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mOutputMix,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "outputMix->Realize");
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- if(mOutputMix)
- VCALL0(mOutputMix,Destroy)();
- mOutputMix = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OpenSLPlayback::reset()
-{
- SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
- SLDataLocator_OutputMix loc_outmix;
- SLDataSource audioSrc;
- SLDataSink audioSnk;
- SLresult result;
-
- if(mBufferQueueObj)
- VCALL0(mBufferQueueObj,Destroy)();
- mBufferQueueObj = nullptr;
-
- mRing = nullptr;
-
-#if 0
- if(!mDevice->Flags.get<FrequencyRequest>())
- {
- /* FIXME: Disabled until I figure out how to get the Context needed for
- * the getSystemService call.
- */
- JNIEnv *env = Android_GetJNIEnv();
- jobject jctx = Android_GetContext();
-
- /* Get necessary stuff for using java.lang.Integer,
- * android.content.Context, and android.media.AudioManager.
- */
- jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
- jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
- "parseInt", "(Ljava/lang/String;)I"
- );
- TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
-
- jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
- jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
- "AUDIO_SERVICE", "Ljava/lang/String;"
- );
- jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
- "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
- );
- TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
- ctx_cls, ctx_audsvc, ctx_getSysSvc);
-
- jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
- jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
- "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
- );
- jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
- "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
- );
- TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
- audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
-
- const char *strchars;
- jstring strobj;
-
- /* Now make the calls. */
- //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
- strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
- jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
- strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
- TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
- JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
-
- //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
- strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
- jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
- strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
- TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
- JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
-
- //int sampleRate = Integer.parseInt(srateStr);
- sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
-
- strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr);
- TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
- JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
-
- if(!sampleRate) sampleRate = device->Frequency;
- else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
- }
-#endif
-
- mDevice->FmtChans = DevFmtStereo;
- mDevice->FmtType = DevFmtShort;
-
- SetDefaultWFXChannelOrder(mDevice);
- mFrameSize = mDevice->frameSizeFromFmt();
-
-
- const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
- const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
-
- loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
- loc_bufq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
-
-#ifdef SL_ANDROID_DATAFORMAT_PCM_EX
- SLAndroidDataFormat_PCM_EX format_pcm{};
- format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.sampleRate = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
- SL_BYTEORDER_BIGENDIAN;
- format_pcm.representation = GetTypeRepresentation(mDevice->FmtType);
-#else
- SLDataFormat_PCM format_pcm{};
- format_pcm.formatType = SL_DATAFORMAT_PCM;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.samplesPerSec = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
- SL_BYTEORDER_BIGENDIAN;
-#endif
-
- audioSrc.pLocator = &loc_bufq;
- audioSrc.pFormat = &format_pcm;
-
- loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
- loc_outmix.outputMix = mOutputMix;
- audioSnk.pLocator = &loc_outmix;
- audioSnk.pFormat = nullptr;
-
-
- result = VCALL(mEngine,CreateAudioPlayer)(&mBufferQueueObj, &audioSrc, &audioSnk, ids.size(),
- ids.data(), reqs.data());
- PRINTERR(result, "engine->CreateAudioPlayer");
- if(SL_RESULT_SUCCESS == result)
- {
- /* Set the stream type to "media" (games, music, etc), if possible. */
- SLAndroidConfigurationItf config;
- result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
- PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
- if(SL_RESULT_SUCCESS == result)
- {
- SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
- result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE, &streamType,
- sizeof(streamType));
- PRINTERR(result, "config->SetConfiguration");
- }
-
- /* Clear any error since this was optional. */
- result = SL_RESULT_SUCCESS;
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "bufferQueue->Realize");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- const ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
- try {
- mRing = CreateRingBuffer(num_updates, mFrameSize*mDevice->UpdateSize, true);
- }
- catch(std::exception& e) {
- ERR("Failed allocating ring buffer %ux%ux%u: %s\n", mDevice->UpdateSize,
- num_updates, mFrameSize, e.what());
- result = SL_RESULT_MEMORY_FAILURE;
- }
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- if(mBufferQueueObj)
- VCALL0(mBufferQueueObj,Destroy)();
- mBufferQueueObj = nullptr;
-
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-ALCboolean OpenSLPlayback::start()
-{
- mRing->reset();
-
- SLAndroidSimpleBufferQueueItf bufferQueue;
- SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- &bufferQueue)};
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS != result)
- return ALC_FALSE;
-
- result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
- PRINTERR(result, "bufferQueue->RegisterCallback");
- if(SL_RESULT_SUCCESS != result) return ALC_FALSE;
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread(std::mem_fn(&OpenSLPlayback::mixerProc), this);
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void OpenSLPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
-
- mSem.post();
- mThread.join();
-
- SLPlayItf player;
- SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player)};
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
- PRINTERR(result, "player->SetPlayState");
- }
-
- SLAndroidSimpleBufferQueueItf bufferQueue;
- result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL0(bufferQueue,Clear)();
- PRINTERR(result, "bufferQueue->Clear");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(bufferQueue,RegisterCallback)(nullptr, nullptr);
- PRINTERR(result, "bufferQueue->RegisterCallback");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- SLAndroidSimpleBufferQueueState state;
- do {
- std::this_thread::yield();
- result = VCALL(bufferQueue,GetState)(&state);
- } while(SL_RESULT_SUCCESS == result && state.count > 0);
- PRINTERR(result, "bufferQueue->GetState");
- }
-}
-
-ClockLatency OpenSLPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-struct OpenSLCapture final : public BackendBase {
- OpenSLCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OpenSLCapture() override;
-
- static void processC(SLAndroidSimpleBufferQueueItf bq, void *context);
- void process(SLAndroidSimpleBufferQueueItf bq);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- /* engine interfaces */
- SLObjectItf mEngineObj{nullptr};
- SLEngineItf mEngine;
-
- /* recording interfaces */
- SLObjectItf mRecordObj{nullptr};
-
- RingBufferPtr mRing{nullptr};
- ALCuint mSplOffset{0u};
-
- ALsizei mFrameSize{0};
-
- DEF_NEWDEL(OpenSLCapture)
-};
-
-OpenSLCapture::~OpenSLCapture()
-{
- if(mRecordObj)
- VCALL0(mRecordObj,Destroy)();
- mRecordObj = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-}
-
-
-void OpenSLCapture::processC(SLAndroidSimpleBufferQueueItf bq, void *context)
-{ static_cast<OpenSLCapture*>(context)->process(bq); }
-
-void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf)
-{
- /* A new chunk has been written into the ring buffer, advance it. */
- mRing->writeAdvance(1);
-}
-
-
-ALCenum OpenSLCapture::open(const ALCchar* name)
-{
- if(!name)
- name = opensl_device;
- else if(strcmp(name, opensl_device) != 0)
- return ALC_INVALID_VALUE;
-
- SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
- PRINTERR(result, "slCreateEngine");
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "engine->Realize");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
- PRINTERR(result, "engine->GetInterface");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- mFrameSize = mDevice->frameSizeFromFmt();
- /* Ensure the total length is at least 100ms */
- ALsizei length{maxi(mDevice->BufferSize, mDevice->Frequency/10)};
- /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
- ALsizei update_len{clampi(mDevice->BufferSize/3, mDevice->Frequency/100,
- mDevice->Frequency/100*5)};
- ALsizei num_updates{(length+update_len-1) / update_len};
-
- try {
- mRing = CreateRingBuffer(num_updates, update_len*mFrameSize, false);
-
- mDevice->UpdateSize = update_len;
- mDevice->BufferSize = mRing->writeSpace() * update_len;
- }
- catch(std::exception& e) {
- ERR("Failed to allocate ring buffer: %s\n", e.what());
- result = SL_RESULT_MEMORY_FAILURE;
- }
- }
- if(SL_RESULT_SUCCESS == result)
- {
- const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
- const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
-
- SLDataLocator_IODevice loc_dev{};
- loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
- loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
- loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
- loc_dev.device = nullptr;
-
- SLDataSource audioSrc{};
- audioSrc.pLocator = &loc_dev;
- audioSrc.pFormat = nullptr;
-
- SLDataLocator_AndroidSimpleBufferQueue loc_bq{};
- loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
- loc_bq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
-
-#ifdef SL_ANDROID_DATAFORMAT_PCM_EX
- SLAndroidDataFormat_PCM_EX format_pcm{};
- format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.sampleRate = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
- format_pcm.representation = GetTypeRepresentation(mDevice->FmtType);
-#else
- SLDataFormat_PCM format_pcm{};
- format_pcm.formatType = SL_DATAFORMAT_PCM;
- format_pcm.numChannels = mDevice->channelsFromFmt();
- format_pcm.samplesPerSec = mDevice->Frequency * 1000;
- format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
- format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
- format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
-#endif
-
- SLDataSink audioSnk{};
- audioSnk.pLocator = &loc_bq;
- audioSnk.pFormat = &format_pcm;
-
- result = VCALL(mEngine,CreateAudioRecorder)(&mRecordObj, &audioSrc, &audioSnk,
- ids.size(), ids.data(), reqs.data());
- PRINTERR(result, "engine->CreateAudioRecorder");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- /* Set the record preset to "generic", if possible. */
- SLAndroidConfigurationItf config;
- result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
- PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
- if(SL_RESULT_SUCCESS == result)
- {
- SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
- result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET, &preset,
- sizeof(preset));
- PRINTERR(result, "config->SetConfiguration");
- }
-
- /* Clear any error since this was optional. */
- result = SL_RESULT_SUCCESS;
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mRecordObj,Realize)(SL_BOOLEAN_FALSE);
- PRINTERR(result, "recordObj->Realize");
- }
-
- SLAndroidSimpleBufferQueueItf bufferQueue;
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "recordObj->GetInterface");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
- PRINTERR(result, "bufferQueue->RegisterCallback");
- }
- if(SL_RESULT_SUCCESS == result)
- {
- const ALuint chunk_size{mDevice->UpdateSize * mFrameSize};
-
- auto data = mRing->getWriteVector();
- for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++)
- {
- result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size);
- PRINTERR(result, "bufferQueue->Enqueue");
- }
- for(size_t i{0u};i < data.second.len && SL_RESULT_SUCCESS == result;i++)
- {
- result = VCALL(bufferQueue,Enqueue)(data.second.buf + chunk_size*i, chunk_size);
- PRINTERR(result, "bufferQueue->Enqueue");
- }
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- if(mRecordObj)
- VCALL0(mRecordObj,Destroy)();
- mRecordObj = nullptr;
-
- if(mEngineObj)
- VCALL0(mEngineObj,Destroy)();
- mEngineObj = nullptr;
- mEngine = nullptr;
-
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OpenSLCapture::start()
-{
- SLRecordItf record;
- SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
- PRINTERR(result, "recordObj->GetInterface");
-
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(record,SetRecordState)(SL_RECORDSTATE_RECORDING);
- PRINTERR(result, "record->SetRecordState");
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to start capture: 0x%08x", result);
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-void OpenSLCapture::stop()
-{
- SLRecordItf record;
- SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
- PRINTERR(result, "recordObj->GetInterface");
-
- if(SL_RESULT_SUCCESS == result)
- {
- result = VCALL(record,SetRecordState)(SL_RECORDSTATE_PAUSED);
- PRINTERR(result, "record->SetRecordState");
- }
-}
-
-ALCenum OpenSLCapture::captureSamples(void* buffer, ALCuint samples)
-{
- ALsizei chunk_size = mDevice->UpdateSize * mFrameSize;
- SLAndroidSimpleBufferQueueItf bufferQueue;
- SLresult result;
- ALCuint i;
-
- result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "recordObj->GetInterface");
-
- /* Read the desired samples from the ring buffer then advance its read
- * pointer.
- */
- auto data = mRing->getReadVector();
- for(i = 0;i < samples;)
- {
- ALCuint rem{minu(samples - i, mDevice->UpdateSize - mSplOffset)};
- memcpy((ALCbyte*)buffer + i*mFrameSize, data.first.buf + mSplOffset*mFrameSize,
- rem * mFrameSize);
-
- mSplOffset += rem;
- if(mSplOffset == mDevice->UpdateSize)
- {
- /* Finished a chunk, reset the offset and advance the read pointer. */
- mSplOffset = 0;
-
- mRing->readAdvance(1);
- result = VCALL(bufferQueue,Enqueue)(data.first.buf, chunk_size);
- PRINTERR(result, "bufferQueue->Enqueue");
- if(SL_RESULT_SUCCESS != result) break;
-
- data.first.len--;
- if(!data.first.len)
- data.first = data.second;
- else
- data.first.buf += chunk_size;
- }
-
- i += rem;
- }
-
- if(SL_RESULT_SUCCESS != result)
- {
- aluHandleDisconnect(mDevice, "Failed to update capture buffer: 0x%08x", result);
- return ALC_INVALID_DEVICE;
- }
-
- return ALC_NO_ERROR;
-}
-
-ALCuint OpenSLCapture::availableSamples()
-{ return mRing->readSpace()*mDevice->UpdateSize - mSplOffset; }
-
-} // namespace
-
-bool OSLBackendFactory::init() { return true; }
-
-bool OSLBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void OSLBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(opensl_device, sizeof(opensl_device));
- break;
- }
-}
-
-BackendPtr OSLBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new OpenSLPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new OpenSLCapture{device}};
- return nullptr;
-}
-
-BackendFactory &OSLBackendFactory::getFactory()
-{
- static OSLBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/opensl.h b/Alc/backends/opensl.h
deleted file mode 100644
index 809aa339..00000000
--- a/Alc/backends/opensl.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_OSL_H
-#define BACKENDS_OSL_H
-
-#include "backends/base.h"
-
-struct OSLBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_OSL_H */
diff --git a/Alc/backends/oss.cpp b/Alc/backends/oss.cpp
deleted file mode 100644
index 8cfe9e96..00000000
--- a/Alc/backends/oss.cpp
+++ /dev/null
@@ -1,751 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/oss.h"
-
-#include <fcntl.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <atomic>
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <exception>
-#include <functional>
-#include <memory>
-#include <new>
-#include <string>
-#include <thread>
-#include <utility>
-
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "aloptional.h"
-#include "alu.h"
-#include "logging.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "vector.h"
-
-#include <sys/soundcard.h>
-
-/*
- * The OSS documentation talks about SOUND_MIXER_READ, but the header
- * only contains MIXER_READ. Play safe. Same for WRITE.
- */
-#ifndef SOUND_MIXER_READ
-#define SOUND_MIXER_READ MIXER_READ
-#endif
-#ifndef SOUND_MIXER_WRITE
-#define SOUND_MIXER_WRITE MIXER_WRITE
-#endif
-
-#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
-#define ALC_OSS_COMPAT
-#endif
-#ifndef SNDCTL_AUDIOINFO
-#define ALC_OSS_COMPAT
-#endif
-
-/*
- * FreeBSD strongly discourages the use of specific devices,
- * such as those returned in oss_audioinfo.devnode
- */
-#ifdef __FreeBSD__
-#define ALC_OSS_DEVNODE_TRUC
-#endif
-
-namespace {
-
-constexpr char DefaultName[] = "OSS Default";
-std::string DefaultPlayback{"/dev/dsp"};
-std::string DefaultCapture{"/dev/dsp"};
-
-struct DevMap {
- std::string name;
- std::string device_name;
-};
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-#ifdef ALC_OSS_COMPAT
-
-#define DSP_CAP_OUTPUT 0x00020000
-#define DSP_CAP_INPUT 0x00010000
-void ALCossListPopulate(al::vector<DevMap> *devlist, int type)
-{
- devlist->emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback});
-}
-
-#else
-
-void ALCossListAppend(al::vector<DevMap> *list, const char *handle, size_t hlen, const char *path, size_t plen)
-{
-#ifdef ALC_OSS_DEVNODE_TRUC
- for(size_t i{0};i < plen;i++)
- {
- if(path[i] == '.')
- {
- if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
- hlen = hlen + i - plen;
- plen = i;
- }
- }
-#endif
- if(handle[0] == '\0')
- {
- handle = path;
- hlen = plen;
- }
-
- std::string basename{handle, hlen};
- basename.erase(std::find(basename.begin(), basename.end(), '\0'), basename.end());
- std::string devname{path, plen};
- devname.erase(std::find(devname.begin(), devname.end(), '\0'), devname.end());
-
- auto iter = std::find_if(list->cbegin(), list->cend(),
- [&devname](const DevMap &entry) -> bool
- { return entry.device_name == devname; }
- );
- if(iter != list->cend())
- return;
-
- int count{1};
- std::string newname{basename};
- while(checkName(PlaybackDevices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
-
- list->emplace_back(DevMap{std::move(newname), std::move(devname)});
- const DevMap &entry = list->back();
-
- TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
-}
-
-void ALCossListPopulate(al::vector<DevMap> *devlist, int type_flag)
-{
- int fd{open("/dev/mixer", O_RDONLY)};
- if(fd < 0)
- {
- TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
- goto done;
- }
-
- oss_sysinfo si;
- if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
- {
- TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
- goto done;
- }
-
- for(int i{0};i < si.numaudios;i++)
- {
- oss_audioinfo ai;
- ai.dev = i;
- if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
- {
- ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
- continue;
- }
- if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
- continue;
-
- const char *handle;
- size_t len;
- if(ai.handle[0] != '\0')
- {
- len = strnlen(ai.handle, sizeof(ai.handle));
- handle = ai.handle;
- }
- else
- {
- len = strnlen(ai.name, sizeof(ai.name));
- handle = ai.name;
- }
-
- ALCossListAppend(devlist, handle, len, ai.devnode,
- strnlen(ai.devnode, sizeof(ai.devnode)));
- }
-
-done:
- if(fd >= 0)
- close(fd);
- fd = -1;
-
- const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()};
- auto iter = std::find_if(devlist->cbegin(), devlist->cend(),
- [defdev](const DevMap &entry) -> bool
- { return entry.device_name == defdev; }
- );
- if(iter == devlist->cend())
- devlist->insert(devlist->begin(), DevMap{DefaultName, defdev});
- else
- {
- DevMap entry{std::move(*iter)};
- devlist->erase(iter);
- devlist->insert(devlist->begin(), std::move(entry));
- }
- devlist->shrink_to_fit();
-}
-
-#endif
-
-int log2i(ALCuint x)
-{
- int y = 0;
- while (x > 1)
- {
- x >>= 1;
- y++;
- }
- return y;
-}
-
-
-struct OSSPlayback final : public BackendBase {
- OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OSSPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- int mFd{-1};
-
- al::vector<ALubyte> mMixData;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(OSSPlayback)
-};
-
-OSSPlayback::~OSSPlayback()
-{
- if(mFd != -1)
- close(mFd);
- mFd = -1;
-}
-
-
-int OSSPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const int frame_size{mDevice->frameSizeFromFmt()};
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = mFd;
- pollitem.events = POLLOUT;
-
- unlock();
- int pret{poll(&pollitem, 1, 1000)};
- lock();
- if(pret < 0)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed waiting for playback buffer: %s", strerror(errno));
- break;
- }
- else if(pret == 0)
- {
- WARN("poll timeout\n");
- continue;
- }
-
- ALubyte *write_ptr{mMixData.data()};
- size_t to_write{mMixData.size()};
- aluMixData(mDevice, write_ptr, to_write/frame_size);
- while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
- {
- ssize_t wrote{write(mFd, write_ptr, to_write)};
- if(wrote < 0)
- {
- if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- ERR("write failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed writing playback samples: %s",
- strerror(errno));
- break;
- }
-
- to_write -= wrote;
- write_ptr += wrote;
- }
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum OSSPlayback::open(const ALCchar *name)
-{
- const char *devname{DefaultPlayback.c_str()};
- if(!name)
- name = DefaultName;
- else
- {
- if(PlaybackDevices.empty())
- ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
-
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- return ALC_INVALID_VALUE;
- devname = iter->device_name.c_str();
- }
-
- mFd = ::open(devname, O_WRONLY);
- if(mFd == -1)
- {
- ERR("Could not open %s: %s\n", devname, strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OSSPlayback::reset()
-{
- int numFragmentsLogSize;
- int log2FragmentSize;
- unsigned int periods;
- audio_buf_info info;
- ALuint frameSize;
- int numChannels;
- int ossFormat;
- int ossSpeed;
- const char *err;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- ossFormat = AFMT_S8;
- break;
- case DevFmtUByte:
- ossFormat = AFMT_U8;
- break;
- case DevFmtUShort:
- case DevFmtInt:
- case DevFmtUInt:
- case DevFmtFloat:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- ossFormat = AFMT_S16_NE;
- break;
- }
-
- periods = mDevice->BufferSize / mDevice->UpdateSize;
- numChannels = mDevice->channelsFromFmt();
- ossSpeed = mDevice->Frequency;
- frameSize = numChannels * mDevice->bytesFromFmt();
- /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
- log2FragmentSize = maxi(log2i(mDevice->UpdateSize*frameSize), 4);
- numFragmentsLogSize = (periods << 16) | log2FragmentSize;
-
-#define CHECKERR(func) if((func) < 0) { \
- err = #func; \
- goto err; \
-}
- /* Don't fail if SETFRAGMENT fails. We can handle just about anything
- * that's reported back via GETOSPACE */
- ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
- if(0)
- {
- err:
- ERR("%s failed: %s\n", err, strerror(errno));
- return ALC_FALSE;
- }
-#undef CHECKERR
-
- if(mDevice->channelsFromFmt() != numChannels)
- {
- ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
- numChannels);
- return ALC_FALSE;
- }
-
- if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
- (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
- (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
- {
- ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType),
- ossFormat);
- return ALC_FALSE;
- }
-
- mDevice->Frequency = ossSpeed;
- mDevice->UpdateSize = info.fragsize / frameSize;
- mDevice->BufferSize = info.fragments * mDevice->UpdateSize;
-
- SetDefaultChannelOrder(mDevice);
-
- mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
-
- return ALC_TRUE;
-}
-
-ALCboolean OSSPlayback::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void OSSPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
- ERR("Error resetting device: %s\n", strerror(errno));
-}
-
-
-struct OSScapture final : public BackendBase {
- OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~OSScapture() override;
-
- int recordProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- int mFd{-1};
-
- RingBufferPtr mRing{nullptr};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(OSScapture)
-};
-
-OSScapture::~OSScapture()
-{
- if(mFd != -1)
- close(mFd);
- mFd = -1;
-}
-
-
-int OSScapture::recordProc()
-{
- SetRTPriority();
- althrd_setname(RECORD_THREAD_NAME);
-
- const int frame_size{mDevice->frameSizeFromFmt()};
- while(!mKillNow.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = mFd;
- pollitem.events = POLLIN;
-
- int sret{poll(&pollitem, 1, 1000)};
- if(sret < 0)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed to check capture samples: %s", strerror(errno));
- break;
- }
- else if(sret == 0)
- {
- WARN("poll timeout\n");
- continue;
- }
-
- auto vec = mRing->getWriteVector();
- if(vec.first.len > 0)
- {
- ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
- if(amt < 0)
- {
- ERR("read failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed reading capture samples: %s",
- strerror(errno));
- break;
- }
- mRing->writeAdvance(amt/frame_size);
- }
- }
-
- return 0;
-}
-
-
-ALCenum OSScapture::open(const ALCchar *name)
-{
- const char *devname{DefaultCapture.c_str()};
- if(!name)
- name = DefaultName;
- else
- {
- if(CaptureDevices.empty())
- ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
-
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- return ALC_INVALID_VALUE;
- devname = iter->device_name.c_str();
- }
-
- mFd = ::open(devname, O_RDONLY);
- if(mFd == -1)
- {
- ERR("Could not open %s: %s\n", devname, strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- int ossFormat{};
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- ossFormat = AFMT_S8;
- break;
- case DevFmtUByte:
- ossFormat = AFMT_U8;
- break;
- case DevFmtShort:
- ossFormat = AFMT_S16_NE;
- break;
- case DevFmtUShort:
- case DevFmtInt:
- case DevFmtUInt:
- case DevFmtFloat:
- ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
-
- int periods{4};
- int numChannels{mDevice->channelsFromFmt()};
- int frameSize{numChannels * mDevice->bytesFromFmt()};
- int ossSpeed{static_cast<int>(mDevice->Frequency)};
- int log2FragmentSize{log2i(mDevice->BufferSize * frameSize / periods)};
-
- /* according to the OSS spec, 16 bytes are the minimum */
- log2FragmentSize = std::max(log2FragmentSize, 4);
- int numFragmentsLogSize{(periods << 16) | log2FragmentSize};
-
- audio_buf_info info;
- const char *err;
-#define CHECKERR(func) if((func) < 0) { \
- err = #func; \
- goto err; \
-}
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
- CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
- if(0)
- {
- err:
- ERR("%s failed: %s\n", err, strerror(errno));
- close(mFd);
- mFd = -1;
- return ALC_INVALID_VALUE;
- }
-#undef CHECKERR
-
- if(mDevice->channelsFromFmt() != numChannels)
- {
- ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
- numChannels);
- close(mFd);
- mFd = -1;
- return ALC_INVALID_VALUE;
- }
-
- if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
- (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
- (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
- {
- ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType), ossFormat);
- close(mFd);
- mFd = -1;
- return ALC_INVALID_VALUE;
- }
-
- mRing = CreateRingBuffer(mDevice->BufferSize, frameSize, false);
- if(!mRing)
- {
- ERR("Ring buffer create failed\n");
- close(mFd);
- mFd = -1;
- return ALC_OUT_OF_MEMORY;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean OSScapture::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create record thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void OSScapture::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
- ERR("Error resetting device: %s\n", strerror(errno));
-}
-
-ALCenum OSScapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint OSScapture::availableSamples()
-{ return mRing->readSpace(); }
-
-} // namespace
-
-
-BackendFactory &OSSBackendFactory::getFactory()
-{
- static OSSBackendFactory factory{};
- return factory;
-}
-
-bool OSSBackendFactory::init()
-{
- if(auto devopt = ConfigValueStr(nullptr, "oss", "device"))
- DefaultPlayback = std::move(*devopt);
- if(auto capopt = ConfigValueStr(nullptr, "oss", "capture"))
- DefaultCapture = std::move(*capopt);
-
- return true;
-}
-
-bool OSSBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void OSSBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
-#ifdef HAVE_STAT
- struct stat buf;
- if(stat(entry.device_name.c_str(), &buf) == 0)
-#endif
- {
- /* Includes null char. */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- }
- };
-
- switch(type)
- {
- case DevProbe::Playback:
- PlaybackDevices.clear();
- ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- CaptureDevices.clear();
- ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new OSSPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new OSScapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/oss.h b/Alc/backends/oss.h
deleted file mode 100644
index 9e63d7b6..00000000
--- a/Alc/backends/oss.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_OSS_H
-#define BACKENDS_OSS_H
-
-#include "backends/base.h"
-
-struct OSSBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_OSS_H */
diff --git a/Alc/backends/portaudio.cpp b/Alc/backends/portaudio.cpp
deleted file mode 100644
index 73e972c5..00000000
--- a/Alc/backends/portaudio.cpp
+++ /dev/null
@@ -1,463 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/portaudio.h"
-
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "ringbuffer.h"
-#include "compat.h"
-
-#include <portaudio.h>
-
-
-namespace {
-
-constexpr ALCchar pa_device[] = "PortAudio Default";
-
-
-#ifdef HAVE_DYNLOAD
-void *pa_handle;
-#define MAKE_FUNC(x) decltype(x) * p##x
-MAKE_FUNC(Pa_Initialize);
-MAKE_FUNC(Pa_Terminate);
-MAKE_FUNC(Pa_GetErrorText);
-MAKE_FUNC(Pa_StartStream);
-MAKE_FUNC(Pa_StopStream);
-MAKE_FUNC(Pa_OpenStream);
-MAKE_FUNC(Pa_CloseStream);
-MAKE_FUNC(Pa_GetDefaultOutputDevice);
-MAKE_FUNC(Pa_GetDefaultInputDevice);
-MAKE_FUNC(Pa_GetStreamInfo);
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define Pa_Initialize pPa_Initialize
-#define Pa_Terminate pPa_Terminate
-#define Pa_GetErrorText pPa_GetErrorText
-#define Pa_StartStream pPa_StartStream
-#define Pa_StopStream pPa_StopStream
-#define Pa_OpenStream pPa_OpenStream
-#define Pa_CloseStream pPa_CloseStream
-#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
-#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
-#define Pa_GetStreamInfo pPa_GetStreamInfo
-#endif
-#endif
-
-
-struct PortPlayback final : public BackendBase {
- PortPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PortPlayback() override;
-
- static int writeCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData);
- int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
- const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- PaStream *mStream{nullptr};
- PaStreamParameters mParams{};
- ALuint mUpdateSize{0u};
-
- DEF_NEWDEL(PortPlayback)
-};
-
-PortPlayback::~PortPlayback()
-{
- PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- mStream = nullptr;
-}
-
-
-int PortPlayback::writeCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData)
-{
- return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
- framesPerBuffer, timeInfo, statusFlags);
-}
-
-int PortPlayback::writeCallback(const void*, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*,
- const PaStreamCallbackFlags)
-{
- lock();
- aluMixData(mDevice, outputBuffer, framesPerBuffer);
- unlock();
- return 0;
-}
-
-
-ALCenum PortPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = pa_device;
- else if(strcmp(name, pa_device) != 0)
- return ALC_INVALID_VALUE;
-
- mUpdateSize = mDevice->UpdateSize;
-
- auto devidopt = ConfigValueInt(nullptr, "port", "device");
- if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
- else mParams.device = Pa_GetDefaultOutputDevice();
- mParams.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
- mParams.hostApiSpecificStreamInfo = nullptr;
-
- mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mParams.sampleFormat = paInt8;
- break;
- case DevFmtUByte:
- mParams.sampleFormat = paUInt8;
- break;
- case DevFmtUShort:
- /* fall-through */
- case DevFmtShort:
- mParams.sampleFormat = paInt16;
- break;
- case DevFmtUInt:
- /* fall-through */
- case DevFmtInt:
- mParams.sampleFormat = paInt32;
- break;
- case DevFmtFloat:
- mParams.sampleFormat = paFloat32;
- break;
- }
-
-retry_open:
- PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize,
- paNoFlag, &PortPlayback::writeCallbackC, this)};
- if(err != paNoError)
- {
- if(mParams.sampleFormat == paFloat32)
- {
- mParams.sampleFormat = paInt16;
- goto retry_open;
- }
- ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-
-}
-
-ALCboolean PortPlayback::reset()
-{
- const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
- mDevice->Frequency = streamInfo->sampleRate;
- mDevice->UpdateSize = mUpdateSize;
-
- if(mParams.sampleFormat == paInt8)
- mDevice->FmtType = DevFmtByte;
- else if(mParams.sampleFormat == paUInt8)
- mDevice->FmtType = DevFmtUByte;
- else if(mParams.sampleFormat == paInt16)
- mDevice->FmtType = DevFmtShort;
- else if(mParams.sampleFormat == paInt32)
- mDevice->FmtType = DevFmtInt;
- else if(mParams.sampleFormat == paFloat32)
- mDevice->FmtType = DevFmtFloat;
- else
- {
- ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat);
- return ALC_FALSE;
- }
-
- if(mParams.channelCount == 2)
- mDevice->FmtChans = DevFmtStereo;
- else if(mParams.channelCount == 1)
- mDevice->FmtChans = DevFmtMono;
- else
- {
- ERR("Unexpected channel count: %u\n", mParams.channelCount);
- return ALC_FALSE;
- }
- SetDefaultChannelOrder(mDevice);
-
- return ALC_TRUE;
-}
-
-ALCboolean PortPlayback::start()
-{
- PaError err{Pa_StartStream(mStream)};
- if(err != paNoError)
- {
- ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void PortPlayback::stop()
-{
- PaError err{Pa_StopStream(mStream)};
- if(err != paNoError)
- ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
-}
-
-
-struct PortCapture final : public BackendBase {
- PortCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PortCapture() override;
-
- static int readCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void *userData);
- int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
- const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- PaStream *mStream{nullptr};
- PaStreamParameters mParams;
-
- RingBufferPtr mRing{nullptr};
-
- DEF_NEWDEL(PortCapture)
-};
-
-PortCapture::~PortCapture()
-{
- PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- mStream = nullptr;
-}
-
-
-int PortCapture::readCallbackC(const void *inputBuffer, void *outputBuffer,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
- const PaStreamCallbackFlags statusFlags, void* userData)
-{
- return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
- framesPerBuffer, timeInfo, statusFlags);
-}
-
-int PortCapture::readCallback(const void *inputBuffer, void*,
- unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*,
- const PaStreamCallbackFlags)
-{
- mRing->write(inputBuffer, framesPerBuffer);
- return 0;
-}
-
-
-ALCenum PortCapture::open(const ALCchar *name)
-{
- if(!name)
- name = pa_device;
- else if(strcmp(name, pa_device) != 0)
- return ALC_INVALID_VALUE;
-
- ALuint samples{mDevice->BufferSize};
- samples = maxu(samples, 100 * mDevice->Frequency / 1000);
- ALsizei frame_size{mDevice->frameSizeFromFmt()};
-
- mRing = CreateRingBuffer(samples, frame_size, false);
- if(!mRing) return ALC_INVALID_VALUE;
-
- auto devidopt = ConfigValueInt(nullptr, "port", "capture");
- if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
- else mParams.device = Pa_GetDefaultOutputDevice();
- mParams.suggestedLatency = 0.0f;
- mParams.hostApiSpecificStreamInfo = nullptr;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mParams.sampleFormat = paInt8;
- break;
- case DevFmtUByte:
- mParams.sampleFormat = paUInt8;
- break;
- case DevFmtShort:
- mParams.sampleFormat = paInt16;
- break;
- case DevFmtInt:
- mParams.sampleFormat = paInt32;
- break;
- case DevFmtFloat:
- mParams.sampleFormat = paFloat32;
- break;
- case DevFmtUInt:
- case DevFmtUShort:
- ERR("%s samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
- mParams.channelCount = mDevice->channelsFromFmt();
-
- PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
- paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)};
- if(err != paNoError)
- {
- ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-
-ALCboolean PortCapture::start()
-{
- PaError err{Pa_StartStream(mStream)};
- if(err != paNoError)
- {
- ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
- return ALC_FALSE;
- }
- return ALC_TRUE;
-}
-
-void PortCapture::stop()
-{
- PaError err{Pa_StopStream(mStream)};
- if(err != paNoError)
- ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
-}
-
-
-ALCuint PortCapture::availableSamples()
-{ return mRing->readSpace(); }
-
-ALCenum PortCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-} // namespace
-
-
-bool PortBackendFactory::init()
-{
- PaError err;
-
-#ifdef HAVE_DYNLOAD
- if(!pa_handle)
- {
-#ifdef _WIN32
-# define PALIB "portaudio.dll"
-#elif defined(__APPLE__) && defined(__MACH__)
-# define PALIB "libportaudio.2.dylib"
-#elif defined(__OpenBSD__)
-# define PALIB "libportaudio.so"
-#else
-# define PALIB "libportaudio.so.2"
-#endif
-
- pa_handle = LoadLib(PALIB);
- if(!pa_handle)
- return false;
-
-#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \
- if(p##f == nullptr) \
- { \
- CloseLib(pa_handle); \
- pa_handle = nullptr; \
- return false; \
- } \
-} while(0)
- LOAD_FUNC(Pa_Initialize);
- LOAD_FUNC(Pa_Terminate);
- LOAD_FUNC(Pa_GetErrorText);
- LOAD_FUNC(Pa_StartStream);
- LOAD_FUNC(Pa_StopStream);
- LOAD_FUNC(Pa_OpenStream);
- LOAD_FUNC(Pa_CloseStream);
- LOAD_FUNC(Pa_GetDefaultOutputDevice);
- LOAD_FUNC(Pa_GetDefaultInputDevice);
- LOAD_FUNC(Pa_GetStreamInfo);
-#undef LOAD_FUNC
-
- if((err=Pa_Initialize()) != paNoError)
- {
- ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
- CloseLib(pa_handle);
- pa_handle = nullptr;
- return false;
- }
- }
-#else
- if((err=Pa_Initialize()) != paNoError)
- {
- ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
- return false;
- }
-#endif
- return true;
-}
-
-bool PortBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void PortBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(pa_device, sizeof(pa_device));
- break;
- }
-}
-
-BackendPtr PortBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new PortPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new PortCapture{device}};
- return nullptr;
-}
-
-BackendFactory &PortBackendFactory::getFactory()
-{
- static PortBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/portaudio.h b/Alc/backends/portaudio.h
deleted file mode 100644
index 082e9020..00000000
--- a/Alc/backends/portaudio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_PORTAUDIO_H
-#define BACKENDS_PORTAUDIO_H
-
-#include "backends/base.h"
-
-struct PortBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_PORTAUDIO_H */
diff --git a/Alc/backends/pulseaudio.cpp b/Alc/backends/pulseaudio.cpp
deleted file mode 100644
index da209c8d..00000000
--- a/Alc/backends/pulseaudio.cpp
+++ /dev/null
@@ -1,1532 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Konstantinos Natsakis <[email protected]>
- * Copyright (C) 2010 by Chris Robinson <[email protected]>
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/pulseaudio.h"
-
-#include <poll.h>
-#include <cstring>
-
-#include <array>
-#include <string>
-#include <vector>
-#include <atomic>
-#include <thread>
-#include <algorithm>
-#include <condition_variable>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "compat.h"
-#include "alexcpt.h"
-
-#include <pulse/pulseaudio.h>
-
-
-namespace {
-
-#ifdef HAVE_DYNLOAD
-#define PULSE_FUNCS(MAGIC) \
- MAGIC(pa_mainloop_new); \
- MAGIC(pa_mainloop_free); \
- MAGIC(pa_mainloop_set_poll_func); \
- MAGIC(pa_mainloop_run); \
- MAGIC(pa_mainloop_get_api); \
- MAGIC(pa_context_new); \
- MAGIC(pa_context_unref); \
- MAGIC(pa_context_get_state); \
- MAGIC(pa_context_disconnect); \
- MAGIC(pa_context_set_state_callback); \
- MAGIC(pa_context_errno); \
- MAGIC(pa_context_connect); \
- MAGIC(pa_context_get_server_info); \
- MAGIC(pa_context_get_sink_info_by_name); \
- MAGIC(pa_context_get_sink_info_list); \
- MAGIC(pa_context_get_source_info_by_name); \
- MAGIC(pa_context_get_source_info_list); \
- MAGIC(pa_stream_new); \
- MAGIC(pa_stream_unref); \
- MAGIC(pa_stream_drop); \
- MAGIC(pa_stream_get_state); \
- MAGIC(pa_stream_peek); \
- MAGIC(pa_stream_write); \
- MAGIC(pa_stream_connect_record); \
- MAGIC(pa_stream_connect_playback); \
- MAGIC(pa_stream_readable_size); \
- MAGIC(pa_stream_writable_size); \
- MAGIC(pa_stream_is_corked); \
- MAGIC(pa_stream_cork); \
- MAGIC(pa_stream_is_suspended); \
- MAGIC(pa_stream_get_device_name); \
- MAGIC(pa_stream_get_latency); \
- MAGIC(pa_stream_set_write_callback); \
- MAGIC(pa_stream_set_buffer_attr); \
- MAGIC(pa_stream_get_buffer_attr); \
- MAGIC(pa_stream_get_sample_spec); \
- MAGIC(pa_stream_get_time); \
- MAGIC(pa_stream_set_read_callback); \
- MAGIC(pa_stream_set_state_callback); \
- MAGIC(pa_stream_set_moved_callback); \
- MAGIC(pa_stream_set_underflow_callback); \
- MAGIC(pa_stream_new_with_proplist); \
- MAGIC(pa_stream_disconnect); \
- MAGIC(pa_stream_set_buffer_attr_callback); \
- MAGIC(pa_stream_begin_write); \
- MAGIC(pa_channel_map_init_auto); \
- MAGIC(pa_channel_map_parse); \
- MAGIC(pa_channel_map_snprint); \
- MAGIC(pa_channel_map_equal); \
- MAGIC(pa_channel_map_superset); \
- MAGIC(pa_operation_get_state); \
- MAGIC(pa_operation_unref); \
- MAGIC(pa_sample_spec_valid); \
- MAGIC(pa_frame_size); \
- MAGIC(pa_strerror); \
- MAGIC(pa_path_get_filename); \
- MAGIC(pa_get_binary_name); \
- MAGIC(pa_xmalloc); \
- MAGIC(pa_xfree);
-
-void *pulse_handle;
-#define MAKE_FUNC(x) decltype(x) * p##x
-PULSE_FUNCS(MAKE_FUNC)
-#undef MAKE_FUNC
-
-#ifndef IN_IDE_PARSER
-#define pa_mainloop_new ppa_mainloop_new
-#define pa_mainloop_free ppa_mainloop_free
-#define pa_mainloop_set_poll_func ppa_mainloop_set_poll_func
-#define pa_mainloop_run ppa_mainloop_run
-#define pa_mainloop_get_api ppa_mainloop_get_api
-#define pa_context_new ppa_context_new
-#define pa_context_unref ppa_context_unref
-#define pa_context_get_state ppa_context_get_state
-#define pa_context_disconnect ppa_context_disconnect
-#define pa_context_set_state_callback ppa_context_set_state_callback
-#define pa_context_errno ppa_context_errno
-#define pa_context_connect ppa_context_connect
-#define pa_context_get_server_info ppa_context_get_server_info
-#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
-#define pa_context_get_sink_info_list ppa_context_get_sink_info_list
-#define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
-#define pa_context_get_source_info_list ppa_context_get_source_info_list
-#define pa_stream_new ppa_stream_new
-#define pa_stream_unref ppa_stream_unref
-#define pa_stream_disconnect ppa_stream_disconnect
-#define pa_stream_drop ppa_stream_drop
-#define pa_stream_set_write_callback ppa_stream_set_write_callback
-#define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
-#define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
-#define pa_stream_get_sample_spec ppa_stream_get_sample_spec
-#define pa_stream_get_time ppa_stream_get_time
-#define pa_stream_set_read_callback ppa_stream_set_read_callback
-#define pa_stream_set_state_callback ppa_stream_set_state_callback
-#define pa_stream_set_moved_callback ppa_stream_set_moved_callback
-#define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
-#define pa_stream_connect_record ppa_stream_connect_record
-#define pa_stream_connect_playback ppa_stream_connect_playback
-#define pa_stream_readable_size ppa_stream_readable_size
-#define pa_stream_writable_size ppa_stream_writable_size
-#define pa_stream_is_corked ppa_stream_is_corked
-#define pa_stream_cork ppa_stream_cork
-#define pa_stream_is_suspended ppa_stream_is_suspended
-#define pa_stream_get_device_name ppa_stream_get_device_name
-#define pa_stream_get_latency ppa_stream_get_latency
-#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
-#define pa_stream_begin_write ppa_stream_begin_write*/
-#define pa_channel_map_init_auto ppa_channel_map_init_auto
-#define pa_channel_map_parse ppa_channel_map_parse
-#define pa_channel_map_snprint ppa_channel_map_snprint
-#define pa_channel_map_equal ppa_channel_map_equal
-#define pa_channel_map_superset ppa_channel_map_superset
-#define pa_operation_get_state ppa_operation_get_state
-#define pa_operation_unref ppa_operation_unref
-#define pa_sample_spec_valid ppa_sample_spec_valid
-#define pa_frame_size ppa_frame_size
-#define pa_strerror ppa_strerror
-#define pa_stream_get_state ppa_stream_get_state
-#define pa_stream_peek ppa_stream_peek
-#define pa_stream_write ppa_stream_write
-#define pa_xfree ppa_xfree
-#define pa_path_get_filename ppa_path_get_filename
-#define pa_get_binary_name ppa_get_binary_name
-#define pa_xmalloc ppa_xmalloc
-#endif /* IN_IDE_PARSER */
-
-#endif
-
-
-constexpr pa_channel_map MonoChanMap{
- 1, {PA_CHANNEL_POSITION_MONO}
-}, StereoChanMap{
- 2, {PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT}
-}, QuadChanMap{
- 4, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
- }
-}, X51ChanMap{
- 6, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
- }
-}, X51RearChanMap{
- 6, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
- }
-}, X61ChanMap{
- 7, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_CENTER,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
- }
-}, X71ChanMap{
- 8, {
- PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
- }
-};
-
-size_t ChannelFromPulse(pa_channel_position_t chan)
-{
- switch(chan)
- {
- case PA_CHANNEL_POSITION_INVALID: break;
- case PA_CHANNEL_POSITION_MONO: return FrontCenter;
- case PA_CHANNEL_POSITION_FRONT_LEFT: return FrontLeft;
- case PA_CHANNEL_POSITION_FRONT_RIGHT: return FrontRight;
- case PA_CHANNEL_POSITION_FRONT_CENTER: return FrontCenter;
- case PA_CHANNEL_POSITION_REAR_CENTER: return BackCenter;
- case PA_CHANNEL_POSITION_REAR_LEFT: return BackLeft;
- case PA_CHANNEL_POSITION_REAR_RIGHT: return BackRight;
- case PA_CHANNEL_POSITION_LFE: return LFE;
- case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: break;
- case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: break;
- case PA_CHANNEL_POSITION_SIDE_LEFT: return SideLeft;
- case PA_CHANNEL_POSITION_SIDE_RIGHT: return SideRight;
- case PA_CHANNEL_POSITION_AUX0: return Aux0;
- case PA_CHANNEL_POSITION_AUX1: return Aux1;
- case PA_CHANNEL_POSITION_AUX2: return Aux2;
- case PA_CHANNEL_POSITION_AUX3: return Aux3;
- case PA_CHANNEL_POSITION_AUX4: return Aux4;
- case PA_CHANNEL_POSITION_AUX5: return Aux5;
- case PA_CHANNEL_POSITION_AUX6: return Aux6;
- case PA_CHANNEL_POSITION_AUX7: return Aux7;
- case PA_CHANNEL_POSITION_AUX8: return Aux8;
- case PA_CHANNEL_POSITION_AUX9: return Aux9;
- case PA_CHANNEL_POSITION_AUX10: return Aux10;
- case PA_CHANNEL_POSITION_AUX11: return Aux11;
- case PA_CHANNEL_POSITION_AUX12: return Aux12;
- case PA_CHANNEL_POSITION_AUX13: return Aux13;
- case PA_CHANNEL_POSITION_AUX14: return Aux14;
- case PA_CHANNEL_POSITION_AUX15: return Aux15;
- case PA_CHANNEL_POSITION_AUX16: break;
- case PA_CHANNEL_POSITION_AUX17: break;
- case PA_CHANNEL_POSITION_AUX18: break;
- case PA_CHANNEL_POSITION_AUX19: break;
- case PA_CHANNEL_POSITION_AUX20: break;
- case PA_CHANNEL_POSITION_AUX21: break;
- case PA_CHANNEL_POSITION_AUX22: break;
- case PA_CHANNEL_POSITION_AUX23: break;
- case PA_CHANNEL_POSITION_AUX24: break;
- case PA_CHANNEL_POSITION_AUX25: break;
- case PA_CHANNEL_POSITION_AUX26: break;
- case PA_CHANNEL_POSITION_AUX27: break;
- case PA_CHANNEL_POSITION_AUX28: break;
- case PA_CHANNEL_POSITION_AUX29: break;
- case PA_CHANNEL_POSITION_AUX30: break;
- case PA_CHANNEL_POSITION_AUX31: break;
- case PA_CHANNEL_POSITION_TOP_CENTER: break;
- case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return UpperFrontLeft;
- case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return UpperFrontRight;
- case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: break;
- case PA_CHANNEL_POSITION_TOP_REAR_LEFT: return UpperBackLeft;
- case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return UpperBackRight;
- case PA_CHANNEL_POSITION_TOP_REAR_CENTER: break;
- case PA_CHANNEL_POSITION_MAX: break;
- }
- throw al::backend_exception{ALC_INVALID_VALUE, "Unexpected channel enum %d", chan};
-}
-
-void SetChannelOrderFromMap(ALCdevice *device, const pa_channel_map &chanmap)
-{
- device->RealOut.ChannelIndex.fill(-1);
- for(int i{0};i < chanmap.channels;++i)
- device->RealOut.ChannelIndex[ChannelFromPulse(chanmap.map[i])] = i;
-}
-
-
-/* *grumble* Don't use enums for bitflags. */
-inline pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
-{ return pa_stream_flags_t(int(lhs) | int(rhs)); }
-inline pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
-{
- lhs = pa_stream_flags_t(int(lhs) | int(rhs));
- return lhs;
-}
-inline pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_flags_t rhs)
-{
- lhs = pa_context_flags_t(int(lhs) | int(rhs));
- return lhs;
-}
-
-inline pa_stream_flags_t& operator&=(pa_stream_flags_t &lhs, int rhs)
-{
- lhs = pa_stream_flags_t(int(lhs) & rhs);
- return lhs;
-}
-
-
-/* Global flags and properties */
-pa_context_flags_t pulse_ctx_flags;
-
-pa_mainloop *pulse_mainloop{nullptr};
-
-std::mutex pulse_lock;
-std::condition_variable pulse_condvar;
-
-int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata)
-{
- auto plock = static_cast<std::unique_lock<std::mutex>*>(userdata);
- plock->unlock();
- int r{poll(ufds, nfds, timeout)};
- plock->lock();
- return r;
-}
-
-int pulse_mainloop_thread()
-{
- SetRTPriority();
-
- std::unique_lock<std::mutex> plock{pulse_lock};
- pulse_mainloop = pa_mainloop_new();
-
- pa_mainloop_set_poll_func(pulse_mainloop, pulse_poll_func, &plock);
- pulse_condvar.notify_all();
-
- int ret{};
- pa_mainloop_run(pulse_mainloop, &ret);
-
- pa_mainloop_free(pulse_mainloop);
- pulse_mainloop = nullptr;
-
- return ret;
-}
-
-
-/* PulseAudio Event Callbacks */
-void context_state_callback(pa_context *context, void* /*pdata*/)
-{
- pa_context_state_t state{pa_context_get_state(context)};
- if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
- pulse_condvar.notify_all();
-}
-
-void stream_state_callback(pa_stream *stream, void* /*pdata*/)
-{
- pa_stream_state_t state{pa_stream_get_state(stream)};
- if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
- pulse_condvar.notify_all();
-}
-
-void stream_success_callback(pa_stream* /*stream*/, int /*success*/, void* /*pdata*/)
-{
- pulse_condvar.notify_all();
-}
-
-void wait_for_operation(pa_operation *op, std::unique_lock<std::mutex> &plock)
-{
- if(op)
- {
- while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
- pulse_condvar.wait(plock);
- pa_operation_unref(op);
- }
-}
-
-
-pa_context *connect_context(std::unique_lock<std::mutex> &plock)
-{
- const char *name{"OpenAL Soft"};
-
- const PathNamePair &binname = GetProcBinary();
- if(!binname.fname.empty())
- name = binname.fname.c_str();
-
- if(UNLIKELY(!pulse_mainloop))
- {
- std::thread{pulse_mainloop_thread}.detach();
- while(!pulse_mainloop)
- pulse_condvar.wait(plock);
- }
-
- pa_context *context{pa_context_new(pa_mainloop_get_api(pulse_mainloop), name)};
- if(!context) throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_context_new() failed"};
-
- pa_context_set_state_callback(context, context_state_callback, nullptr);
-
- int err;
- if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0)
- {
- pa_context_state_t state;
- while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
- {
- if(!PA_CONTEXT_IS_GOOD(state))
- {
- err = pa_context_errno(context);
- if(err > 0) err = -err;
- break;
- }
-
- pulse_condvar.wait(plock);
- }
- }
- pa_context_set_state_callback(context, nullptr, nullptr);
-
- if(err < 0)
- {
- pa_context_unref(context);
- throw al::backend_exception{ALC_INVALID_VALUE, "Context did not connect (%s)",
- pa_strerror(err)};
- }
-
- return context;
-}
-
-
-void pulse_close(pa_context *context, pa_stream *stream)
-{
- std::lock_guard<std::mutex> _{pulse_lock};
- if(stream)
- {
- pa_stream_set_state_callback(stream, nullptr, nullptr);
- pa_stream_set_moved_callback(stream, nullptr, nullptr);
- pa_stream_set_write_callback(stream, nullptr, nullptr);
- pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- }
-
- pa_context_disconnect(context);
- pa_context_unref(context);
-}
-
-
-struct DevMap {
- std::string name;
- std::string device_name;
-};
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-pa_stream *pulse_connect_stream(const char *device_name, std::unique_lock<std::mutex> &plock,
- pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
- pa_channel_map *chanmap, BackendType type)
-{
- const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
- pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)};
- if(!stream)
- throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_stream_new() failed (%s)",
- pa_strerror(pa_context_errno(context))};
-
- pa_stream_set_state_callback(stream, stream_state_callback, nullptr);
-
- int err{(type==BackendType::Playback) ?
- pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) :
- pa_stream_connect_record(stream, device_name, attr, flags)};
- if(err < 0)
- {
- pa_stream_unref(stream);
- throw al::backend_exception{ALC_INVALID_VALUE, "%s did not connect (%s)", stream_id,
- pa_strerror(err)};
- }
-
- pa_stream_state_t state;
- while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
- {
- if(!PA_STREAM_IS_GOOD(state))
- {
- int err{pa_context_errno(context)};
- pa_stream_unref(stream);
- throw al::backend_exception{ALC_INVALID_VALUE, "%s did not get ready (%s)", stream_id,
- pa_strerror(err)};
- }
-
- pulse_condvar.wait(plock);
- }
- pa_stream_set_state_callback(stream, nullptr, nullptr);
-
- return stream;
-}
-
-
-void device_sink_callback(pa_context*, const pa_sink_info *info, int eol, void*)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
-
- /* Skip this device is if it's already in the list. */
- if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [info](const DevMap &entry) -> bool
- { return entry.device_name == info->name; }
- ) != PlaybackDevices.cend())
- return;
-
- /* Make sure the display name (description) is unique. Append a number
- * counter as needed.
- */
- int count{1};
- std::string newname{info->description};
- while(checkName(PlaybackDevices, newname))
- {
- newname = info->description;
- newname += " #";
- newname += std::to_string(++count);
- }
- PlaybackDevices.emplace_back(DevMap{std::move(newname), info->name});
- DevMap &newentry = PlaybackDevices.back();
-
- TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
-}
-
-void probePlaybackDevices()
-{
- PlaybackDevices.clear();
-
- try {
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_context *context{connect_context(plock)};
-
- const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
- PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
-
- pa_sample_spec spec{};
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 2;
-
- pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec,
- nullptr, BackendType::Playback)};
- pa_operation *op{pa_context_get_sink_info_by_name(context,
- pa_stream_get_device_name(stream), device_sink_callback, nullptr)};
- wait_for_operation(op, plock);
-
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- stream = nullptr;
-
- op = pa_context_get_sink_info_list(context, device_sink_callback, nullptr);
- wait_for_operation(op, plock);
-
- pa_context_disconnect(context);
- pa_context_unref(context);
- }
- catch(std::exception &e) {
- ERR("Error enumerating devices: %s\n", e.what());
- }
-}
-
-
-void device_source_callback(pa_context*, const pa_source_info *info, int eol, void*)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
-
- /* Skip this device is if it's already in the list. */
- if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [info](const DevMap &entry) -> bool
- { return entry.device_name == info->name; }
- ) != CaptureDevices.cend())
- return;
-
- /* Make sure the display name (description) is unique. Append a number
- * counter as needed.
- */
- int count{1};
- std::string newname{info->description};
- while(checkName(CaptureDevices, newname))
- {
- newname = info->description;
- newname += " #";
- newname += std::to_string(++count);
- }
- CaptureDevices.emplace_back(DevMap{std::move(newname), info->name});
- DevMap &newentry = CaptureDevices.back();
-
- TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
-}
-
-void probeCaptureDevices()
-{
- CaptureDevices.clear();
-
- try {
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_context *context{connect_context(plock)};
-
- const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
- PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
-
- pa_sample_spec spec{};
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 1;
-
- pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec, nullptr,
- BackendType::Capture)};
- pa_operation *op{pa_context_get_source_info_by_name(context,
- pa_stream_get_device_name(stream), device_source_callback, nullptr)};
- wait_for_operation(op, plock);
-
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- stream = nullptr;
-
- op = pa_context_get_source_info_list(context, device_source_callback, nullptr);
- wait_for_operation(op, plock);
-
- pa_context_disconnect(context);
- pa_context_unref(context);
- }
- catch(std::exception &e) {
- ERR("Error enumerating devices: %s\n", e.what());
- }
-}
-
-
-struct PulsePlayback final : public BackendBase {
- PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PulsePlayback() override;
-
- static void bufferAttrCallbackC(pa_stream *stream, void *pdata);
- void bufferAttrCallback(pa_stream *stream);
-
- static void contextStateCallbackC(pa_context *context, void *pdata);
- void contextStateCallback(pa_context *context);
-
- static void streamStateCallbackC(pa_stream *stream, void *pdata);
- void streamStateCallback(pa_stream *stream);
-
- static void streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata);
- void streamWriteCallback(pa_stream *stream, size_t nbytes);
-
- static void sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
- void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol);
-
- static void sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
- void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol);
-
- static void streamMovedCallbackC(pa_stream *stream, void *pdata);
- void streamMovedCallback(pa_stream *stream);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- ClockLatency getClockLatency() override;
- void lock() override;
- void unlock() override;
-
- std::string mDeviceName;
-
- pa_buffer_attr mAttr;
- pa_sample_spec mSpec;
-
- pa_stream *mStream{nullptr};
- pa_context *mContext{nullptr};
-
- ALuint mFrameSize{0u};
-
- DEF_NEWDEL(PulsePlayback)
-};
-
-PulsePlayback::~PulsePlayback()
-{
- if(!mContext)
- return;
-
- pulse_close(mContext, mStream);
- mContext = nullptr;
- mStream = nullptr;
-}
-
-
-void PulsePlayback::bufferAttrCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->bufferAttrCallback(stream); }
-
-void PulsePlayback::bufferAttrCallback(pa_stream *stream)
-{
- /* FIXME: Update the device's UpdateSize (and/or BufferSize) using the new
- * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
- * property, which probably shouldn't change between device resets. But
- * leaving it alone means ALC_REFRESH will be off.
- */
- mAttr = *(pa_stream_get_buffer_attr(stream));
- TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
-}
-
-void PulsePlayback::contextStateCallbackC(pa_context *context, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->contextStateCallback(context); }
-
-void PulsePlayback::contextStateCallback(pa_context *context)
-{
- if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
- {
- ERR("Received context failure!\n");
- aluHandleDisconnect(mDevice, "Playback state failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulsePlayback::streamStateCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->streamStateCallback(stream); }
-
-void PulsePlayback::streamStateCallback(pa_stream *stream)
-{
- if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
- {
- ERR("Received stream failure!\n");
- aluHandleDisconnect(mDevice, "Playback stream failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulsePlayback::streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->streamWriteCallback(stream, nbytes); }
-
-void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes)
-{
- void *buf{pa_xmalloc(nbytes)};
- aluMixData(mDevice, buf, nbytes/mFrameSize);
-
- int ret{pa_stream_write(stream, buf, nbytes, pa_xfree, 0, PA_SEEK_RELATIVE)};
- if(UNLIKELY(ret != PA_OK))
- ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
-}
-
-void PulsePlayback::sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); }
-
-void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int eol)
-{
- struct ChannelMap {
- DevFmtChannels chans;
- pa_channel_map map;
- };
- static constexpr std::array<ChannelMap,7> chanmaps{{
- { DevFmtX71, X71ChanMap },
- { DevFmtX61, X61ChanMap },
- { DevFmtX51, X51ChanMap },
- { DevFmtX51Rear, X51RearChanMap },
- { DevFmtQuad, QuadChanMap },
- { DevFmtStereo, StereoChanMap },
- { DevFmtMono, MonoChanMap }
- }};
-
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
-
- auto chanmap = std::find_if(chanmaps.cbegin(), chanmaps.cend(),
- [info](const ChannelMap &chanmap) -> bool
- { return pa_channel_map_superset(&info->channel_map, &chanmap.map); }
- );
- if(chanmap != chanmaps.cend())
- {
- if(!mDevice->Flags.get<ChannelsRequest>())
- mDevice->FmtChans = chanmap->chans;
- }
- else
- {
- char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{};
- pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
- WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
- }
-
- if(info->active_port)
- TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
- mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
- info->active_port && strcmp(info->active_port->name, "analog-output-headphones") == 0);
-}
-
-void PulsePlayback::sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); }
-
-void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int eol)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
- mDevice->DeviceName = info->description;
-}
-
-void PulsePlayback::streamMovedCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); }
-
-void PulsePlayback::streamMovedCallback(pa_stream *stream)
-{
- mDeviceName = pa_stream_get_device_name(stream);
- TRACE("Stream moved to %s\n", mDeviceName.c_str());
-}
-
-
-ALCenum PulsePlayback::open(const ALCchar *name)
-{
- const char *pulse_name{nullptr};
- const char *dev_name{nullptr};
-
- if(name)
- {
- if(PlaybackDevices.empty())
- probePlaybackDevices();
-
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == PlaybackDevices.cend())
- throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
- pulse_name = iter->device_name.c_str();
- dev_name = iter->name.c_str();
- }
-
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- mContext = connect_context(plock);
- pa_context_set_state_callback(mContext, &PulsePlayback::contextStateCallbackC, this);
-
- pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS};
- if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
- flags |= PA_STREAM_DONT_MOVE;
-
- pa_sample_spec spec{};
- spec.format = PA_SAMPLE_S16NE;
- spec.rate = 44100;
- spec.channels = 2;
-
- if(!pulse_name)
- {
- pulse_name = getenv("ALSOFT_PULSE_DEFAULT");
- if(pulse_name && !pulse_name[0]) pulse_name = nullptr;
- }
- TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr,
- BackendType::Playback);
-
- pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
- mFrameSize = pa_frame_size(pa_stream_get_sample_spec(mStream));
-
- mDeviceName = pa_stream_get_device_name(mStream);
- if(!dev_name)
- {
- pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
- &PulsePlayback::sinkNameCallbackC, this)};
- wait_for_operation(op, plock);
- }
- else
- mDevice->DeviceName = dev_name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean PulsePlayback::reset()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- if(mStream)
- {
- pa_stream_set_state_callback(mStream, nullptr, nullptr);
- pa_stream_set_moved_callback(mStream, nullptr, nullptr);
- pa_stream_set_write_callback(mStream, nullptr, nullptr);
- pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
- pa_stream_disconnect(mStream);
- pa_stream_unref(mStream);
- mStream = nullptr;
- }
-
- pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
- &PulsePlayback::sinkInfoCallbackC, this)};
- wait_for_operation(op, plock);
-
- pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
- PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS};
- if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
- flags |= PA_STREAM_DONT_MOVE;
- if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", 0))
- {
- /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some
- * reason. So if the user wants to adjust the overall device latency,
- * we can't ask to get write signals as soon as minreq is reached.
- */
- flags &= ~PA_STREAM_EARLY_REQUESTS;
- flags |= PA_STREAM_ADJUST_LATENCY;
- }
- if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", 0) ||
- !mDevice->Flags.get<FrequencyRequest>())
- flags |= PA_STREAM_FIX_RATE;
-
- pa_channel_map chanmap{};
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- chanmap = MonoChanMap;
- break;
- case DevFmtAmbi3D:
- mDevice->FmtChans = DevFmtStereo;
- /*fall-through*/
- case DevFmtStereo:
- chanmap = StereoChanMap;
- break;
- case DevFmtQuad:
- chanmap = QuadChanMap;
- break;
- case DevFmtX51:
- chanmap = X51ChanMap;
- break;
- case DevFmtX51Rear:
- chanmap = X51RearChanMap;
- break;
- case DevFmtX61:
- chanmap = X61ChanMap;
- break;
- case DevFmtX71:
- chanmap = X71ChanMap;
- break;
- }
- SetChannelOrderFromMap(mDevice, chanmap);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- /* fall-through */
- case DevFmtUByte:
- mSpec.format = PA_SAMPLE_U8;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- mSpec.format = PA_SAMPLE_S16NE;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- /* fall-through */
- case DevFmtInt:
- mSpec.format = PA_SAMPLE_S32NE;
- break;
- case DevFmtFloat:
- mSpec.format = PA_SAMPLE_FLOAT32NE;
- break;
- }
- mSpec.rate = mDevice->Frequency;
- mSpec.channels = mDevice->channelsFromFmt();
- if(pa_sample_spec_valid(&mSpec) == 0)
- throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample spec"};
-
- mAttr.maxlength = -1;
- mAttr.tlength = mDevice->BufferSize * pa_frame_size(&mSpec);
- mAttr.prebuf = 0;
- mAttr.minreq = mDevice->UpdateSize * pa_frame_size(&mSpec);
- mAttr.fragsize = -1;
-
- mStream = pulse_connect_stream(mDeviceName.c_str(), plock, mContext, flags, &mAttr, &mSpec,
- &chanmap, BackendType::Playback);
-
- pa_stream_set_state_callback(mStream, &PulsePlayback::streamStateCallbackC, this);
- pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
-
- mSpec = *(pa_stream_get_sample_spec(mStream));
- mFrameSize = pa_frame_size(&mSpec);
-
- if(mDevice->Frequency != mSpec.rate)
- {
- /* Server updated our playback rate, so modify the buffer attribs
- * accordingly.
- */
- const auto scale = static_cast<double>(mSpec.rate) / mDevice->Frequency;
- const ALuint perlen{static_cast<ALuint>(clampd(scale*mDevice->UpdateSize + 0.5, 64.0,
- 8192.0))};
- const ALuint buflen{static_cast<ALuint>(clampd(scale*mDevice->BufferSize + 0.5, perlen*2,
- std::numeric_limits<int>::max()/mFrameSize))};
-
- mAttr.maxlength = -1;
- mAttr.tlength = buflen * mFrameSize;
- mAttr.prebuf = 0;
- mAttr.minreq = perlen * mFrameSize;
-
- op = pa_stream_set_buffer_attr(mStream, &mAttr, stream_success_callback, nullptr);
- wait_for_operation(op, plock);
-
- mDevice->Frequency = mSpec.rate;
- }
-
- pa_stream_set_buffer_attr_callback(mStream, &PulsePlayback::bufferAttrCallbackC, this);
- bufferAttrCallback(mStream);
-
- mDevice->BufferSize = mAttr.tlength / mFrameSize;
- mDevice->UpdateSize = mAttr.minreq / mFrameSize;
-
- /* HACK: prebuf should be 0 as that's what we set it to. However on some
- * systems it comes back as non-0, so we have to make sure the device will
- * write enough audio to start playback. The lack of manual start control
- * may have unintended consequences, but it's better than not starting at
- * all.
- */
- if(mAttr.prebuf != 0)
- {
- ALuint len{mAttr.prebuf / mFrameSize};
- if(len <= mDevice->BufferSize)
- ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
- len, mAttr.prebuf, mDevice->BufferSize);
- }
-
- return ALC_TRUE;
-}
-
-ALCboolean PulsePlayback::start()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
- pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
-
- return ALC_TRUE;
-}
-
-void PulsePlayback::stop()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- pa_stream_set_write_callback(mStream, nullptr, nullptr);
- pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
-}
-
-
-ClockLatency PulsePlayback::getClockLatency()
-{
- ClockLatency ret;
- pa_usec_t latency;
- int neg, err;
-
- { std::lock_guard<std::mutex> _{pulse_lock};
- ret.ClockTime = GetDeviceClockTime(mDevice);
- err = pa_stream_get_latency(mStream, &latency, &neg);
- }
-
- if(UNLIKELY(err != 0))
- {
- /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
- * after starting the stream and no timing info has been received from
- * the server yet. Should we wait, possibly stalling the app, or give a
- * dummy value? Either way, it shouldn't be 0. */
- if(err != -PA_ERR_NODATA)
- ERR("Failed to get stream latency: 0x%x\n", err);
- latency = 0;
- neg = 0;
- }
- else if(UNLIKELY(neg))
- latency = 0;
- ret.Latency = std::chrono::microseconds{latency};
-
- return ret;
-}
-
-
-void PulsePlayback::lock()
-{ pulse_lock.lock(); }
-
-void PulsePlayback::unlock()
-{ pulse_lock.unlock(); }
-
-
-struct PulseCapture final : public BackendBase {
- PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PulseCapture() override;
-
- static void contextStateCallbackC(pa_context *context, void *pdata);
- void contextStateCallback(pa_context *context);
-
- static void streamStateCallbackC(pa_stream *stream, void *pdata);
- void streamStateCallback(pa_stream *stream);
-
- static void sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata);
- void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol);
-
- static void streamMovedCallbackC(pa_stream *stream, void *pdata);
- void streamMovedCallback(pa_stream *stream);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
- ClockLatency getClockLatency() override;
- void lock() override;
- void unlock() override;
-
- std::string mDeviceName;
-
- ALCuint mLastReadable{0u};
- al::byte mSilentVal{};
-
- al::span<const al::byte> mCapBuffer;
- ssize_t mCapLen{0};
-
- pa_buffer_attr mAttr{};
- pa_sample_spec mSpec{};
-
- pa_stream *mStream{nullptr};
- pa_context *mContext{nullptr};
-
- DEF_NEWDEL(PulseCapture)
-};
-
-PulseCapture::~PulseCapture()
-{
- if(!mContext)
- return;
-
- pulse_close(mContext, mStream);
- mContext = nullptr;
- mStream = nullptr;
-}
-
-void PulseCapture::contextStateCallbackC(pa_context *context, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->contextStateCallback(context); }
-
-void PulseCapture::contextStateCallback(pa_context *context)
-{
- if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
- {
- ERR("Received context failure!\n");
- aluHandleDisconnect(mDevice, "Capture state failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulseCapture::streamStateCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); }
-
-void PulseCapture::streamStateCallback(pa_stream *stream)
-{
- if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
- {
- ERR("Received stream failure!\n");
- aluHandleDisconnect(mDevice, "Capture stream failure");
- }
- pulse_condvar.notify_all();
-}
-
-void PulseCapture::sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); }
-
-void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, int eol)
-{
- if(eol)
- {
- pulse_condvar.notify_all();
- return;
- }
- mDevice->DeviceName = info->description;
-}
-
-void PulseCapture::streamMovedCallbackC(pa_stream *stream, void *pdata)
-{ static_cast<PulseCapture*>(pdata)->streamMovedCallback(stream); }
-
-void PulseCapture::streamMovedCallback(pa_stream *stream)
-{
- mDeviceName = pa_stream_get_device_name(stream);
- TRACE("Stream moved to %s\n", mDeviceName.c_str());
-}
-
-
-ALCenum PulseCapture::open(const ALCchar *name)
-{
- const char *pulse_name{nullptr};
- if(name)
- {
- if(CaptureDevices.empty())
- probeCaptureDevices();
-
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name; }
- );
- if(iter == CaptureDevices.cend())
- throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
- pulse_name = iter->device_name.c_str();
- mDevice->DeviceName = iter->name;
- }
-
- std::unique_lock<std::mutex> plock{pulse_lock};
-
- mContext = connect_context(plock);
- pa_context_set_state_callback(mContext, &PulseCapture::contextStateCallbackC, this);
-
- pa_channel_map chanmap{};
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- chanmap = MonoChanMap;
- break;
- case DevFmtStereo:
- chanmap = StereoChanMap;
- break;
- case DevFmtQuad:
- chanmap = QuadChanMap;
- break;
- case DevFmtX51:
- chanmap = X51ChanMap;
- break;
- case DevFmtX51Rear:
- chanmap = X51RearChanMap;
- break;
- case DevFmtX61:
- chanmap = X61ChanMap;
- break;
- case DevFmtX71:
- chanmap = X71ChanMap;
- break;
- case DevFmtAmbi3D:
- throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
- DevFmtChannelsString(mDevice->FmtChans)};
- }
- SetChannelOrderFromMap(mDevice, chanmap);
-
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- mSilentVal = al::byte(0x80);
- mSpec.format = PA_SAMPLE_U8;
- break;
- case DevFmtShort:
- mSpec.format = PA_SAMPLE_S16NE;
- break;
- case DevFmtInt:
- mSpec.format = PA_SAMPLE_S32NE;
- break;
- case DevFmtFloat:
- mSpec.format = PA_SAMPLE_FLOAT32NE;
- break;
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
- DevFmtTypeString(mDevice->FmtType)};
- }
- mSpec.rate = mDevice->Frequency;
- mSpec.channels = mDevice->channelsFromFmt();
- if(pa_sample_spec_valid(&mSpec) == 0)
- throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample format"};
-
- ALuint samples{mDevice->BufferSize};
- samples = maxu(samples, 100 * mDevice->Frequency / 1000);
-
- mAttr.minreq = -1;
- mAttr.prebuf = -1;
- mAttr.maxlength = samples * pa_frame_size(&mSpec);
- mAttr.tlength = -1;
- mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * pa_frame_size(&mSpec);
-
- pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY};
- if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
- flags |= PA_STREAM_DONT_MOVE;
-
- TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, &mAttr, &mSpec, &chanmap,
- BackendType::Capture);
-
- pa_stream_set_moved_callback(mStream, &PulseCapture::streamMovedCallbackC, this);
- pa_stream_set_state_callback(mStream, &PulseCapture::streamStateCallbackC, this);
-
- mDeviceName = pa_stream_get_device_name(mStream);
- if(mDevice->DeviceName.empty())
- {
- pa_operation *op{pa_context_get_source_info_by_name(mContext, mDeviceName.c_str(),
- &PulseCapture::sourceNameCallbackC, this)};
- wait_for_operation(op, plock);
- }
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean PulseCapture::start()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
- pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
- return ALC_TRUE;
-}
-
-void PulseCapture::stop()
-{
- std::unique_lock<std::mutex> plock{pulse_lock};
- pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, nullptr)};
- wait_for_operation(op, plock);
-}
-
-ALCenum PulseCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
-{
- al::span<al::byte> dstbuf{static_cast<al::byte*>(buffer), samples * pa_frame_size(&mSpec)};
-
- /* Capture is done in fragment-sized chunks, so we loop until we get all
- * that's available */
- mLastReadable -= dstbuf.size();
- std::lock_guard<std::mutex> _{pulse_lock};
- while(!dstbuf.empty())
- {
- if(mCapBuffer.empty())
- {
- if(UNLIKELY(!mDevice->Connected.load(std::memory_order_acquire)))
- break;
- const pa_stream_state_t state{pa_stream_get_state(mStream)};
- if(UNLIKELY(!PA_STREAM_IS_GOOD(state)))
- {
- aluHandleDisconnect(mDevice, "Bad capture state: %u", state);
- break;
- }
- const void *capbuf;
- size_t caplen;
- if(UNLIKELY(pa_stream_peek(mStream, &capbuf, &caplen) < 0))
- {
- aluHandleDisconnect(mDevice, "Failed retrieving capture samples: %s",
- pa_strerror(pa_context_errno(mContext)));
- break;
- }
- if(caplen == 0) break;
- if(UNLIKELY(!capbuf))
- mCapLen = -static_cast<ssize_t>(caplen);
- else
- mCapLen = static_cast<ssize_t>(caplen);
- mCapBuffer = {static_cast<const al::byte*>(capbuf), caplen};
- }
-
- const size_t rem{minz(dstbuf.size(), mCapBuffer.size())};
- if(UNLIKELY(mCapLen < 0))
- std::fill_n(dstbuf.begin(), rem, mSilentVal);
- else
- std::copy_n(mCapBuffer.begin(), rem, dstbuf.begin());
- dstbuf = dstbuf.subspan(rem);
- mCapBuffer = mCapBuffer.subspan(rem);
-
- if(mCapBuffer.empty())
- {
- pa_stream_drop(mStream);
- mCapLen = 0;
- }
- }
- if(!dstbuf.empty())
- std::fill(dstbuf.begin(), dstbuf.end(), mSilentVal);
-
- return ALC_NO_ERROR;
-}
-
-ALCuint PulseCapture::availableSamples()
-{
- size_t readable{mCapBuffer.size()};
-
- if(mDevice->Connected.load(std::memory_order_acquire))
- {
- std::lock_guard<std::mutex> _{pulse_lock};
- size_t got{pa_stream_readable_size(mStream)};
- if(static_cast<ssize_t>(got) < 0)
- {
- ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
- aluHandleDisconnect(mDevice, "Failed getting readable size: %s", pa_strerror(got));
- }
- else
- {
- const auto caplen = static_cast<size_t>(std::abs(mCapLen));
- if(got > caplen) readable += got - caplen;
- }
- }
-
- readable = std::min<size_t>(readable, std::numeric_limits<ALCuint>::max());
- mLastReadable = std::max(mLastReadable, static_cast<ALCuint>(readable));
- return mLastReadable / pa_frame_size(&mSpec);
-}
-
-
-ClockLatency PulseCapture::getClockLatency()
-{
- ClockLatency ret;
- pa_usec_t latency;
- int neg, err;
-
- { std::lock_guard<std::mutex> _{pulse_lock};
- ret.ClockTime = GetDeviceClockTime(mDevice);
- err = pa_stream_get_latency(mStream, &latency, &neg);
- }
-
- if(UNLIKELY(err != 0))
- {
- ERR("Failed to get stream latency: 0x%x\n", err);
- latency = 0;
- neg = 0;
- }
- else if(UNLIKELY(neg))
- latency = 0;
- ret.Latency = std::chrono::microseconds{latency};
-
- return ret;
-}
-
-
-void PulseCapture::lock()
-{ pulse_lock.lock(); }
-
-void PulseCapture::unlock()
-{ pulse_lock.unlock(); }
-
-} // namespace
-
-
-bool PulseBackendFactory::init()
-{
-#ifdef HAVE_DYNLOAD
- if(!pulse_handle)
- {
- bool ret{true};
- std::string missing_funcs;
-
-#ifdef _WIN32
-#define PALIB "libpulse-0.dll"
-#elif defined(__APPLE__) && defined(__MACH__)
-#define PALIB "libpulse.0.dylib"
-#else
-#define PALIB "libpulse.so.0"
-#endif
- pulse_handle = LoadLib(PALIB);
- if(!pulse_handle)
- {
- WARN("Failed to load %s\n", PALIB);
- return false;
- }
-
-#define LOAD_FUNC(x) do { \
- p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \
- if(!(p##x)) { \
- ret = false; \
- missing_funcs += "\n" #x; \
- } \
-} while(0)
- PULSE_FUNCS(LOAD_FUNC)
-#undef LOAD_FUNC
-
- if(!ret)
- {
- WARN("Missing expected functions:%s\n", missing_funcs.c_str());
- CloseLib(pulse_handle);
- pulse_handle = nullptr;
- return false;
- }
- }
-#endif /* HAVE_DYNLOAD */
-
- pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
- if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", 1))
- pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
-
- try {
- std::unique_lock<std::mutex> plock{pulse_lock};
- pa_context *context{connect_context(plock)};
- pa_context_disconnect(context);
- pa_context_unref(context);
- return true;
- }
- catch(...) {
- return false;
- }
-}
-
-bool PulseBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void PulseBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
- switch(type)
- {
- case DevProbe::Playback:
- probePlaybackDevices();
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- probeCaptureDevices();
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr PulseBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new PulsePlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new PulseCapture{device}};
- return nullptr;
-}
-
-BackendFactory &PulseBackendFactory::getFactory()
-{
- static PulseBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/pulseaudio.h b/Alc/backends/pulseaudio.h
deleted file mode 100644
index 40f3e305..00000000
--- a/Alc/backends/pulseaudio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_PULSEAUDIO_H
-#define BACKENDS_PULSEAUDIO_H
-
-#include "backends/base.h"
-
-class PulseBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_PULSEAUDIO_H */
diff --git a/Alc/backends/qsa.cpp b/Alc/backends/qsa.cpp
deleted file mode 100644
index 64ed53aa..00000000
--- a/Alc/backends/qsa.cpp
+++ /dev/null
@@ -1,953 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011-2013 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/qsa.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sched.h>
-#include <errno.h>
-#include <memory.h>
-#include <poll.h>
-
-#include <thread>
-#include <memory>
-#include <algorithm>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "threads.h"
-
-#include <sys/asoundlib.h>
-#include <sys/neutrino.h>
-
-
-namespace {
-
-struct qsa_data {
- snd_pcm_t* pcmHandle{nullptr};
- int audio_fd{-1};
-
- snd_pcm_channel_setup_t csetup{};
- snd_pcm_channel_params_t cparams{};
-
- ALvoid* buffer{nullptr};
- ALsizei size{0};
-
- std::atomic<ALenum> mKillNow{AL_TRUE};
- std::thread mThread;
-};
-
-struct DevMap {
- ALCchar* name;
- int card;
- int dev;
-};
-
-al::vector<DevMap> DeviceNameMap;
-al::vector<DevMap> CaptureNameMap;
-
-constexpr ALCchar qsaDevice[] = "QSA Default";
-
-constexpr struct {
- int32_t format;
-} formatlist[] = {
- {SND_PCM_SFMT_FLOAT_LE},
- {SND_PCM_SFMT_S32_LE},
- {SND_PCM_SFMT_U32_LE},
- {SND_PCM_SFMT_S16_LE},
- {SND_PCM_SFMT_U16_LE},
- {SND_PCM_SFMT_S8},
- {SND_PCM_SFMT_U8},
- {0},
-};
-
-constexpr struct {
- int32_t rate;
-} ratelist[] = {
- {192000},
- {176400},
- {96000},
- {88200},
- {48000},
- {44100},
- {32000},
- {24000},
- {22050},
- {16000},
- {12000},
- {11025},
- {8000},
- {0},
-};
-
-constexpr struct {
- int32_t channels;
-} channellist[] = {
- {8},
- {7},
- {6},
- {4},
- {2},
- {1},
- {0},
-};
-
-void deviceList(int type, al::vector<DevMap> *devmap)
-{
- snd_ctl_t* handle;
- snd_pcm_info_t pcminfo;
- int max_cards, card, err, dev;
- DevMap entry;
- char name[1024];
- snd_ctl_hw_info info;
-
- max_cards = snd_cards();
- if(max_cards < 0)
- return;
-
- std::for_each(devmap->begin(), devmap->end(),
- [](const DevMap &entry) -> void
- { free(entry.name); }
- );
- devmap->clear();
-
- entry.name = strdup(qsaDevice);
- entry.card = 0;
- entry.dev = 0;
- devmap->push_back(entry);
-
- for(card = 0;card < max_cards;card++)
- {
- if((err=snd_ctl_open(&handle, card)) < 0)
- continue;
-
- if((err=snd_ctl_hw_info(handle, &info)) < 0)
- {
- snd_ctl_close(handle);
- continue;
- }
-
- for(dev = 0;dev < (int)info.pcmdevs;dev++)
- {
- if((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
- continue;
-
- if((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
- (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
- {
- snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
- entry.name = strdup(name);
- entry.card = card;
- entry.dev = dev;
-
- devmap->push_back(entry);
- TRACE("Got device \"%s\", card %d, dev %d\n", name, card, dev);
- }
- }
- snd_ctl_close(handle);
- }
-}
-
-
-/* Wrappers to use an old-style backend with the new interface. */
-struct PlaybackWrapper final : public BackendBase {
- PlaybackWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
- ~PlaybackWrapper() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- std::unique_ptr<qsa_data> mExtraData;
-
- DEF_NEWDEL(PlaybackWrapper)
-};
-
-
-FORCE_ALIGN static int qsa_proc_playback(void *ptr)
-{
- PlaybackWrapper *self = static_cast<PlaybackWrapper*>(ptr);
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- snd_pcm_channel_status_t status;
- sched_param param;
- char* write_ptr;
- ALint len;
- int sret;
-
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- /* Increase default 10 priority to 11 to avoid jerky sound */
- SchedGet(0, 0, &param);
- param.sched_priority=param.sched_curpriority+1;
- SchedSet(0, 0, SCHED_NOCHANGE, &param);
-
- const ALint frame_size = device->frameSizeFromFmt();
-
- self->lock();
- while(!data->mKillNow.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = data->audio_fd;
- pollitem.events = POLLOUT;
-
- /* Select also works like time slice to OS */
- self->unlock();
- sret = poll(&pollitem, 1, 2000);
- self->lock();
- if(sret == -1)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll error: %s\n", strerror(errno));
- aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
- break;
- }
- if(sret == 0)
- {
- ERR("poll timeout\n");
- continue;
- }
-
- len = data->size;
- write_ptr = static_cast<char*>(data->buffer);
- aluMixData(device, write_ptr, len/frame_size);
- while(len>0 && !data->mKillNow.load(std::memory_order_acquire))
- {
- int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
- if(wrote <= 0)
- {
- if(errno==EAGAIN || errno==EWOULDBLOCK)
- continue;
-
- memset(&status, 0, sizeof(status));
- status.channel = SND_PCM_CHANNEL_PLAYBACK;
-
- snd_pcm_plugin_status(data->pcmHandle, &status);
-
- /* we need to reinitialize the sound channel if we've underrun the buffer */
- if(status.status == SND_PCM_STATUS_UNDERRUN ||
- status.status == SND_PCM_STATUS_READY)
- {
- if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
- {
- aluHandleDisconnect(device, "Playback recovery failed");
- break;
- }
- }
- }
- else
- {
- write_ptr += wrote;
- len -= wrote;
- }
- }
- }
- self->unlock();
-
- return 0;
-}
-
-/************/
-/* Playback */
-/************/
-
-static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
-{
- ALCdevice *device = self->mDevice;
- int card, dev;
- int status;
-
- std::unique_ptr<qsa_data> data{new qsa_data{}};
- data->mKillNow.store(AL_TRUE, std::memory_order_relaxed);
-
- if(!deviceName)
- deviceName = qsaDevice;
-
- if(strcmp(deviceName, qsaDevice) == 0)
- status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
- else
- {
- if(DeviceNameMap.empty())
- deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
-
- auto iter = std::find_if(DeviceNameMap.begin(), DeviceNameMap.end(),
- [deviceName](const DevMap &entry) -> bool
- { return entry.name && strcmp(deviceName, entry.name) == 0; }
- );
- if(iter == DeviceNameMap.cend())
- return ALC_INVALID_DEVICE;
-
- status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
- }
-
- if(status < 0)
- return ALC_INVALID_DEVICE;
-
- data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
- if(data->audio_fd < 0)
- {
- snd_pcm_close(data->pcmHandle);
- return ALC_INVALID_DEVICE;
- }
-
- device->DeviceName = deviceName;
- self->mExtraData = std::move(data);
-
- return ALC_NO_ERROR;
-}
-
-static void qsa_close_playback(PlaybackWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- if (data->buffer!=NULL)
- {
- free(data->buffer);
- data->buffer=NULL;
- }
-
- snd_pcm_close(data->pcmHandle);
-
- self->mExtraData = nullptr;
-}
-
-static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
-{
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- int32_t format=-1;
-
- switch(device->FmtType)
- {
- case DevFmtByte:
- format=SND_PCM_SFMT_S8;
- break;
- case DevFmtUByte:
- format=SND_PCM_SFMT_U8;
- break;
- case DevFmtShort:
- format=SND_PCM_SFMT_S16_LE;
- break;
- case DevFmtUShort:
- format=SND_PCM_SFMT_U16_LE;
- break;
- case DevFmtInt:
- format=SND_PCM_SFMT_S32_LE;
- break;
- case DevFmtUInt:
- format=SND_PCM_SFMT_U32_LE;
- break;
- case DevFmtFloat:
- format=SND_PCM_SFMT_FLOAT_LE;
- break;
- }
-
- /* we actually don't want to block on writes */
- snd_pcm_nonblock_mode(data->pcmHandle, 1);
- /* Disable mmap to control data transfer to the audio device */
- snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
- snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
-
- // configure a sound channel
- memset(&data->cparams, 0, sizeof(data->cparams));
- data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
- data->cparams.mode=SND_PCM_MODE_BLOCK;
- data->cparams.start_mode=SND_PCM_START_FULL;
- data->cparams.stop_mode=SND_PCM_STOP_STOP;
-
- data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt();
- data->cparams.buf.block.frags_max=device->BufferSize / device->UpdateSize;
- data->cparams.buf.block.frags_min=data->cparams.buf.block.frags_max;
-
- data->cparams.format.interleave=1;
- data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=device->channelsFromFmt();
- data->cparams.format.format=format;
-
- if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
- {
- int original_rate=data->cparams.format.rate;
- int original_voices=data->cparams.format.voices;
- int original_format=data->cparams.format.format;
- int it;
- int jt;
-
- for (it=0; it<1; it++)
- {
- /* Check for second pass */
- if (it==1)
- {
- original_rate=ratelist[0].rate;
- original_voices=channellist[0].channels;
- original_format=formatlist[0].format;
- }
-
- do {
- /* At first downgrade sample format */
- jt=0;
- do {
- if (formatlist[jt].format==data->cparams.format.format)
- {
- data->cparams.format.format=formatlist[jt+1].format;
- break;
- }
- if (formatlist[jt].format==0)
- {
- data->cparams.format.format=0;
- break;
- }
- jt++;
- } while(1);
-
- if (data->cparams.format.format==0)
- {
- data->cparams.format.format=original_format;
-
- /* At secod downgrade sample rate */
- jt=0;
- do {
- if (ratelist[jt].rate==data->cparams.format.rate)
- {
- data->cparams.format.rate=ratelist[jt+1].rate;
- break;
- }
- if (ratelist[jt].rate==0)
- {
- data->cparams.format.rate=0;
- break;
- }
- jt++;
- } while(1);
-
- if (data->cparams.format.rate==0)
- {
- data->cparams.format.rate=original_rate;
- data->cparams.format.format=original_format;
-
- /* At third downgrade channels number */
- jt=0;
- do {
- if(channellist[jt].channels==data->cparams.format.voices)
- {
- data->cparams.format.voices=channellist[jt+1].channels;
- break;
- }
- if (channellist[jt].channels==0)
- {
- data->cparams.format.voices=0;
- break;
- }
- jt++;
- } while(1);
- }
-
- if (data->cparams.format.voices==0)
- {
- break;
- }
- }
-
- data->cparams.buf.block.frag_size=device->UpdateSize*
- data->cparams.format.voices*
- snd_pcm_format_width(data->cparams.format.format)/8;
- data->cparams.buf.block.frags_max=device->NumUpdates;
- data->cparams.buf.block.frags_min=device->NumUpdates;
- if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
- {
- continue;
- }
- else
- {
- break;
- }
- } while(1);
-
- if (data->cparams.format.voices!=0)
- {
- break;
- }
- }
-
- if (data->cparams.format.voices==0)
- {
- return ALC_FALSE;
- }
- }
-
- if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
- {
- return ALC_FALSE;
- }
-
- memset(&data->csetup, 0, sizeof(data->csetup));
- data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
- if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
- {
- return ALC_FALSE;
- }
-
- /* now fill back to the our AL device */
- device->Frequency=data->cparams.format.rate;
-
- switch (data->cparams.format.voices)
- {
- case 1:
- device->FmtChans=DevFmtMono;
- break;
- case 2:
- device->FmtChans=DevFmtStereo;
- break;
- case 4:
- device->FmtChans=DevFmtQuad;
- break;
- case 6:
- device->FmtChans=DevFmtX51;
- break;
- case 7:
- device->FmtChans=DevFmtX61;
- break;
- case 8:
- device->FmtChans=DevFmtX71;
- break;
- default:
- device->FmtChans=DevFmtMono;
- break;
- }
-
- switch (data->cparams.format.format)
- {
- case SND_PCM_SFMT_S8:
- device->FmtType=DevFmtByte;
- break;
- case SND_PCM_SFMT_U8:
- device->FmtType=DevFmtUByte;
- break;
- case SND_PCM_SFMT_S16_LE:
- device->FmtType=DevFmtShort;
- break;
- case SND_PCM_SFMT_U16_LE:
- device->FmtType=DevFmtUShort;
- break;
- case SND_PCM_SFMT_S32_LE:
- device->FmtType=DevFmtInt;
- break;
- case SND_PCM_SFMT_U32_LE:
- device->FmtType=DevFmtUInt;
- break;
- case SND_PCM_SFMT_FLOAT_LE:
- device->FmtType=DevFmtFloat;
- break;
- default:
- device->FmtType=DevFmtShort;
- break;
- }
-
- SetDefaultChannelOrder(device);
-
- device->UpdateSize=data->csetup.buf.block.frag_size / device->frameSizeFromFmt();
- device->NumUpdates=data->csetup.buf.block.frags;
-
- data->size=data->csetup.buf.block.frag_size;
- data->buffer=malloc(data->size);
- if (!data->buffer)
- {
- return ALC_FALSE;
- }
-
- return ALC_TRUE;
-}
-
-static ALCboolean qsa_start_playback(PlaybackWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- try {
- data->mKillNow.store(AL_FALSE, std::memory_order_release);
- data->mThread = std::thread(qsa_proc_playback, self);
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-static void qsa_stop_playback(PlaybackWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- if(data->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !data->mThread.joinable())
- return;
- data->mThread.join();
-}
-
-
-PlaybackWrapper::~PlaybackWrapper()
-{
- if(mExtraData)
- qsa_close_playback(this);
-}
-
-ALCenum PlaybackWrapper::open(const ALCchar *name)
-{ return qsa_open_playback(this, name); }
-
-ALCboolean PlaybackWrapper::reset()
-{ return qsa_reset_playback(this); }
-
-ALCboolean PlaybackWrapper::start()
-{ return qsa_start_playback(this); }
-
-void PlaybackWrapper::stop()
-{ qsa_stop_playback(this); }
-
-
-/***********/
-/* Capture */
-/***********/
-
-struct CaptureWrapper final : public BackendBase {
- CaptureWrapper(ALCdevice *device) noexcept : BackendBase{device} { }
- ~CaptureWrapper() override;
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- std::unique_ptr<qsa_data> mExtraData;
-
- DEF_NEWDEL(CaptureWrapper)
-};
-
-static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
-{
- ALCdevice *device = self->mDevice;
- int card, dev;
- int format=-1;
- int status;
-
- std::unique_ptr<qsa_data> data{new qsa_data{}};
-
- if(!deviceName)
- deviceName = qsaDevice;
-
- if(strcmp(deviceName, qsaDevice) == 0)
- status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
- else
- {
- if(CaptureNameMap.empty())
- deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
-
- auto iter = std::find_if(CaptureNameMap.cbegin(), CaptureNameMap.cend(),
- [deviceName](const DevMap &entry) -> bool
- { return entry.name && strcmp(deviceName, entry.name) == 0; }
- );
- if(iter == CaptureNameMap.cend())
- return ALC_INVALID_DEVICE;
-
- status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
- }
-
- if(status < 0)
- return ALC_INVALID_DEVICE;
-
- data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
- if(data->audio_fd < 0)
- {
- snd_pcm_close(data->pcmHandle);
- return ALC_INVALID_DEVICE;
- }
-
- device->DeviceName = deviceName;
-
- switch (device->FmtType)
- {
- case DevFmtByte:
- format=SND_PCM_SFMT_S8;
- break;
- case DevFmtUByte:
- format=SND_PCM_SFMT_U8;
- break;
- case DevFmtShort:
- format=SND_PCM_SFMT_S16_LE;
- break;
- case DevFmtUShort:
- format=SND_PCM_SFMT_U16_LE;
- break;
- case DevFmtInt:
- format=SND_PCM_SFMT_S32_LE;
- break;
- case DevFmtUInt:
- format=SND_PCM_SFMT_U32_LE;
- break;
- case DevFmtFloat:
- format=SND_PCM_SFMT_FLOAT_LE;
- break;
- }
-
- /* we actually don't want to block on reads */
- snd_pcm_nonblock_mode(data->pcmHandle, 1);
- /* Disable mmap to control data transfer to the audio device */
- snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
-
- /* configure a sound channel */
- memset(&data->cparams, 0, sizeof(data->cparams));
- data->cparams.mode=SND_PCM_MODE_BLOCK;
- data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
- data->cparams.start_mode=SND_PCM_START_GO;
- data->cparams.stop_mode=SND_PCM_STOP_STOP;
-
- data->cparams.buf.block.frag_size=device->UpdateSize * device->frameSizeFromFmt();
- data->cparams.buf.block.frags_max=device->NumUpdates;
- data->cparams.buf.block.frags_min=device->NumUpdates;
-
- data->cparams.format.interleave=1;
- data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=device->channelsFromFmt();
- data->cparams.format.format=format;
-
- if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
- {
- snd_pcm_close(data->pcmHandle);
- return ALC_INVALID_VALUE;
- }
-
- self->mExtraData = std::move(data);
-
- return ALC_NO_ERROR;
-}
-
-static void qsa_close_capture(CaptureWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
-
- if (data->pcmHandle!=nullptr)
- snd_pcm_close(data->pcmHandle);
- data->pcmHandle = nullptr;
-
- self->mExtraData = nullptr;
-}
-
-static void qsa_start_capture(CaptureWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
- int rstatus;
-
- if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
- {
- ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- return;
- }
-
- memset(&data->csetup, 0, sizeof(data->csetup));
- data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
- if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
- {
- ERR("capture setup failed: %s\n", snd_strerror(rstatus));
- return;
- }
-
- snd_pcm_capture_go(data->pcmHandle);
-}
-
-static void qsa_stop_capture(CaptureWrapper *self)
-{
- qsa_data *data = self->mExtraData.get();
- snd_pcm_capture_flush(data->pcmHandle);
-}
-
-static ALCuint qsa_available_samples(CaptureWrapper *self)
-{
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- snd_pcm_channel_status_t status;
- ALint frame_size = device->frameSizeFromFmt();
- ALint free_size;
- int rstatus;
-
- memset(&status, 0, sizeof (status));
- status.channel=SND_PCM_CHANNEL_CAPTURE;
- snd_pcm_plugin_status(data->pcmHandle, &status);
- if ((status.status==SND_PCM_STATUS_OVERRUN) ||
- (status.status==SND_PCM_STATUS_READY))
- {
- if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
- {
- ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
- return 0;
- }
-
- snd_pcm_capture_go(data->pcmHandle);
- return 0;
- }
-
- free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
- free_size-=status.free;
-
- return free_size/frame_size;
-}
-
-static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
-{
- ALCdevice *device = self->mDevice;
- qsa_data *data = self->mExtraData.get();
- char* read_ptr;
- snd_pcm_channel_status_t status;
- int selectret;
- int bytes_read;
- ALint frame_size=device->frameSizeFromFmt();
- ALint len=samples*frame_size;
- int rstatus;
-
- read_ptr = static_cast<char*>(buffer);
-
- while (len>0)
- {
- pollfd pollitem{};
- pollitem.fd = data->audio_fd;
- pollitem.events = POLLOUT;
-
- /* Select also works like time slice to OS */
- bytes_read=0;
- selectret = poll(&pollitem, 1, 2000);
- switch (selectret)
- {
- case -1:
- aluHandleDisconnect(device, "Failed to check capture samples");
- return ALC_INVALID_DEVICE;
- case 0:
- break;
- default:
- bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
- break;
- }
-
- if (bytes_read<=0)
- {
- if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
- {
- continue;
- }
-
- memset(&status, 0, sizeof (status));
- status.channel=SND_PCM_CHANNEL_CAPTURE;
- snd_pcm_plugin_status(data->pcmHandle, &status);
-
- /* we need to reinitialize the sound channel if we've overrun the buffer */
- if ((status.status==SND_PCM_STATUS_OVERRUN) ||
- (status.status==SND_PCM_STATUS_READY))
- {
- if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
- {
- ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device, "Failed capture recovery: %s",
- snd_strerror(rstatus));
- return ALC_INVALID_DEVICE;
- }
- snd_pcm_capture_go(data->pcmHandle);
- }
- }
- else
- {
- read_ptr+=bytes_read;
- len-=bytes_read;
- }
- }
-
- return ALC_NO_ERROR;
-}
-
-
-CaptureWrapper::~CaptureWrapper()
-{
- if(mExtraData)
- qsa_close_capture(this);
-}
-
-ALCenum CaptureWrapper::open(const ALCchar *name)
-{ return qsa_open_capture(this, name); }
-
-ALCboolean CaptureWrapper::start()
-{ qsa_start_capture(this); return ALC_TRUE; }
-
-void CaptureWrapper::stop()
-{ qsa_stop_capture(this); }
-
-ALCenum CaptureWrapper::captureSamples(void *buffer, ALCuint samples)
-{ return qsa_capture_samples(this, buffer, samples); }
-
-ALCuint CaptureWrapper::availableSamples()
-{ return qsa_available_samples(this); }
-
-} // namespace
-
-
-bool QSABackendFactory::init()
-{ return true; }
-
-bool QSABackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void QSABackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- const char *n = entry.name;
- if(n && n[0])
- outnames->append(n, strlen(n)+1);
- };
-
- switch (type)
- {
- case DevProbe::Playback:
- deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
- std::for_each(DeviceNameMap.cbegin(), DeviceNameMap.cend(), add_device);
- break;
- case DevProbe::Capture:
- deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
- std::for_each(CaptureNameMap.cbegin(), CaptureNameMap.cend(), add_device);
- break;
- }
-}
-
-BackendPtr QSABackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new PlaybackWrapper{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new CaptureWrapper{device}};
- return nullptr;
-}
-
-BackendFactory &QSABackendFactory::getFactory()
-{
- static QSABackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/qsa.h b/Alc/backends/qsa.h
deleted file mode 100644
index da548bba..00000000
--- a/Alc/backends/qsa.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_QSA_H
-#define BACKENDS_QSA_H
-
-#include "backends/base.h"
-
-struct QSABackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_QSA_H */
diff --git a/Alc/backends/sdl2.cpp b/Alc/backends/sdl2.cpp
deleted file mode 100644
index 97547959..00000000
--- a/Alc/backends/sdl2.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/sdl2.h"
-
-#include <stdlib.h>
-#include <SDL2/SDL.h>
-
-#include <string>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "threads.h"
-#include "compat.h"
-
-
-namespace {
-
-#ifdef _WIN32
-#define DEVNAME_PREFIX "OpenAL Soft on "
-#else
-#define DEVNAME_PREFIX ""
-#endif
-
-constexpr ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
-
-struct Sdl2Backend final : public BackendBase {
- Sdl2Backend(ALCdevice *device) noexcept : BackendBase{device} { }
- ~Sdl2Backend() override;
-
- static void audioCallbackC(void *ptr, Uint8 *stream, int len);
- void audioCallback(Uint8 *stream, int len);
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
- void lock() override;
- void unlock() override;
-
- SDL_AudioDeviceID mDeviceID{0u};
- ALsizei mFrameSize{0};
-
- ALuint mFrequency{0u};
- DevFmtChannels mFmtChans{};
- DevFmtType mFmtType{};
- ALuint mUpdateSize{0u};
-
- DEF_NEWDEL(Sdl2Backend)
-};
-
-Sdl2Backend::~Sdl2Backend()
-{
- if(mDeviceID)
- SDL_CloseAudioDevice(mDeviceID);
- mDeviceID = 0;
-}
-
-void Sdl2Backend::audioCallbackC(void *ptr, Uint8 *stream, int len)
-{ static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); }
-
-void Sdl2Backend::audioCallback(Uint8 *stream, int len)
-{
- assert((len % mFrameSize) == 0);
- aluMixData(mDevice, stream, len / mFrameSize);
-}
-
-ALCenum Sdl2Backend::open(const ALCchar *name)
-{
- SDL_AudioSpec want{}, have{};
- want.freq = mDevice->Frequency;
- switch(mDevice->FmtType)
- {
- case DevFmtUByte: want.format = AUDIO_U8; break;
- case DevFmtByte: want.format = AUDIO_S8; break;
- case DevFmtUShort: want.format = AUDIO_U16SYS; break;
- case DevFmtShort: want.format = AUDIO_S16SYS; break;
- case DevFmtUInt: /* fall-through */
- case DevFmtInt: want.format = AUDIO_S32SYS; break;
- case DevFmtFloat: want.format = AUDIO_F32; break;
- }
- want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
- want.samples = mDevice->UpdateSize;
- want.callback = &Sdl2Backend::audioCallbackC;
- want.userdata = this;
-
- /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
- * necessarily the first in the list.
- */
- if(!name || strcmp(name, defaultDeviceName) == 0)
- mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
- SDL_AUDIO_ALLOW_ANY_CHANGE);
- else
- {
- const size_t prefix_len = strlen(DEVNAME_PREFIX);
- if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
- mDeviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
- SDL_AUDIO_ALLOW_ANY_CHANGE);
- else
- mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
- SDL_AUDIO_ALLOW_ANY_CHANGE);
- }
- if(mDeviceID == 0)
- return ALC_INVALID_VALUE;
-
- mDevice->Frequency = have.freq;
- if(have.channels == 1)
- mDevice->FmtChans = DevFmtMono;
- else if(have.channels == 2)
- mDevice->FmtChans = DevFmtStereo;
- else
- {
- ERR("Got unhandled SDL channel count: %d\n", (int)have.channels);
- return ALC_INVALID_VALUE;
- }
- switch(have.format)
- {
- case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
- case AUDIO_S8: mDevice->FmtType = DevFmtByte; break;
- case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break;
- case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort; break;
- case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break;
- case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break;
- default:
- ERR("Got unsupported SDL format: 0x%04x\n", have.format);
- return ALC_INVALID_VALUE;
- }
- mDevice->UpdateSize = have.samples;
- mDevice->BufferSize = have.samples * 2; /* SDL always (tries to) use two periods. */
-
- mFrameSize = mDevice->frameSizeFromFmt();
- mFrequency = mDevice->Frequency;
- mFmtChans = mDevice->FmtChans;
- mFmtType = mDevice->FmtType;
- mUpdateSize = mDevice->UpdateSize;
-
- mDevice->DeviceName = name ? name : defaultDeviceName;
- return ALC_NO_ERROR;
-}
-
-ALCboolean Sdl2Backend::reset()
-{
- mDevice->Frequency = mFrequency;
- mDevice->FmtChans = mFmtChans;
- mDevice->FmtType = mFmtType;
- mDevice->UpdateSize = mUpdateSize;
- mDevice->BufferSize = mUpdateSize * 2;
- SetDefaultWFXChannelOrder(mDevice);
- return ALC_TRUE;
-}
-
-ALCboolean Sdl2Backend::start()
-{
- SDL_PauseAudioDevice(mDeviceID, 0);
- return ALC_TRUE;
-}
-
-void Sdl2Backend::stop()
-{ SDL_PauseAudioDevice(mDeviceID, 1); }
-
-void Sdl2Backend::lock()
-{ SDL_LockAudioDevice(mDeviceID); }
-
-void Sdl2Backend::unlock()
-{ SDL_UnlockAudioDevice(mDeviceID); }
-
-} // namespace
-
-BackendFactory &SDL2BackendFactory::getFactory()
-{
- static SDL2BackendFactory factory{};
- return factory;
-}
-
-bool SDL2BackendFactory::init()
-{ return (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0); }
-
-bool SDL2BackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback; }
-
-void SDL2BackendFactory::probe(DevProbe type, std::string *outnames)
-{
- if(type != DevProbe::Playback)
- return;
-
- int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
-
- /* Includes null char. */
- outnames->append(defaultDeviceName, sizeof(defaultDeviceName));
- for(int i{0};i < num_devices;++i)
- {
- std::string name{DEVNAME_PREFIX};
- name += SDL_GetAudioDeviceName(i, SDL_FALSE);
- if(!name.empty())
- outnames->append(name.c_str(), name.length()+1);
- }
-}
-
-BackendPtr SDL2BackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new Sdl2Backend{device}};
- return nullptr;
-}
diff --git a/Alc/backends/sdl2.h b/Alc/backends/sdl2.h
deleted file mode 100644
index 041d47ee..00000000
--- a/Alc/backends/sdl2.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_SDL2_H
-#define BACKENDS_SDL2_H
-
-#include "backends/base.h"
-
-struct SDL2BackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_SDL2_H */
diff --git a/Alc/backends/sndio.cpp b/Alc/backends/sndio.cpp
deleted file mode 100644
index 587f67bb..00000000
--- a/Alc/backends/sndio.cpp
+++ /dev/null
@@ -1,495 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/sndio.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "threads.h"
-#include "vector.h"
-#include "ringbuffer.h"
-
-#include <sndio.h>
-
-
-namespace {
-
-static const ALCchar sndio_device[] = "SndIO Default";
-
-
-struct SndioPlayback final : public BackendBase {
- SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~SndioPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- sio_hdl *mSndHandle{nullptr};
-
- al::vector<ALubyte> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(SndioPlayback)
-};
-
-SndioPlayback::~SndioPlayback()
-{
- if(mSndHandle)
- sio_close(mSndHandle);
- mSndHandle = nullptr;
-}
-
-int SndioPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const ALsizei frameSize{mDevice->frameSizeFromFmt()};
-
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto WritePtr = static_cast<ALubyte*>(mBuffer.data());
- size_t len{mBuffer.size()};
-
- lock();
- aluMixData(mDevice, WritePtr, len/frameSize);
- unlock();
- while(len > 0 && !mKillNow.load(std::memory_order_acquire))
- {
- size_t wrote{sio_write(mSndHandle, WritePtr, len)};
- if(wrote == 0)
- {
- ERR("sio_write failed\n");
- aluHandleDisconnect(mDevice, "Failed to write playback samples");
- break;
- }
-
- len -= wrote;
- WritePtr += wrote;
- }
- }
-
- return 0;
-}
-
-
-ALCenum SndioPlayback::open(const ALCchar *name)
-{
- if(!name)
- name = sndio_device;
- else if(strcmp(name, sndio_device) != 0)
- return ALC_INVALID_VALUE;
-
- mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
- if(mSndHandle == nullptr)
- {
- ERR("Could not open device\n");
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean SndioPlayback::reset()
-{
- sio_par par;
- sio_initpar(&par);
-
- par.rate = mDevice->Frequency;
- par.pchan = ((mDevice->FmtChans != DevFmtMono) ? 2 : 1);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- par.bits = 8;
- par.sig = 1;
- break;
- case DevFmtUByte:
- par.bits = 8;
- par.sig = 0;
- break;
- case DevFmtFloat:
- case DevFmtShort:
- par.bits = 16;
- par.sig = 1;
- break;
- case DevFmtUShort:
- par.bits = 16;
- par.sig = 0;
- break;
- case DevFmtInt:
- par.bits = 32;
- par.sig = 1;
- break;
- case DevFmtUInt:
- par.bits = 32;
- par.sig = 0;
- break;
- }
- par.le = SIO_LE_NATIVE;
-
- par.round = mDevice->UpdateSize;
- par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
- if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
-
- if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
- {
- ERR("Failed to set device parameters\n");
- return ALC_FALSE;
- }
-
- if(par.bits != par.bps*8)
- {
- ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
- return ALC_FALSE;
- }
-
- mDevice->Frequency = par.rate;
- mDevice->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
-
- if(par.bits == 8 && par.sig == 1)
- mDevice->FmtType = DevFmtByte;
- else if(par.bits == 8 && par.sig == 0)
- mDevice->FmtType = DevFmtUByte;
- else if(par.bits == 16 && par.sig == 1)
- mDevice->FmtType = DevFmtShort;
- else if(par.bits == 16 && par.sig == 0)
- mDevice->FmtType = DevFmtUShort;
- else if(par.bits == 32 && par.sig == 1)
- mDevice->FmtType = DevFmtInt;
- else if(par.bits == 32 && par.sig == 0)
- mDevice->FmtType = DevFmtUInt;
- else
- {
- ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
- return ALC_FALSE;
- }
-
- SetDefaultChannelOrder(mDevice);
-
- mDevice->UpdateSize = par.round;
- mDevice->BufferSize = par.bufsz + par.round;
-
- mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
- std::fill(mBuffer.begin(), mBuffer.end(), 0);
-
- return ALC_TRUE;
-}
-
-ALCboolean SndioPlayback::start()
-{
- if(!sio_start(mSndHandle))
- {
- ERR("Error starting playback\n");
- return ALC_FALSE;
- }
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- sio_stop(mSndHandle);
- return ALC_FALSE;
-}
-
-void SndioPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(!sio_stop(mSndHandle))
- ERR("Error stopping device\n");
-}
-
-
-struct SndioCapture final : public BackendBase {
- SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~SndioCapture() override;
-
- int recordProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- sio_hdl *mSndHandle{nullptr};
-
- RingBufferPtr mRing;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(SndioCapture)
-};
-
-SndioCapture::~SndioCapture()
-{
- if(mSndHandle)
- sio_close(mSndHandle);
- mSndHandle = nullptr;
-}
-
-int SndioCapture::recordProc()
-{
- SetRTPriority();
- althrd_setname(RECORD_THREAD_NAME);
-
- const ALsizei frameSize{mDevice->frameSizeFromFmt()};
-
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto data = mRing->getWriteVector();
- size_t todo{data.first.len + data.second.len};
- if(todo == 0)
- {
- static char junk[4096];
- sio_read(mSndHandle, junk,
- minz(sizeof(junk)/frameSize, mDevice->UpdateSize)*frameSize);
- continue;
- }
-
- size_t total{0u};
- data.first.len *= frameSize;
- data.second.len *= frameSize;
- todo = minz(todo, mDevice->UpdateSize) * frameSize;
- while(total < todo)
- {
- if(!data.first.len)
- data.first = data.second;
-
- size_t got{sio_read(mSndHandle, data.first.buf, minz(todo-total, data.first.len))};
- if(!got)
- {
- aluHandleDisconnect(mDevice, "Failed to read capture samples");
- break;
- }
-
- data.first.buf += got;
- data.first.len -= got;
- total += got;
- }
- mRing->writeAdvance(total / frameSize);
- }
-
- return 0;
-}
-
-
-ALCenum SndioCapture::open(const ALCchar *name)
-{
- if(!name)
- name = sndio_device;
- else if(strcmp(name, sndio_device) != 0)
- return ALC_INVALID_VALUE;
-
- mSndHandle = sio_open(nullptr, SIO_REC, 0);
- if(mSndHandle == nullptr)
- {
- ERR("Could not open device\n");
- return ALC_INVALID_VALUE;
- }
-
- sio_par par;
- sio_initpar(&par);
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- par.bps = 1;
- par.sig = 1;
- break;
- case DevFmtUByte:
- par.bps = 1;
- par.sig = 0;
- break;
- case DevFmtShort:
- par.bps = 2;
- par.sig = 1;
- break;
- case DevFmtUShort:
- par.bps = 2;
- par.sig = 0;
- break;
- case DevFmtInt:
- par.bps = 4;
- par.sig = 1;
- break;
- case DevFmtUInt:
- par.bps = 4;
- par.sig = 0;
- break;
- case DevFmtFloat:
- ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
- return ALC_INVALID_VALUE;
- }
- par.bits = par.bps * 8;
- par.le = SIO_LE_NATIVE;
- par.msb = SIO_LE_NATIVE ? 0 : 1;
- par.rchan = mDevice->channelsFromFmt();
- par.rate = mDevice->Frequency;
-
- par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
- par.round = minu(par.appbufsz, mDevice->Frequency/40);
-
- mDevice->UpdateSize = par.round;
- mDevice->BufferSize = par.appbufsz;
-
- if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
- {
- ERR("Failed to set device parameters\n");
- return ALC_INVALID_VALUE;
- }
-
- if(par.bits != par.bps*8)
- {
- ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
- return ALC_INVALID_VALUE;
- }
-
- if(!((mDevice->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
- (mDevice->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
- (mDevice->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
- (mDevice->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
- (mDevice->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
- (mDevice->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
- mDevice->channelsFromFmt() != (ALsizei)par.rchan ||
- mDevice->Frequency != par.rate)
- {
- ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
- DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
- mDevice->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
- return ALC_INVALID_VALUE;
- }
-
- mRing = CreateRingBuffer(mDevice->BufferSize, par.bps*par.rchan, false);
- if(!mRing)
- {
- ERR("Failed to allocate %u-byte ringbuffer\n", mDevice->BufferSize*par.bps*par.rchan);
- return ALC_OUT_OF_MEMORY;
- }
-
- SetDefaultChannelOrder(mDevice);
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean SndioCapture::start()
-{
- if(!sio_start(mSndHandle))
- {
- ERR("Error starting playback\n");
- return ALC_FALSE;
- }
-
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create record thread: %s\n", e.what());
- }
- catch(...) {
- }
- sio_stop(mSndHandle);
- return ALC_FALSE;
-}
-
-void SndioCapture::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(!sio_stop(mSndHandle))
- ERR("Error stopping device\n");
-}
-
-ALCenum SndioCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint SndioCapture::availableSamples()
-{ return mRing->readSpace(); }
-
-} // namespace
-
-BackendFactory &SndIOBackendFactory::getFactory()
-{
- static SndIOBackendFactory factory{};
- return factory;
-}
-
-bool SndIOBackendFactory::init()
-{ return true; }
-
-bool SndIOBackendFactory::querySupport(BackendType type)
-{ return (type == BackendType::Playback || type == BackendType::Capture); }
-
-void SndIOBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- case DevProbe::Capture:
- /* Includes null char. */
- outnames->append(sndio_device, sizeof(sndio_device));
- break;
- }
-}
-
-BackendPtr SndIOBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new SndioPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new SndioCapture{device}};
- return nullptr;
-}
diff --git a/Alc/backends/sndio.h b/Alc/backends/sndio.h
deleted file mode 100644
index 1ed63d5e..00000000
--- a/Alc/backends/sndio.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_SNDIO_H
-#define BACKENDS_SNDIO_H
-
-#include "backends/base.h"
-
-struct SndIOBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_SNDIO_H */
diff --git a/Alc/backends/solaris.cpp b/Alc/backends/solaris.cpp
deleted file mode 100644
index 584f6e66..00000000
--- a/Alc/backends/solaris.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/solaris.h"
-
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-#include <unistd.h>
-#include <errno.h>
-#include <poll.h>
-#include <math.h>
-
-#include <thread>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "alconfig.h"
-#include "threads.h"
-#include "vector.h"
-#include "compat.h"
-
-#include <sys/audioio.h>
-
-
-namespace {
-
-constexpr ALCchar solaris_device[] = "Solaris Default";
-
-std::string solaris_driver{"/dev/audio"};
-
-
-struct SolarisBackend final : public BackendBase {
- SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
- ~SolarisBackend() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- int mFd{-1};
-
- al::vector<ALubyte> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(SolarisBackend)
-};
-
-SolarisBackend::~SolarisBackend()
-{
- if(mFd != -1)
- close(mFd);
- mFd = -1;
-}
-
-int SolarisBackend::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const int frame_size{mDevice->frameSizeFromFmt()};
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- pollfd pollitem{};
- pollitem.fd = mFd;
- pollitem.events = POLLOUT;
-
- unlock();
- int pret{poll(&pollitem, 1, 1000)};
- lock();
- if(pret < 0)
- {
- if(errno == EINTR || errno == EAGAIN)
- continue;
- ERR("poll failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed to wait for playback buffer: %s",
- strerror(errno));
- break;
- }
- else if(pret == 0)
- {
- WARN("poll timeout\n");
- continue;
- }
-
- ALubyte *write_ptr{mBuffer.data()};
- size_t to_write{mBuffer.size()};
- aluMixData(mDevice, write_ptr, to_write/frame_size);
- while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
- {
- ssize_t wrote{write(mFd, write_ptr, to_write)};
- if(wrote < 0)
- {
- if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- ERR("write failed: %s\n", strerror(errno));
- aluHandleDisconnect(mDevice, "Failed to write playback samples: %s",
- strerror(errno));
- break;
- }
-
- to_write -= wrote;
- write_ptr += wrote;
- }
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum SolarisBackend::open(const ALCchar *name)
-{
- if(!name)
- name = solaris_device;
- else if(strcmp(name, solaris_device) != 0)
- return ALC_INVALID_VALUE;
-
- mFd = ::open(solaris_driver.c_str(), O_WRONLY);
- if(mFd == -1)
- {
- ERR("Could not open %s: %s\n", solaris_driver.c_str(), strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
- return ALC_NO_ERROR;
-}
-
-ALCboolean SolarisBackend::reset()
-{
- audio_info_t info;
- AUDIO_INITINFO(&info);
-
- info.play.sample_rate = mDevice->Frequency;
-
- if(mDevice->FmtChans != DevFmtMono)
- mDevice->FmtChans = DevFmtStereo;
- ALsizei numChannels{mDevice->channelsFromFmt()};
- info.play.channels = numChannels;
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- info.play.precision = 8;
- info.play.encoding = AUDIO_ENCODING_LINEAR;
- break;
- case DevFmtUByte:
- info.play.precision = 8;
- info.play.encoding = AUDIO_ENCODING_LINEAR8;
- break;
- case DevFmtUShort:
- case DevFmtInt:
- case DevFmtUInt:
- case DevFmtFloat:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- info.play.precision = 16;
- info.play.encoding = AUDIO_ENCODING_LINEAR;
- break;
- }
-
- ALsizei frameSize{numChannels * mDevice->bytesFromFmt()};
- info.play.buffer_size = mDevice->BufferSize * frameSize;
-
- if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
- {
- ERR("ioctl failed: %s\n", strerror(errno));
- return ALC_FALSE;
- }
-
- if(mDevice->channelsFromFmt() != (ALsizei)info.play.channels)
- {
- ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
- info.play.channels);
- return ALC_FALSE;
- }
-
- if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && mDevice->FmtType == DevFmtUByte) ||
- (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtByte) ||
- (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtShort) ||
- (info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtInt)))
- {
- ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(mDevice->FmtType),
- info.play.precision, info.play.encoding);
- return ALC_FALSE;
- }
-
- mDevice->Frequency = info.play.sample_rate;
- mDevice->BufferSize = info.play.buffer_size / frameSize;
- mDevice->UpdateSize = mDevice->BufferSize / 2;
-
- SetDefaultChannelOrder(mDevice);
-
- mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
- std::fill(mBuffer.begin(), mBuffer.end(), 0);
-
- return ALC_TRUE;
-}
-
-ALCboolean SolarisBackend::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Could not create playback thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void SolarisBackend::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- if(ioctl(mFd, AUDIO_DRAIN) < 0)
- ERR("Error draining device: %s\n", strerror(errno));
-}
-
-} // namespace
-
-BackendFactory &SolarisBackendFactory::getFactory()
-{
- static SolarisBackendFactory factory{};
- return factory;
-}
-
-bool SolarisBackendFactory::init()
-{
- if(auto devopt = ConfigValueStr(nullptr, "solaris", "device"))
- solaris_driver = std::move(*devopt);
- return true;
-}
-
-bool SolarisBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback; }
-
-void SolarisBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- {
-#ifdef HAVE_STAT
- struct stat buf;
- if(stat(solaris_driver.c_str(), &buf) == 0)
-#endif
- outnames->append(solaris_device, sizeof(solaris_device));
- }
- break;
-
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr SolarisBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new SolarisBackend{device}};
- return nullptr;
-}
diff --git a/Alc/backends/solaris.h b/Alc/backends/solaris.h
deleted file mode 100644
index 98b10593..00000000
--- a/Alc/backends/solaris.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_SOLARIS_H
-#define BACKENDS_SOLARIS_H
-
-#include "backends/base.h"
-
-struct SolarisBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_SOLARIS_H */
diff --git a/Alc/backends/wasapi.cpp b/Alc/backends/wasapi.cpp
deleted file mode 100644
index bd009463..00000000
--- a/Alc/backends/wasapi.cpp
+++ /dev/null
@@ -1,1763 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/wasapi.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
-#include <wtypes.h>
-#include <mmdeviceapi.h>
-#include <audioclient.h>
-#include <cguid.h>
-#include <devpropdef.h>
-#include <mmreg.h>
-#include <propsys.h>
-#include <propkey.h>
-#include <devpkey.h>
-#ifndef _WAVEFORMATEXTENSIBLE_
-#include <ks.h>
-#include <ksmedia.h>
-#endif
-
-#include <deque>
-#include <mutex>
-#include <atomic>
-#include <thread>
-#include <vector>
-#include <string>
-#include <future>
-#include <algorithm>
-#include <functional>
-#include <condition_variable>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "compat.h"
-#include "converter.h"
-#include "threads.h"
-
-
-/* Some headers seem to define these as macros for __uuidof, which is annoying
- * since some headers don't declare them at all. Hopefully the ifdef is enough
- * to tell if they need to be declared.
- */
-#ifndef KSDATAFORMAT_SUBTYPE_PCM
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
-DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-#endif
-
-DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
-DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
-DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
-
-
-namespace {
-
-#define MONO SPEAKER_FRONT_CENTER
-#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
-#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
-#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
-#define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
-#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
-#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
-#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
-
-#define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
-
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
-/* Scales the given value using 64-bit integer math, ceiling the result. */
-inline int64_t ScaleCeil(int64_t val, int64_t new_scale, int64_t old_scale)
-{
- return (val*new_scale + old_scale-1) / old_scale;
-}
-
-
-struct PropVariant {
- PROPVARIANT mProp;
-
-public:
- PropVariant() { PropVariantInit(&mProp); }
- ~PropVariant() { clear(); }
-
- void clear() { PropVariantClear(&mProp); }
-
- PROPVARIANT* get() noexcept { return &mProp; }
-
- PROPVARIANT& operator*() noexcept { return mProp; }
- const PROPVARIANT& operator*() const noexcept { return mProp; }
-
- PROPVARIANT* operator->() noexcept { return &mProp; }
- const PROPVARIANT* operator->() const noexcept { return &mProp; }
-};
-
-struct DevMap {
- std::string name;
- std::string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
- std::wstring devid;
-
- template<typename T0, typename T1, typename T2>
- DevMap(T0&& name_, T1&& guid_, T2&& devid_)
- : name{std::forward<T0>(name_)}
- , endpoint_guid{std::forward<T1>(guid_)}
- , devid{std::forward<T2>(devid_)}
- { }
-};
-
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
-{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
-}
-
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
-
-
-using NameGUIDPair = std::pair<std::string,std::string>;
-NameGUIDPair get_device_name_and_guid(IMMDevice *device)
-{
- std::string name{DEVNAME_HEAD};
- std::string guid;
-
- IPropertyStore *ps;
- HRESULT hr = device->OpenPropertyStore(STGM_READ, &ps);
- if(FAILED(hr))
- {
- WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
- return { name+"Unknown Device Name", "Unknown Device GUID" };
- }
-
- PropVariant pvprop;
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(DEVPKEY_Device_FriendlyName), pvprop.get());
- if(FAILED(hr))
- {
- WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
- name += "Unknown Device Name";
- }
- else if(pvprop->vt == VT_LPWSTR)
- name += wstr_to_utf8(pvprop->pwszVal);
- else
- {
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
- name += "Unknown Device Name";
- }
-
- pvprop.clear();
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_GUID), pvprop.get());
- if(FAILED(hr))
- {
- WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
- guid = "Unknown Device GUID";
- }
- else if(pvprop->vt == VT_LPWSTR)
- guid = wstr_to_utf8(pvprop->pwszVal);
- else
- {
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
- guid = "Unknown Device GUID";
- }
-
- ps->Release();
-
- return {name, guid};
-}
-
-void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
-{
- IPropertyStore *ps;
- HRESULT hr = device->OpenPropertyStore(STGM_READ, &ps);
- if(FAILED(hr))
- {
- WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
- return;
- }
-
- PropVariant pvform;
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_FormFactor), pvform.get());
- if(FAILED(hr))
- WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
- else if(pvform->vt == VT_UI4)
- *formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
- else if(pvform->vt == VT_EMPTY)
- *formfactor = UnknownFormFactor;
- else
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
-
- ps->Release();
-}
-
-
-void add_device(IMMDevice *device, const WCHAR *devid, al::vector<DevMap> &list)
-{
- std::string basename, guidstr;
- std::tie(basename, guidstr) = get_device_name_and_guid(device);
-
- int count{1};
- std::string newname{basename};
- while(checkName(list, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- list.emplace_back(std::move(newname), std::move(guidstr), devid);
- const DevMap &newentry = list.back();
-
- TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(),
- newentry.endpoint_guid.c_str(), newentry.devid.c_str());
-}
-
-WCHAR *get_device_id(IMMDevice *device)
-{
- WCHAR *devid;
-
- HRESULT hr = device->GetId(&devid);
- if(FAILED(hr))
- {
- ERR("Failed to get device id: %lx\n", hr);
- return nullptr;
- }
-
- return devid;
-}
-
-HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, al::vector<DevMap> &list)
-{
- IMMDeviceCollection *coll;
- HRESULT hr{devenum->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, &coll)};
- if(FAILED(hr))
- {
- ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
- return hr;
- }
-
- IMMDevice *defdev{nullptr};
- WCHAR *defdevid{nullptr};
- UINT count{0};
- hr = coll->GetCount(&count);
- if(SUCCEEDED(hr) && count > 0)
- {
- list.clear();
- list.reserve(count);
-
- hr = devenum->GetDefaultAudioEndpoint(flowdir, eMultimedia, &defdev);
- }
- if(SUCCEEDED(hr) && defdev != nullptr)
- {
- defdevid = get_device_id(defdev);
- if(defdevid)
- add_device(defdev, defdevid, list);
- }
-
- for(UINT i{0};i < count;++i)
- {
- IMMDevice *device;
- hr = coll->Item(i, &device);
- if(FAILED(hr)) continue;
-
- WCHAR *devid{get_device_id(device)};
- if(devid)
- {
- if(!defdevid || wcscmp(devid, defdevid) != 0)
- add_device(device, devid, list);
- CoTaskMemFree(devid);
- }
- device->Release();
- }
-
- if(defdev) defdev->Release();
- if(defdevid) CoTaskMemFree(defdevid);
- coll->Release();
-
- return S_OK;
-}
-
-
-bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
-{
- *out = WAVEFORMATEXTENSIBLE{};
- if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
- {
- *out = *CONTAINING_RECORD(in, const WAVEFORMATEXTENSIBLE, Format);
- out->Format.cbSize = sizeof(*out) - sizeof(out->Format);
- }
- else if(in->wFormatTag == WAVE_FORMAT_PCM)
- {
- out->Format = *in;
- out->Format.cbSize = 0;
- out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
- if(out->Format.nChannels == 1)
- out->dwChannelMask = MONO;
- else if(out->Format.nChannels == 2)
- out->dwChannelMask = STEREO;
- else
- ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
- out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- }
- else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
- {
- out->Format = *in;
- out->Format.cbSize = 0;
- out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
- if(out->Format.nChannels == 1)
- out->dwChannelMask = MONO;
- else if(out->Format.nChannels == 2)
- out->dwChannelMask = STEREO;
- else
- ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
- out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- }
- else
- {
- ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
- return false;
- }
- return true;
-}
-
-void TraceFormat(const char *msg, const WAVEFORMATEX *format)
-{
- constexpr size_t fmtex_extra_size{sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)};
- if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= fmtex_extra_size)
- {
- class GuidPrinter {
- char mMsg[64];
-
- public:
- GuidPrinter(const GUID &guid)
- {
- std::snprintf(mMsg, al::size(mMsg),
- "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
- DWORD{guid.Data1}, guid.Data2, guid.Data3,
- guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
- guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
- }
- const char *c_str() const { return mMsg; }
- };
-
- const WAVEFORMATEXTENSIBLE *fmtex{
- CONTAINING_RECORD(format, const WAVEFORMATEXTENSIBLE, Format)};
- TRACE("%s:\n"
- " FormatTag = 0x%04x\n"
- " Channels = %d\n"
- " SamplesPerSec = %lu\n"
- " AvgBytesPerSec = %lu\n"
- " BlockAlign = %d\n"
- " BitsPerSample = %d\n"
- " Size = %d\n"
- " Samples = %d\n"
- " ChannelMask = 0x%lx\n"
- " SubFormat = %s\n",
- msg, fmtex->Format.wFormatTag, fmtex->Format.nChannels, fmtex->Format.nSamplesPerSec,
- fmtex->Format.nAvgBytesPerSec, fmtex->Format.nBlockAlign, fmtex->Format.wBitsPerSample,
- fmtex->Format.cbSize, fmtex->Samples.wReserved, fmtex->dwChannelMask,
- GuidPrinter{fmtex->SubFormat}.c_str());
- }
- else
- TRACE("%s:\n"
- " FormatTag = 0x%04x\n"
- " Channels = %d\n"
- " SamplesPerSec = %lu\n"
- " AvgBytesPerSec = %lu\n"
- " BlockAlign = %d\n"
- " BitsPerSample = %d\n"
- " Size = %d\n",
- msg, format->wFormatTag, format->nChannels, format->nSamplesPerSec,
- format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample, format->cbSize);
-}
-
-
-enum class MsgType : unsigned int {
- OpenDevice,
- ResetDevice,
- StartDevice,
- StopDevice,
- CloseDevice,
- EnumeratePlayback,
- EnumerateCapture,
- QuitThread,
-
- Count
-};
-
-constexpr char MessageStr[static_cast<unsigned int>(MsgType::Count)][20]{
- "Open Device",
- "Reset Device",
- "Start Device",
- "Stop Device",
- "Close Device",
- "Enumerate Playback",
- "Enumerate Capture",
- "Quit"
-};
-
-
-/* Proxy interface used by the message handler. */
-struct WasapiProxy {
- virtual HRESULT openProxy() = 0;
- virtual void closeProxy() = 0;
-
- virtual HRESULT resetProxy() = 0;
- virtual HRESULT startProxy() = 0;
- virtual void stopProxy() = 0;
-
- struct Msg {
- MsgType mType;
- WasapiProxy *mProxy;
- std::promise<HRESULT> mPromise;
- };
- static std::deque<Msg> mMsgQueue;
- static std::mutex mMsgQueueLock;
- static std::condition_variable mMsgQueueCond;
-
- std::future<HRESULT> pushMessage(MsgType type)
- {
- std::promise<HRESULT> promise;
- std::future<HRESULT> future{promise.get_future()};
- { std::lock_guard<std::mutex> _{mMsgQueueLock};
- mMsgQueue.emplace_back(Msg{type, this, std::move(promise)});
- }
- mMsgQueueCond.notify_one();
- return future;
- }
-
- static std::future<HRESULT> pushMessageStatic(MsgType type)
- {
- std::promise<HRESULT> promise;
- std::future<HRESULT> future{promise.get_future()};
- { std::lock_guard<std::mutex> _{mMsgQueueLock};
- mMsgQueue.emplace_back(Msg{type, nullptr, std::move(promise)});
- }
- mMsgQueueCond.notify_one();
- return future;
- }
-
- static bool popMessage(Msg &msg)
- {
- std::unique_lock<std::mutex> lock{mMsgQueueLock};
- while(mMsgQueue.empty())
- mMsgQueueCond.wait(lock);
- msg = std::move(mMsgQueue.front());
- mMsgQueue.pop_front();
- return msg.mType != MsgType::QuitThread;
- }
-
- static int messageHandler(std::promise<HRESULT> *promise);
-};
-std::deque<WasapiProxy::Msg> WasapiProxy::mMsgQueue;
-std::mutex WasapiProxy::mMsgQueueLock;
-std::condition_variable WasapiProxy::mMsgQueueCond;
-
-int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
-{
- TRACE("Starting message thread\n");
-
- HRESULT cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(FAILED(cohr))
- {
- WARN("Failed to initialize COM: 0x%08lx\n", cohr);
- promise->set_value(cohr);
- return 0;
- }
-
- void *ptr{};
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr)};
- if(FAILED(hr))
- {
- WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
- promise->set_value(hr);
- CoUninitialize();
- return 0;
- }
- auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
- Enumerator->Release();
- Enumerator = nullptr;
- CoUninitialize();
-
- TRACE("Message thread initialization complete\n");
- promise->set_value(S_OK);
- promise = nullptr;
-
- TRACE("Starting message loop\n");
- ALuint deviceCount{0};
- Msg msg;
- while(popMessage(msg))
- {
- TRACE("Got message \"%s\" (0x%04x, this=%p)\n",
- MessageStr[static_cast<unsigned int>(msg.mType)], static_cast<unsigned int>(msg.mType),
- msg.mProxy);
-
- switch(msg.mType)
- {
- case MsgType::OpenDevice:
- hr = cohr = S_OK;
- if(++deviceCount == 1)
- hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(SUCCEEDED(hr))
- hr = msg.mProxy->openProxy();
- msg.mPromise.set_value(hr);
-
- if(FAILED(hr))
- {
- if(--deviceCount == 0 && SUCCEEDED(cohr))
- CoUninitialize();
- }
- continue;
-
- case MsgType::ResetDevice:
- hr = msg.mProxy->resetProxy();
- msg.mPromise.set_value(hr);
- continue;
-
- case MsgType::StartDevice:
- hr = msg.mProxy->startProxy();
- msg.mPromise.set_value(hr);
- continue;
-
- case MsgType::StopDevice:
- msg.mProxy->stopProxy();
- msg.mPromise.set_value(S_OK);
- continue;
-
- case MsgType::CloseDevice:
- msg.mProxy->closeProxy();
- msg.mPromise.set_value(S_OK);
-
- if(--deviceCount == 0)
- CoUninitialize();
- continue;
-
- case MsgType::EnumeratePlayback:
- case MsgType::EnumerateCapture:
- hr = cohr = S_OK;
- if(++deviceCount == 1)
- hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(SUCCEEDED(hr))
- hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr);
- if(FAILED(hr))
- msg.mPromise.set_value(hr);
- else
- {
- Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
-
- if(msg.mType == MsgType::EnumeratePlayback)
- hr = probe_devices(Enumerator, eRender, PlaybackDevices);
- else if(msg.mType == MsgType::EnumerateCapture)
- hr = probe_devices(Enumerator, eCapture, CaptureDevices);
- msg.mPromise.set_value(hr);
-
- Enumerator->Release();
- Enumerator = nullptr;
- }
-
- if(--deviceCount == 0 && SUCCEEDED(cohr))
- CoUninitialize();
- continue;
-
- default:
- ERR("Unexpected message: %u\n", static_cast<unsigned int>(msg.mType));
- msg.mPromise.set_value(E_FAIL);
- continue;
- }
- }
- TRACE("Message loop finished\n");
-
- return 0;
-}
-
-
-struct WasapiPlayback final : public BackendBase, WasapiProxy {
- WasapiPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WasapiPlayback() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- HRESULT openProxy() override;
- void closeProxy() override;
-
- ALCboolean reset() override;
- HRESULT resetProxy() override;
- ALCboolean start() override;
- HRESULT startProxy() override;
- void stop() override;
- void stopProxy() override;
-
- ClockLatency getClockLatency() override;
-
- std::wstring mDevId;
-
- IMMDevice *mMMDev{nullptr};
- IAudioClient *mClient{nullptr};
- IAudioRenderClient *mRender{nullptr};
- HANDLE mNotifyEvent{nullptr};
-
- std::atomic<UINT32> mPadding{0u};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WasapiPlayback)
-};
-
-WasapiPlayback::~WasapiPlayback()
-{
- pushMessage(MsgType::CloseDevice).wait();
-
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-}
-
-
-FORCE_ALIGN int WasapiPlayback::mixerProc()
-{
- HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(FAILED(hr))
- {
- ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
- return 1;
- }
-
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- const ALuint update_size{mDevice->UpdateSize};
- const UINT32 buffer_len{mDevice->BufferSize};
- while(!mKillNow.load(std::memory_order_relaxed))
- {
- UINT32 written;
- hr = mClient->GetCurrentPadding(&written);
- if(FAILED(hr))
- {
- ERR("Failed to get padding: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failed to retrieve buffer padding: 0x%08lx", hr);
- break;
- }
- mPadding.store(written, std::memory_order_relaxed);
-
- ALuint len{buffer_len - written};
- if(len < update_size)
- {
- DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
- if(res != WAIT_OBJECT_0)
- ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
- continue;
- }
-
- BYTE *buffer;
- hr = mRender->GetBuffer(len, &buffer);
- if(SUCCEEDED(hr))
- {
- lock();
- aluMixData(mDevice, buffer, len);
- mPadding.store(written + len, std::memory_order_relaxed);
- unlock();
- hr = mRender->ReleaseBuffer(len, 0);
- }
- if(FAILED(hr))
- {
- ERR("Failed to buffer data: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "Failed to send playback samples: 0x%08lx", hr);
- break;
- }
- }
- mPadding.store(0u, std::memory_order_release);
-
- CoUninitialize();
- return 0;
-}
-
-
-ALCenum WasapiPlayback::open(const ALCchar *name)
-{
- HRESULT hr{S_OK};
-
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(mNotifyEvent == nullptr)
- {
- ERR("Failed to create notify events: %lu\n", GetLastError());
- hr = E_FAIL;
- }
-
- if(SUCCEEDED(hr))
- {
- if(name)
- {
- if(PlaybackDevices.empty())
- pushMessage(MsgType::EnumeratePlayback).wait();
-
- hr = E_FAIL;
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name || entry.endpoint_guid == name; }
- );
- if(iter == PlaybackDevices.cend())
- {
- std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [&wname](const DevMap &entry) -> bool
- { return entry.devid == wname; }
- );
- }
- if(iter == PlaybackDevices.cend())
- WARN("Failed to find device name matching \"%s\"\n", name);
- else
- {
- mDevId = iter->devid;
- mDevice->DeviceName = iter->name;
- hr = S_OK;
- }
- }
- }
-
- if(SUCCEEDED(hr))
- hr = pushMessage(MsgType::OpenDevice).get();
-
- if(FAILED(hr))
- {
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-
- mDevId.clear();
-
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- return ALC_NO_ERROR;
-}
-
-HRESULT WasapiPlayback::openProxy()
-{
- void *ptr;
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
- {
- auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
- if(mDevId.empty())
- hr = Enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &mMMDev);
- else
- hr = Enumerator->GetDevice(mDevId.c_str(), &mMMDev);
- Enumerator->Release();
- }
- if(SUCCEEDED(hr))
- hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(SUCCEEDED(hr))
- {
- mClient = static_cast<IAudioClient*>(ptr);
- if(mDevice->DeviceName.empty())
- mDevice->DeviceName = get_device_name_and_guid(mMMDev).first;
- }
-
- if(FAILED(hr))
- {
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
- }
-
- return hr;
-}
-
-void WasapiPlayback::closeProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
-}
-
-
-ALCboolean WasapiPlayback::reset()
-{
- HRESULT hr{pushMessage(MsgType::ResetDevice).get()};
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-HRESULT WasapiPlayback::resetProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- void *ptr;
- HRESULT hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(FAILED(hr))
- {
- ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
- return hr;
- }
- mClient = static_cast<IAudioClient*>(ptr);
-
- WAVEFORMATEX *wfx;
- hr = mClient->GetMixFormat(&wfx);
- if(FAILED(hr))
- {
- ERR("Failed to get mix format: 0x%08lx\n", hr);
- return hr;
- }
-
- WAVEFORMATEXTENSIBLE OutputType;
- if(!MakeExtensible(&OutputType, wfx))
- {
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
- CoTaskMemFree(wfx);
- wfx = nullptr;
-
- const REFERENCE_TIME per_time{mDevice->UpdateSize * REFTIME_PER_SEC / mDevice->Frequency};
- const REFERENCE_TIME buf_time{mDevice->BufferSize * REFTIME_PER_SEC / mDevice->Frequency};
-
- if(!mDevice->Flags.get<FrequencyRequest>())
- mDevice->Frequency = OutputType.Format.nSamplesPerSec;
- if(!mDevice->Flags.get<ChannelsRequest>())
- {
- if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
- mDevice->FmtChans = DevFmtMono;
- else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
- mDevice->FmtChans = DevFmtStereo;
- else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
- mDevice->FmtChans = DevFmtQuad;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
- mDevice->FmtChans = DevFmtX51;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
- mDevice->FmtChans = DevFmtX51Rear;
- else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
- mDevice->FmtChans = DevFmtX61;
- else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
- mDevice->FmtChans = DevFmtX71;
- else
- ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
- }
-
- OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- OutputType.Format.nChannels = 1;
- OutputType.dwChannelMask = MONO;
- break;
- case DevFmtAmbi3D:
- mDevice->FmtChans = DevFmtStereo;
- /*fall-through*/
- case DevFmtStereo:
- OutputType.Format.nChannels = 2;
- OutputType.dwChannelMask = STEREO;
- break;
- case DevFmtQuad:
- OutputType.Format.nChannels = 4;
- OutputType.dwChannelMask = QUAD;
- break;
- case DevFmtX51:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1;
- break;
- case DevFmtX51Rear:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1REAR;
- break;
- case DevFmtX61:
- OutputType.Format.nChannels = 7;
- OutputType.dwChannelMask = X6DOT1;
- break;
- case DevFmtX71:
- OutputType.Format.nChannels = 8;
- OutputType.dwChannelMask = X7DOT1;
- break;
- }
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- /* fall-through */
- case DevFmtUByte:
- OutputType.Format.wBitsPerSample = 8;
- OutputType.Samples.wValidBitsPerSample = 8;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- /* fall-through */
- case DevFmtShort:
- OutputType.Format.wBitsPerSample = 16;
- OutputType.Samples.wValidBitsPerSample = 16;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- /* fall-through */
- case DevFmtInt:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtFloat:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- break;
- }
- OutputType.Format.nSamplesPerSec = mDevice->Frequency;
-
- OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
- OutputType.Format.wBitsPerSample / 8;
- OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
- OutputType.Format.nBlockAlign;
-
- TraceFormat("Requesting playback format", &OutputType.Format);
- hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
- if(FAILED(hr))
- {
- ERR("Failed to check format support: 0x%08lx\n", hr);
- hr = mClient->GetMixFormat(&wfx);
- }
- if(FAILED(hr))
- {
- ERR("Failed to find a supported format: 0x%08lx\n", hr);
- return hr;
- }
-
- if(wfx != nullptr)
- {
- TraceFormat("Got playback format", wfx);
- if(!MakeExtensible(&OutputType, wfx))
- {
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
- CoTaskMemFree(wfx);
- wfx = nullptr;
-
- mDevice->Frequency = OutputType.Format.nSamplesPerSec;
- if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
- mDevice->FmtChans = DevFmtMono;
- else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
- mDevice->FmtChans = DevFmtStereo;
- else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
- mDevice->FmtChans = DevFmtQuad;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
- mDevice->FmtChans = DevFmtX51;
- else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
- mDevice->FmtChans = DevFmtX51Rear;
- else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
- mDevice->FmtChans = DevFmtX61;
- else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
- mDevice->FmtChans = DevFmtX71;
- else
- {
- ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
- mDevice->FmtChans = DevFmtStereo;
- OutputType.Format.nChannels = 2;
- OutputType.dwChannelMask = STEREO;
- }
-
- if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
- {
- if(OutputType.Format.wBitsPerSample == 8)
- mDevice->FmtType = DevFmtUByte;
- else if(OutputType.Format.wBitsPerSample == 16)
- mDevice->FmtType = DevFmtShort;
- else if(OutputType.Format.wBitsPerSample == 32)
- mDevice->FmtType = DevFmtInt;
- else
- {
- mDevice->FmtType = DevFmtShort;
- OutputType.Format.wBitsPerSample = 16;
- }
- }
- else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
- {
- mDevice->FmtType = DevFmtFloat;
- OutputType.Format.wBitsPerSample = 32;
- }
- else
- {
- ERR("Unhandled format sub-type\n");
- mDevice->FmtType = DevFmtShort;
- if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
- OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.wBitsPerSample = 16;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- }
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
- }
-
- EndpointFormFactor formfactor = UnknownFormFactor;
- get_device_formfactor(mMMDev, &formfactor);
- mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
- (formfactor == Headphones || formfactor == Headset));
-
- SetDefaultWFXChannelOrder(mDevice);
-
- hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time,
- 0, &OutputType.Format, nullptr);
- if(FAILED(hr))
- {
- ERR("Failed to initialize audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- UINT32 buffer_len, min_len;
- REFERENCE_TIME min_per;
- hr = mClient->GetDevicePeriod(&min_per, nullptr);
- if(SUCCEEDED(hr))
- hr = mClient->GetBufferSize(&buffer_len);
- if(FAILED(hr))
- {
- ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
- return hr;
- }
-
- /* Find the nearest multiple of the period size to the update size */
- if(min_per < per_time)
- min_per *= maxi64((per_time + min_per/2) / min_per, 1);
- min_len = (UINT32)ScaleCeil(min_per, mDevice->Frequency, REFTIME_PER_SEC);
- min_len = minu(min_len, buffer_len/2);
-
- mDevice->UpdateSize = min_len;
- mDevice->BufferSize = buffer_len;
-
- hr = mClient->SetEventHandle(mNotifyEvent);
- if(FAILED(hr))
- {
- ERR("Failed to set event handle: 0x%08lx\n", hr);
- return hr;
- }
-
- return hr;
-}
-
-
-ALCboolean WasapiPlayback::start()
-{
- HRESULT hr{pushMessage(MsgType::StartDevice).get()};
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-HRESULT WasapiPlayback::startProxy()
-{
- ResetEvent(mNotifyEvent);
-
- HRESULT hr = mClient->Start();
- if(FAILED(hr))
- {
- ERR("Failed to start audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- void *ptr;
- hr = mClient->GetService(IID_IAudioRenderClient, &ptr);
- if(SUCCEEDED(hr))
- {
- mRender = static_cast<IAudioRenderClient*>(ptr);
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this};
- }
- catch(...) {
- mRender->Release();
- mRender = nullptr;
- ERR("Failed to start thread\n");
- hr = E_FAIL;
- }
- }
-
- if(FAILED(hr))
- mClient->Stop();
-
- return hr;
-}
-
-
-void WasapiPlayback::stop()
-{ pushMessage(MsgType::StopDevice).wait(); }
-
-void WasapiPlayback::stopProxy()
-{
- if(!mRender || !mThread.joinable())
- return;
-
- mKillNow.store(true, std::memory_order_release);
- mThread.join();
-
- mRender->Release();
- mRender = nullptr;
- mClient->Stop();
-}
-
-
-ClockLatency WasapiPlayback::getClockLatency()
-{
- ClockLatency ret;
-
- lock();
- ret.ClockTime = GetDeviceClockTime(mDevice);
- ret.Latency = std::chrono::seconds{mPadding.load(std::memory_order_relaxed)};
- ret.Latency /= mDevice->Frequency;
- unlock();
-
- return ret;
-}
-
-
-struct WasapiCapture final : public BackendBase, WasapiProxy {
- WasapiCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WasapiCapture() override;
-
- int recordProc();
-
- ALCenum open(const ALCchar *name) override;
- HRESULT openProxy() override;
- void closeProxy() override;
-
- HRESULT resetProxy() override;
- ALCboolean start() override;
- HRESULT startProxy() override;
- void stop() override;
- void stopProxy() override;
-
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- std::wstring mDevId;
-
- IMMDevice *mMMDev{nullptr};
- IAudioClient *mClient{nullptr};
- IAudioCaptureClient *mCapture{nullptr};
- HANDLE mNotifyEvent{nullptr};
-
- ChannelConverterPtr mChannelConv;
- SampleConverterPtr mSampleConv;
- RingBufferPtr mRing;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WasapiCapture)
-};
-
-WasapiCapture::~WasapiCapture()
-{
- pushMessage(MsgType::CloseDevice).wait();
-
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-}
-
-
-FORCE_ALIGN int WasapiCapture::recordProc()
-{
- HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if(FAILED(hr))
- {
- ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
- aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
- return 1;
- }
-
- althrd_setname(RECORD_THREAD_NAME);
-
- al::vector<float> samples;
- while(!mKillNow.load(std::memory_order_relaxed))
- {
- UINT32 avail;
- hr = mCapture->GetNextPacketSize(&avail);
- if(FAILED(hr))
- ERR("Failed to get next packet size: 0x%08lx\n", hr);
- else if(avail > 0)
- {
- UINT32 numsamples;
- DWORD flags;
- BYTE *rdata;
-
- hr = mCapture->GetBuffer(&rdata, &numsamples, &flags, nullptr, nullptr);
- if(FAILED(hr))
- ERR("Failed to get capture buffer: 0x%08lx\n", hr);
- else
- {
- if(mChannelConv)
- {
- samples.resize(numsamples*2);
- mChannelConv->convert(rdata, samples.data(), numsamples);
- rdata = reinterpret_cast<BYTE*>(samples.data());
- }
-
- auto data = mRing->getWriteVector();
-
- size_t dstframes;
- if(mSampleConv)
- {
- const ALvoid *srcdata{rdata};
- auto srcframes = static_cast<ALsizei>(numsamples);
-
- dstframes = mSampleConv->convert(&srcdata, &srcframes, data.first.buf,
- static_cast<ALsizei>(minz(data.first.len, INT_MAX)));
- if(srcframes > 0 && dstframes == data.first.len && data.second.len > 0)
- {
- /* If some source samples remain, all of the first dest
- * block was filled, and there's space in the second
- * dest block, do another run for the second block.
- */
- dstframes += mSampleConv->convert(&srcdata, &srcframes, data.second.buf,
- static_cast<ALsizei>(minz(data.second.len, INT_MAX)));
- }
- }
- else
- {
- const auto framesize = static_cast<ALuint>(mDevice->frameSizeFromFmt());
- size_t len1 = minz(data.first.len, numsamples);
- size_t len2 = minz(data.second.len, numsamples-len1);
-
- memcpy(data.first.buf, rdata, len1*framesize);
- if(len2 > 0)
- memcpy(data.second.buf, rdata+len1*framesize, len2*framesize);
- dstframes = len1 + len2;
- }
-
- mRing->writeAdvance(dstframes);
-
- hr = mCapture->ReleaseBuffer(numsamples);
- if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
- }
- }
-
- if(FAILED(hr))
- {
- aluHandleDisconnect(mDevice, "Failed to capture samples: 0x%08lx", hr);
- break;
- }
-
- DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
- if(res != WAIT_OBJECT_0)
- ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
- }
-
- CoUninitialize();
- return 0;
-}
-
-
-ALCenum WasapiCapture::open(const ALCchar *name)
-{
- HRESULT hr{S_OK};
-
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(mNotifyEvent == nullptr)
- {
- ERR("Failed to create notify event: %lu\n", GetLastError());
- hr = E_FAIL;
- }
-
- if(SUCCEEDED(hr))
- {
- if(name)
- {
- if(CaptureDevices.empty())
- pushMessage(MsgType::EnumerateCapture).wait();
-
- hr = E_FAIL;
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name || entry.endpoint_guid == name; }
- );
- if(iter == CaptureDevices.cend())
- {
- std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [&wname](const DevMap &entry) -> bool
- { return entry.devid == wname; }
- );
- }
- if(iter == CaptureDevices.cend())
- WARN("Failed to find device name matching \"%s\"\n", name);
- else
- {
- mDevId = iter->devid;
- mDevice->DeviceName = iter->name;
- hr = S_OK;
- }
- }
- }
-
- if(SUCCEEDED(hr))
- hr = pushMessage(MsgType::OpenDevice).get();
-
- if(FAILED(hr))
- {
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-
- mDevId.clear();
-
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- hr = pushMessage(MsgType::ResetDevice).get();
- if(FAILED(hr))
- {
- if(hr == E_OUTOFMEMORY)
- return ALC_OUT_OF_MEMORY;
- return ALC_INVALID_VALUE;
- }
-
- return ALC_NO_ERROR;
-}
-
-HRESULT WasapiCapture::openProxy()
-{
- void *ptr;
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
- {
- auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
- if(mDevId.empty())
- hr = Enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &mMMDev);
- else
- hr = Enumerator->GetDevice(mDevId.c_str(), &mMMDev);
- Enumerator->Release();
- }
- if(SUCCEEDED(hr))
- hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(SUCCEEDED(hr))
- {
- mClient = static_cast<IAudioClient*>(ptr);
- if(mDevice->DeviceName.empty())
- mDevice->DeviceName = get_device_name_and_guid(mMMDev).first;
- }
-
- if(FAILED(hr))
- {
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
- }
-
- return hr;
-}
-
-void WasapiCapture::closeProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- if(mMMDev)
- mMMDev->Release();
- mMMDev = nullptr;
-}
-
-HRESULT WasapiCapture::resetProxy()
-{
- if(mClient)
- mClient->Release();
- mClient = nullptr;
-
- void *ptr;
- HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)};
- if(FAILED(hr))
- {
- ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
- return hr;
- }
- mClient = static_cast<IAudioClient*>(ptr);
-
- // Make sure buffer is at least 100ms in size
- REFERENCE_TIME buf_time{mDevice->BufferSize * REFTIME_PER_SEC / mDevice->Frequency};
- buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
-
- WAVEFORMATEXTENSIBLE OutputType;
- OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- OutputType.Format.nChannels = 1;
- OutputType.dwChannelMask = MONO;
- break;
- case DevFmtStereo:
- OutputType.Format.nChannels = 2;
- OutputType.dwChannelMask = STEREO;
- break;
- case DevFmtQuad:
- OutputType.Format.nChannels = 4;
- OutputType.dwChannelMask = QUAD;
- break;
- case DevFmtX51:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1;
- break;
- case DevFmtX51Rear:
- OutputType.Format.nChannels = 6;
- OutputType.dwChannelMask = X5DOT1REAR;
- break;
- case DevFmtX61:
- OutputType.Format.nChannels = 7;
- OutputType.dwChannelMask = X6DOT1;
- break;
- case DevFmtX71:
- OutputType.Format.nChannels = 8;
- OutputType.dwChannelMask = X7DOT1;
- break;
-
- case DevFmtAmbi3D:
- return E_FAIL;
- }
- switch(mDevice->FmtType)
- {
- /* NOTE: Signedness doesn't matter, the converter will handle it. */
- case DevFmtByte:
- case DevFmtUByte:
- OutputType.Format.wBitsPerSample = 8;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtShort:
- case DevFmtUShort:
- OutputType.Format.wBitsPerSample = 16;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtInt:
- case DevFmtUInt:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- break;
- case DevFmtFloat:
- OutputType.Format.wBitsPerSample = 32;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- break;
- }
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
- OutputType.Format.nSamplesPerSec = mDevice->Frequency;
-
- OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
- OutputType.Format.wBitsPerSample / 8;
- OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
- OutputType.Format.nBlockAlign;
- OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
-
- TraceFormat("Requesting capture format", &OutputType.Format);
- WAVEFORMATEX *wfx;
- hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
- if(FAILED(hr))
- {
- ERR("Failed to check format support: 0x%08lx\n", hr);
- return hr;
- }
-
- mSampleConv = nullptr;
- mChannelConv = nullptr;
-
- if(wfx != nullptr)
- {
- TraceFormat("Got capture format", wfx);
- if(!(wfx->nChannels == OutputType.Format.nChannels ||
- (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
- (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
- {
- ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
- DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
- mDevice->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
- wfx->nSamplesPerSec);
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
-
- if(!MakeExtensible(&OutputType, wfx))
- {
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
- CoTaskMemFree(wfx);
- wfx = nullptr;
- }
-
- DevFmtType srcType;
- if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
- {
- if(OutputType.Format.wBitsPerSample == 8)
- srcType = DevFmtUByte;
- else if(OutputType.Format.wBitsPerSample == 16)
- srcType = DevFmtShort;
- else if(OutputType.Format.wBitsPerSample == 32)
- srcType = DevFmtInt;
- else
- {
- ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
- return E_FAIL;
- }
- }
- else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
- {
- if(OutputType.Format.wBitsPerSample == 32)
- srcType = DevFmtFloat;
- else
- {
- ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
- return E_FAIL;
- }
- }
- else
- {
- ERR("Unhandled format sub-type\n");
- return E_FAIL;
- }
-
- if(mDevice->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
- {
- mChannelConv = CreateChannelConverter(srcType, DevFmtStereo, mDevice->FmtChans);
- if(!mChannelConv)
- {
- ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
- return E_FAIL;
- }
- TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
- /* The channel converter always outputs float, so change the input type
- * for the resampler/type-converter.
- */
- srcType = DevFmtFloat;
- }
- else if(mDevice->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
- {
- mChannelConv = CreateChannelConverter(srcType, DevFmtMono, mDevice->FmtChans);
- if(!mChannelConv)
- {
- ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
- return E_FAIL;
- }
- TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
- srcType = DevFmtFloat;
- }
-
- if(mDevice->Frequency != OutputType.Format.nSamplesPerSec || mDevice->FmtType != srcType)
- {
- mSampleConv = CreateSampleConverter(srcType, mDevice->FmtType, mDevice->channelsFromFmt(),
- OutputType.Format.nSamplesPerSec, mDevice->Frequency, BSinc24Resampler);
- if(!mSampleConv)
- {
- ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
- DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
- mDevice->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
- return E_FAIL;
- }
- TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
- DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
- mDevice->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
- }
-
- hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time,
- 0, &OutputType.Format, nullptr);
- if(FAILED(hr))
- {
- ERR("Failed to initialize audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- UINT32 buffer_len;
- REFERENCE_TIME min_per;
- hr = mClient->GetDevicePeriod(&min_per, nullptr);
- if(SUCCEEDED(hr))
- hr = mClient->GetBufferSize(&buffer_len);
- if(FAILED(hr))
- {
- ERR("Failed to get buffer size: 0x%08lx\n", hr);
- return hr;
- }
- mDevice->UpdateSize = static_cast<ALuint>(ScaleCeil(min_per, mDevice->Frequency,
- REFTIME_PER_SEC));
- mDevice->BufferSize = buffer_len;
-
- buffer_len = maxu(mDevice->BufferSize, buffer_len);
- mRing = CreateRingBuffer(buffer_len, mDevice->frameSizeFromFmt(), false);
- if(!mRing)
- {
- ERR("Failed to allocate capture ring buffer\n");
- return E_OUTOFMEMORY;
- }
-
- hr = mClient->SetEventHandle(mNotifyEvent);
- if(FAILED(hr))
- {
- ERR("Failed to set event handle: 0x%08lx\n", hr);
- return hr;
- }
-
- return hr;
-}
-
-
-ALCboolean WasapiCapture::start()
-{
- HRESULT hr{pushMessage(MsgType::StartDevice).get()};
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-HRESULT WasapiCapture::startProxy()
-{
- ResetEvent(mNotifyEvent);
-
- HRESULT hr{mClient->Start()};
- if(FAILED(hr))
- {
- ERR("Failed to start audio client: 0x%08lx\n", hr);
- return hr;
- }
-
- void *ptr;
- hr = mClient->GetService(IID_IAudioCaptureClient, &ptr);
- if(SUCCEEDED(hr))
- {
- mCapture = static_cast<IAudioCaptureClient*>(ptr);
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this};
- }
- catch(...) {
- mCapture->Release();
- mCapture = nullptr;
- ERR("Failed to start thread\n");
- hr = E_FAIL;
- }
- }
-
- if(FAILED(hr))
- {
- mClient->Stop();
- mClient->Reset();
- }
-
- return hr;
-}
-
-
-void WasapiCapture::stop()
-{ pushMessage(MsgType::StopDevice).wait(); }
-
-void WasapiCapture::stopProxy()
-{
- if(!mCapture || !mThread.joinable())
- return;
-
- mKillNow.store(true, std::memory_order_release);
- mThread.join();
-
- mCapture->Release();
- mCapture = nullptr;
- mClient->Stop();
- mClient->Reset();
-}
-
-
-ALCuint WasapiCapture::availableSamples()
-{ return (ALCuint)mRing->readSpace(); }
-
-ALCenum WasapiCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-} // namespace
-
-
-bool WasapiBackendFactory::init()
-{
- static HRESULT InitResult{E_FAIL};
-
- if(FAILED(InitResult)) try
- {
- std::promise<HRESULT> promise;
- auto future = promise.get_future();
-
- std::thread{&WasapiProxy::messageHandler, &promise}.detach();
- InitResult = future.get();
- }
- catch(...) {
- }
-
- return SUCCEEDED(InitResult) ? ALC_TRUE : ALC_FALSE;
-}
-
-bool WasapiBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void WasapiBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const DevMap &entry) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- outnames->append(entry.name.c_str(), entry.name.length()+1);
- };
- HRESULT hr{};
- switch(type)
- {
- case DevProbe::Playback:
- hr = WasapiProxy::pushMessageStatic(MsgType::EnumeratePlayback).get();
- if(SUCCEEDED(hr))
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- hr = WasapiProxy::pushMessageStatic(MsgType::EnumerateCapture).get();
- if(SUCCEEDED(hr))
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr WasapiBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new WasapiPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new WasapiCapture{device}};
- return nullptr;
-}
-
-BackendFactory &WasapiBackendFactory::getFactory()
-{
- static WasapiBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/wasapi.h b/Alc/backends/wasapi.h
deleted file mode 100644
index 067dd259..00000000
--- a/Alc/backends/wasapi.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_WASAPI_H
-#define BACKENDS_WASAPI_H
-
-#include "backends/base.h"
-
-struct WasapiBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_WASAPI_H */
diff --git a/Alc/backends/wave.cpp b/Alc/backends/wave.cpp
deleted file mode 100644
index 67ed7e79..00000000
--- a/Alc/backends/wave.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/wave.h"
-
-#include <algorithm>
-#include <atomic>
-#include <cerrno>
-#include <chrono>
-#include <cstdint>
-#include <cstdio>
-#include <cstring>
-#include <exception>
-#include <functional>
-#include <thread>
-
-#include "AL/al.h"
-
-#include "alcmain.h"
-#include "alconfig.h"
-#include "almalloc.h"
-#include "alnumeric.h"
-#include "alu.h"
-#include "compat.h"
-#include "logging.h"
-#include "threads.h"
-#include "vector.h"
-
-
-namespace {
-
-using std::chrono::seconds;
-using std::chrono::milliseconds;
-using std::chrono::nanoseconds;
-
-constexpr ALCchar waveDevice[] = "Wave File Writer";
-
-constexpr ALubyte SUBTYPE_PCM[]{
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
- 0x00, 0x38, 0x9b, 0x71
-};
-constexpr ALubyte SUBTYPE_FLOAT[]{
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
- 0x00, 0x38, 0x9b, 0x71
-};
-
-constexpr ALubyte SUBTYPE_BFORMAT_PCM[]{
- 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
- 0xca, 0x00, 0x00, 0x00
-};
-
-constexpr ALubyte SUBTYPE_BFORMAT_FLOAT[]{
- 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
- 0xca, 0x00, 0x00, 0x00
-};
-
-void fwrite16le(ALushort val, FILE *f)
-{
- ALubyte data[2]{ static_cast<ALubyte>(val&0xff), static_cast<ALubyte>((val>>8)&0xff) };
- fwrite(data, 1, 2, f);
-}
-
-void fwrite32le(ALuint val, FILE *f)
-{
- ALubyte data[4]{ static_cast<ALubyte>(val&0xff), static_cast<ALubyte>((val>>8)&0xff),
- static_cast<ALubyte>((val>>16)&0xff), static_cast<ALubyte>((val>>24)&0xff) };
- fwrite(data, 1, 4, f);
-}
-
-
-struct WaveBackend final : public BackendBase {
- WaveBackend(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WaveBackend() override;
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- FILE *mFile{nullptr};
- long mDataStart{-1};
-
- al::vector<ALbyte> mBuffer;
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WaveBackend)
-};
-
-WaveBackend::~WaveBackend()
-{
- if(mFile)
- fclose(mFile);
- mFile = nullptr;
-}
-
-int WaveBackend::mixerProc()
-{
- const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
-
- althrd_setname(MIXER_THREAD_NAME);
-
- const ALsizei frameSize{mDevice->frameSizeFromFmt()};
-
- int64_t done{0};
- auto start = std::chrono::steady_clock::now();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- auto now = std::chrono::steady_clock::now();
-
- /* This converts from nanoseconds to nanosamples, then to samples. */
- int64_t avail{std::chrono::duration_cast<seconds>((now-start) *
- mDevice->Frequency).count()};
- if(avail-done < mDevice->UpdateSize)
- {
- std::this_thread::sleep_for(restTime);
- continue;
- }
- while(avail-done >= mDevice->UpdateSize)
- {
- lock();
- aluMixData(mDevice, mBuffer.data(), mDevice->UpdateSize);
- unlock();
- done += mDevice->UpdateSize;
-
- if(!IS_LITTLE_ENDIAN)
- {
- const ALsizei bytesize{mDevice->bytesFromFmt()};
- ALsizei i;
-
- if(bytesize == 2)
- {
- ALushort *samples = reinterpret_cast<ALushort*>(mBuffer.data());
- const auto len = static_cast<ALsizei>(mBuffer.size() / 2);
- for(i = 0;i < len;i++)
- {
- ALushort samp = samples[i];
- samples[i] = (samp>>8) | (samp<<8);
- }
- }
- else if(bytesize == 4)
- {
- ALuint *samples = reinterpret_cast<ALuint*>(mBuffer.data());
- const auto len = static_cast<ALsizei>(mBuffer.size() / 4);
- for(i = 0;i < len;i++)
- {
- ALuint samp = samples[i];
- samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
- ((samp<<8)&0x00ff0000) | (samp<<24);
- }
- }
- }
-
- size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)};
- (void)fs;
- if(ferror(mFile))
- {
- ERR("Error writing to file\n");
- aluHandleDisconnect(mDevice, "Failed to write playback samples");
- break;
- }
- }
-
- /* For every completed second, increment the start time and reduce the
- * samples done. This prevents the difference between the start time
- * and current time from growing too large, while maintaining the
- * correct number of samples to render.
- */
- if(done >= mDevice->Frequency)
- {
- seconds s{done/mDevice->Frequency};
- start += s;
- done -= mDevice->Frequency*s.count();
- }
- }
-
- return 0;
-}
-
-ALCenum WaveBackend::open(const ALCchar *name)
-{
- const char *fname{GetConfigValue(nullptr, "wave", "file", "")};
- if(!fname[0]) return ALC_INVALID_VALUE;
-
- if(!name)
- name = waveDevice;
- else if(strcmp(name, waveDevice) != 0)
- return ALC_INVALID_VALUE;
-
-#ifdef _WIN32
- {
- std::wstring wname = utf8_to_wstr(fname);
- mFile = _wfopen(wname.c_str(), L"wb");
- }
-#else
- mFile = fopen(fname, "wb");
-#endif
- if(!mFile)
- {
- ERR("Could not open file '%s': %s\n", fname, strerror(errno));
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = name;
-
- return ALC_NO_ERROR;
-}
-
-ALCboolean WaveBackend::reset()
-{
- ALuint channels=0, bytes=0, chanmask=0;
- int isbformat = 0;
- size_t val;
-
- fseek(mFile, 0, SEEK_SET);
- clearerr(mFile);
-
- if(GetConfigValueBool(nullptr, "wave", "bformat", 0))
- {
- mDevice->FmtChans = DevFmtAmbi3D;
- mDevice->mAmbiOrder = 1;
- }
-
- switch(mDevice->FmtType)
- {
- case DevFmtByte:
- mDevice->FmtType = DevFmtUByte;
- break;
- case DevFmtUShort:
- mDevice->FmtType = DevFmtShort;
- break;
- case DevFmtUInt:
- mDevice->FmtType = DevFmtInt;
- break;
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- case DevFmtFloat:
- break;
- }
- switch(mDevice->FmtChans)
- {
- case DevFmtMono: chanmask = 0x04; break;
- case DevFmtStereo: chanmask = 0x01 | 0x02; break;
- case DevFmtQuad: chanmask = 0x01 | 0x02 | 0x10 | 0x20; break;
- case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
- case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
- case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
- case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
- case DevFmtAmbi3D:
- /* .amb output requires FuMa */
- mDevice->mAmbiOrder = mini(mDevice->mAmbiOrder, 3);
- mDevice->mAmbiLayout = AmbiLayout::FuMa;
- mDevice->mAmbiScale = AmbiNorm::FuMa;
- isbformat = 1;
- chanmask = 0;
- break;
- }
- bytes = mDevice->bytesFromFmt();
- channels = mDevice->channelsFromFmt();
-
- rewind(mFile);
-
- fputs("RIFF", mFile);
- fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close
-
- fputs("WAVE", mFile);
-
- fputs("fmt ", mFile);
- fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
-
- // 16-bit val, format type id (extensible: 0xFFFE)
- fwrite16le(0xFFFE, mFile);
- // 16-bit val, channel count
- fwrite16le(channels, mFile);
- // 32-bit val, frequency
- fwrite32le(mDevice->Frequency, mFile);
- // 32-bit val, bytes per second
- fwrite32le(mDevice->Frequency * channels * bytes, mFile);
- // 16-bit val, frame size
- fwrite16le(channels * bytes, mFile);
- // 16-bit val, bits per sample
- fwrite16le(bytes * 8, mFile);
- // 16-bit val, extra byte count
- fwrite16le(22, mFile);
- // 16-bit val, valid bits per sample
- fwrite16le(bytes * 8, mFile);
- // 32-bit val, channel mask
- fwrite32le(chanmask, mFile);
- // 16 byte GUID, sub-type format
- val = fwrite((mDevice->FmtType == DevFmtFloat) ?
- (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
- (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile);
- (void)val;
-
- fputs("data", mFile);
- fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close
-
- if(ferror(mFile))
- {
- ERR("Error writing header: %s\n", strerror(errno));
- return ALC_FALSE;
- }
- mDataStart = ftell(mFile);
-
- SetDefaultWFXChannelOrder(mDevice);
-
- const ALuint bufsize{mDevice->frameSizeFromFmt() * mDevice->UpdateSize};
- mBuffer.resize(bufsize);
-
- return ALC_TRUE;
-}
-
-ALCboolean WaveBackend::start()
-{
- try {
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WaveBackend::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void WaveBackend::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- long size{ftell(mFile)};
- if(size > 0)
- {
- long dataLen{size - mDataStart};
- if(fseek(mFile, mDataStart-4, SEEK_SET) == 0)
- fwrite32le(dataLen, mFile); // 'data' header len
- if(fseek(mFile, 4, SEEK_SET) == 0)
- fwrite32le(size-8, mFile); // 'WAVE' header len
- }
-}
-
-} // namespace
-
-
-bool WaveBackendFactory::init()
-{ return true; }
-
-bool WaveBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback; }
-
-void WaveBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- switch(type)
- {
- case DevProbe::Playback:
- /* Includes null char. */
- outnames->append(waveDevice, sizeof(waveDevice));
- break;
- case DevProbe::Capture:
- break;
- }
-}
-
-BackendPtr WaveBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new WaveBackend{device}};
- return nullptr;
-}
-
-BackendFactory &WaveBackendFactory::getFactory()
-{
- static WaveBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/wave.h b/Alc/backends/wave.h
deleted file mode 100644
index b9b62d7f..00000000
--- a/Alc/backends/wave.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_WAVE_H
-#define BACKENDS_WAVE_H
-
-#include "backends/base.h"
-
-struct WaveBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_WAVE_H */
diff --git a/Alc/backends/winmm.cpp b/Alc/backends/winmm.cpp
deleted file mode 100644
index cd32e95b..00000000
--- a/Alc/backends/winmm.cpp
+++ /dev/null
@@ -1,640 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include "backends/winmm.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
-#include <windows.h>
-#include <mmsystem.h>
-
-#include <array>
-#include <atomic>
-#include <thread>
-#include <vector>
-#include <string>
-#include <algorithm>
-#include <functional>
-
-#include "alcmain.h"
-#include "alu.h"
-#include "ringbuffer.h"
-#include "threads.h"
-#include "compat.h"
-
-#ifndef WAVE_FORMAT_IEEE_FLOAT
-#define WAVE_FORMAT_IEEE_FLOAT 0x0003
-#endif
-
-namespace {
-
-#define DEVNAME_HEAD "OpenAL Soft on "
-
-
-al::vector<std::string> PlaybackDevices;
-al::vector<std::string> CaptureDevices;
-
-bool checkName(const al::vector<std::string> &list, const std::string &name)
-{ return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
-
-void ProbePlaybackDevices(void)
-{
- PlaybackDevices.clear();
-
- ALuint numdevs{waveOutGetNumDevs()};
- PlaybackDevices.reserve(numdevs);
- for(ALuint i{0};i < numdevs;i++)
- {
- std::string dname;
-
- WAVEOUTCAPSW WaveCaps{};
- if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
- {
- const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
-
- int count{1};
- std::string newname{basename};
- while(checkName(PlaybackDevices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- dname = std::move(newname);
-
- TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
- }
- PlaybackDevices.emplace_back(std::move(dname));
- }
-}
-
-void ProbeCaptureDevices(void)
-{
- CaptureDevices.clear();
-
- ALuint numdevs{waveInGetNumDevs()};
- CaptureDevices.reserve(numdevs);
- for(ALuint i{0};i < numdevs;i++)
- {
- std::string dname;
-
- WAVEINCAPSW WaveCaps{};
- if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
- {
- const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
-
- int count{1};
- std::string newname{basename};
- while(checkName(CaptureDevices, newname))
- {
- newname = basename;
- newname += " #";
- newname += std::to_string(++count);
- }
- dname = std::move(newname);
-
- TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
- }
- CaptureDevices.emplace_back(std::move(dname));
- }
-}
-
-
-struct WinMMPlayback final : public BackendBase {
- WinMMPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WinMMPlayback() override;
-
- static void CALLBACK waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
- void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
-
- int mixerProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean reset() override;
- ALCboolean start() override;
- void stop() override;
-
- std::atomic<ALuint> mWritable{0u};
- al::semaphore mSem;
- int mIdx{0};
- std::array<WAVEHDR,4> mWaveBuffer{};
-
- HWAVEOUT mOutHdl{nullptr};
-
- WAVEFORMATEX mFormat{};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WinMMPlayback)
-};
-
-WinMMPlayback::~WinMMPlayback()
-{
- if(mOutHdl)
- waveOutClose(mOutHdl);
- mOutHdl = nullptr;
-
- al_free(mWaveBuffer[0].lpData);
- std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
-}
-
-
-void CALLBACK WinMMPlayback::waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
-{ reinterpret_cast<WinMMPlayback*>(instance)->waveOutProc(device, msg, param1, param2); }
-
-/* WinMMPlayback::waveOutProc
- *
- * Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is
- * completed and returns to the application (for more data)
- */
-void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PTR)
-{
- if(msg != WOM_DONE) return;
- mWritable.fetch_add(1, std::memory_order_acq_rel);
- mSem.post();
-}
-
-FORCE_ALIGN int WinMMPlayback::mixerProc()
-{
- SetRTPriority();
- althrd_setname(MIXER_THREAD_NAME);
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- ALsizei todo = mWritable.load(std::memory_order_acquire);
- if(todo < 1)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
-
- int widx{mIdx};
- do {
- WAVEHDR &waveHdr = mWaveBuffer[widx];
- widx = (widx+1) % mWaveBuffer.size();
-
- aluMixData(mDevice, waveHdr.lpData, mDevice->UpdateSize);
- mWritable.fetch_sub(1, std::memory_order_acq_rel);
- waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR));
- } while(--todo);
- mIdx = widx;
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum WinMMPlayback::open(const ALCchar *name)
-{
- if(PlaybackDevices.empty())
- ProbePlaybackDevices();
-
- // Find the Device ID matching the deviceName if valid
- auto iter = name ?
- std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
- PlaybackDevices.cbegin();
- if(iter == PlaybackDevices.cend()) return ALC_INVALID_VALUE;
- auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
-
-retry_open:
- mFormat = WAVEFORMATEX{};
- if(mDevice->FmtType == DevFmtFloat)
- {
- mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
- mFormat.wBitsPerSample = 32;
- }
- else
- {
- mFormat.wFormatTag = WAVE_FORMAT_PCM;
- if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
- mFormat.wBitsPerSample = 8;
- else
- mFormat.wBitsPerSample = 16;
- }
- mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
- mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
- mFormat.nSamplesPerSec = mDevice->Frequency;
- mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
- mFormat.cbSize = 0;
-
- MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMPlayback::waveOutProcC,
- reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
- if(res != MMSYSERR_NOERROR)
- {
- if(mDevice->FmtType == DevFmtFloat)
- {
- mDevice->FmtType = DevFmtShort;
- goto retry_open;
- }
- ERR("waveOutOpen failed: %u\n", res);
- return ALC_INVALID_VALUE;
- }
-
- mDevice->DeviceName = PlaybackDevices[DeviceID];
- return ALC_NO_ERROR;
-}
-
-ALCboolean WinMMPlayback::reset()
-{
- mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} *
- mFormat.nSamplesPerSec / mDevice->Frequency);
- mDevice->BufferSize = (mDevice->BufferSize+3) & ~0x3;
- mDevice->UpdateSize = mDevice->BufferSize / 4;
- mDevice->Frequency = mFormat.nSamplesPerSec;
-
- if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
- {
- if(mFormat.wBitsPerSample == 32)
- mDevice->FmtType = DevFmtFloat;
- else
- {
- ERR("Unhandled IEEE float sample depth: %d\n", mFormat.wBitsPerSample);
- return ALC_FALSE;
- }
- }
- else if(mFormat.wFormatTag == WAVE_FORMAT_PCM)
- {
- if(mFormat.wBitsPerSample == 16)
- mDevice->FmtType = DevFmtShort;
- else if(mFormat.wBitsPerSample == 8)
- mDevice->FmtType = DevFmtUByte;
- else
- {
- ERR("Unhandled PCM sample depth: %d\n", mFormat.wBitsPerSample);
- return ALC_FALSE;
- }
- }
- else
- {
- ERR("Unhandled format tag: 0x%04x\n", mFormat.wFormatTag);
- return ALC_FALSE;
- }
-
- if(mFormat.nChannels == 2)
- mDevice->FmtChans = DevFmtStereo;
- else if(mFormat.nChannels == 1)
- mDevice->FmtChans = DevFmtMono;
- else
- {
- ERR("Unhandled channel count: %d\n", mFormat.nChannels);
- return ALC_FALSE;
- }
- SetDefaultWFXChannelOrder(mDevice);
-
- ALuint BufferSize{mDevice->UpdateSize * mDevice->frameSizeFromFmt()};
-
- al_free(mWaveBuffer[0].lpData);
- mWaveBuffer[0] = WAVEHDR{};
- mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
- mWaveBuffer[0].dwBufferLength = BufferSize;
- for(size_t i{1};i < mWaveBuffer.size();i++)
- {
- mWaveBuffer[i] = WAVEHDR{};
- mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
- mWaveBuffer[i].dwBufferLength = BufferSize;
- }
- mIdx = 0;
-
- return ALC_TRUE;
-}
-
-ALCboolean WinMMPlayback::start()
-{
- try {
- std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
- [this](WAVEHDR &waveHdr) -> void
- { waveOutPrepareHeader(mOutHdl, &waveHdr, static_cast<UINT>(sizeof(WAVEHDR))); }
- );
- mWritable.store(static_cast<ALuint>(mWaveBuffer.size()), std::memory_order_release);
-
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), this};
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void WinMMPlayback::stop()
-{
- if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
- return;
- mThread.join();
-
- while(mWritable.load(std::memory_order_acquire) < mWaveBuffer.size())
- mSem.wait();
- std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
- [this](WAVEHDR &waveHdr) -> void
- { waveOutUnprepareHeader(mOutHdl, &waveHdr, sizeof(WAVEHDR)); }
- );
- mWritable.store(0, std::memory_order_release);
-}
-
-
-struct WinMMCapture final : public BackendBase {
- WinMMCapture(ALCdevice *device) noexcept : BackendBase{device} { }
- ~WinMMCapture() override;
-
- static void CALLBACK waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
- void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
-
- int captureProc();
-
- ALCenum open(const ALCchar *name) override;
- ALCboolean start() override;
- void stop() override;
- ALCenum captureSamples(void *buffer, ALCuint samples) override;
- ALCuint availableSamples() override;
-
- std::atomic<ALuint> mReadable{0u};
- al::semaphore mSem;
- int mIdx{0};
- std::array<WAVEHDR,4> mWaveBuffer{};
-
- HWAVEIN mInHdl{nullptr};
-
- RingBufferPtr mRing{nullptr};
-
- WAVEFORMATEX mFormat{};
-
- std::atomic<bool> mKillNow{true};
- std::thread mThread;
-
- DEF_NEWDEL(WinMMCapture)
-};
-
-WinMMCapture::~WinMMCapture()
-{
- // Close the Wave device
- if(mInHdl)
- waveInClose(mInHdl);
- mInHdl = nullptr;
-
- al_free(mWaveBuffer[0].lpData);
- std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
-}
-
-void CALLBACK WinMMCapture::waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
-{ reinterpret_cast<WinMMCapture*>(instance)->waveInProc(device, msg, param1, param2); }
-
-/* WinMMCapture::waveInProc
- *
- * Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is
- * completed and returns to the application (with more data).
- */
-void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR)
-{
- if(msg != WIM_DATA) return;
- mReadable.fetch_add(1, std::memory_order_acq_rel);
- mSem.post();
-}
-
-int WinMMCapture::captureProc()
-{
- althrd_setname(RECORD_THREAD_NAME);
-
- lock();
- while(!mKillNow.load(std::memory_order_acquire) &&
- mDevice->Connected.load(std::memory_order_acquire))
- {
- ALuint todo{mReadable.load(std::memory_order_acquire)};
- if(todo < 1)
- {
- unlock();
- mSem.wait();
- lock();
- continue;
- }
-
- int widx{mIdx};
- do {
- WAVEHDR &waveHdr = mWaveBuffer[widx];
- widx = (widx+1) % mWaveBuffer.size();
-
- mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
- mReadable.fetch_sub(1, std::memory_order_acq_rel);
- waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR));
- } while(--todo);
- mIdx = widx;
- }
- unlock();
-
- return 0;
-}
-
-
-ALCenum WinMMCapture::open(const ALCchar *name)
-{
- if(CaptureDevices.empty())
- ProbeCaptureDevices();
-
- // Find the Device ID matching the deviceName if valid
- auto iter = name ?
- std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
- CaptureDevices.cbegin();
- if(iter == CaptureDevices.cend()) return ALC_INVALID_VALUE;
- auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
-
- switch(mDevice->FmtChans)
- {
- case DevFmtMono:
- case DevFmtStereo:
- break;
-
- case DevFmtQuad:
- case DevFmtX51:
- case DevFmtX51Rear:
- case DevFmtX61:
- case DevFmtX71:
- case DevFmtAmbi3D:
- return ALC_INVALID_ENUM;
- }
-
- switch(mDevice->FmtType)
- {
- case DevFmtUByte:
- case DevFmtShort:
- case DevFmtInt:
- case DevFmtFloat:
- break;
-
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- return ALC_INVALID_ENUM;
- }
-
- mFormat = WAVEFORMATEX{};
- mFormat.wFormatTag = (mDevice->FmtType == DevFmtFloat) ?
- WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
- mFormat.nChannels = mDevice->channelsFromFmt();
- mFormat.wBitsPerSample = mDevice->bytesFromFmt() * 8;
- mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
- mFormat.nSamplesPerSec = mDevice->Frequency;
- mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
- mFormat.cbSize = 0;
-
- MMRESULT res{waveInOpen(&mInHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMCapture::waveInProcC,
- reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
- if(res != MMSYSERR_NOERROR)
- {
- ERR("waveInOpen failed: %u\n", res);
- return ALC_INVALID_VALUE;
- }
-
- // Ensure each buffer is 50ms each
- DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u};
- BufferSize -= (BufferSize % mFormat.nBlockAlign);
-
- // Allocate circular memory buffer for the captured audio
- // Make sure circular buffer is at least 100ms in size
- ALuint CapturedDataSize{mDevice->BufferSize};
- CapturedDataSize = static_cast<ALuint>(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size()));
-
- mRing = CreateRingBuffer(CapturedDataSize, mFormat.nBlockAlign, false);
- if(!mRing) return ALC_INVALID_VALUE;
-
- al_free(mWaveBuffer[0].lpData);
- mWaveBuffer[0] = WAVEHDR{};
- mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize*4));
- mWaveBuffer[0].dwBufferLength = BufferSize;
- for(size_t i{1};i < mWaveBuffer.size();++i)
- {
- mWaveBuffer[i] = WAVEHDR{};
- mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
- mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength;
- }
-
- mDevice->DeviceName = CaptureDevices[DeviceID];
- return ALC_NO_ERROR;
-}
-
-ALCboolean WinMMCapture::start()
-{
- try {
- for(size_t i{0};i < mWaveBuffer.size();++i)
- {
- waveInPrepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
- waveInAddBuffer(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
- }
-
- mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this};
-
- waveInStart(mInHdl);
- return ALC_TRUE;
- }
- catch(std::exception& e) {
- ERR("Failed to start mixing thread: %s\n", e.what());
- }
- catch(...) {
- }
- return ALC_FALSE;
-}
-
-void WinMMCapture::stop()
-{
- waveInStop(mInHdl);
-
- mKillNow.store(true, std::memory_order_release);
- if(mThread.joinable())
- {
- mSem.post();
- mThread.join();
- }
-
- waveInReset(mInHdl);
- for(size_t i{0};i < mWaveBuffer.size();++i)
- waveInUnprepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
-
- mReadable.store(0, std::memory_order_release);
- mIdx = 0;
-}
-
-ALCenum WinMMCapture::captureSamples(void *buffer, ALCuint samples)
-{
- mRing->read(buffer, samples);
- return ALC_NO_ERROR;
-}
-
-ALCuint WinMMCapture::availableSamples()
-{ return (ALCuint)mRing->readSpace(); }
-
-} // namespace
-
-
-bool WinMMBackendFactory::init()
-{ return true; }
-
-bool WinMMBackendFactory::querySupport(BackendType type)
-{ return type == BackendType::Playback || type == BackendType::Capture; }
-
-void WinMMBackendFactory::probe(DevProbe type, std::string *outnames)
-{
- auto add_device = [outnames](const std::string &dname) -> void
- {
- /* +1 to also append the null char (to ensure a null-separated list and
- * double-null terminated list).
- */
- if(!dname.empty())
- outnames->append(dname.c_str(), dname.length()+1);
- };
- switch(type)
- {
- case DevProbe::Playback:
- ProbePlaybackDevices();
- std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
- break;
-
- case DevProbe::Capture:
- ProbeCaptureDevices();
- std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
- break;
- }
-}
-
-BackendPtr WinMMBackendFactory::createBackend(ALCdevice *device, BackendType type)
-{
- if(type == BackendType::Playback)
- return BackendPtr{new WinMMPlayback{device}};
- if(type == BackendType::Capture)
- return BackendPtr{new WinMMCapture{device}};
- return nullptr;
-}
-
-BackendFactory &WinMMBackendFactory::getFactory()
-{
- static WinMMBackendFactory factory{};
- return factory;
-}
diff --git a/Alc/backends/winmm.h b/Alc/backends/winmm.h
deleted file mode 100644
index e357ec19..00000000
--- a/Alc/backends/winmm.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef BACKENDS_WINMM_H
-#define BACKENDS_WINMM_H
-
-#include "backends/base.h"
-
-struct WinMMBackendFactory final : public BackendFactory {
-public:
- bool init() override;
-
- bool querySupport(BackendType type) override;
-
- void probe(DevProbe type, std::string *outnames) override;
-
- BackendPtr createBackend(ALCdevice *device, BackendType type) override;
-
- static BackendFactory &getFactory();
-};
-
-#endif /* BACKENDS_WINMM_H */