mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
* use std::move() for local variables that are copied but not reused Addresses ~140 Coverity "Variable copied when it could be moved" defects across 74 files. Every std::move() targets a local variable whose value is consumed by push_back, emplace, assignment, lambda capture, or a function call and is never read again afterward. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * clang: use std::make_shared * clang: use std::make_unique * address feedback --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
699 lines
18 KiB
C++
699 lines
18 KiB
C++
|
|
#include "globalincs/pstypes.h"
|
|
#include "sound/openal.h"
|
|
#include "osapi/osregistry.h"
|
|
#include "options/Option.h"
|
|
#include "parse/parselo.h"
|
|
#include "libs/jansson.h"
|
|
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
#ifdef _WIN32
|
|
#define VC_EXTRALEAN
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
// Stupid windows workaround...
|
|
#ifdef MessageBox
|
|
#undef MessageBox
|
|
#endif
|
|
|
|
|
|
static SCP_string Playback_device;
|
|
static SCP_string Capture_device;
|
|
|
|
|
|
enum {
|
|
OAL_DEVICE_GENERIC,
|
|
OAL_DEVICE_DEFAULT,
|
|
OAL_DEVICE_USER
|
|
};
|
|
|
|
typedef struct OALdevice {
|
|
SCP_string device_name;
|
|
int type;
|
|
bool usable;
|
|
|
|
OALdevice() :
|
|
type(OAL_DEVICE_GENERIC), usable(false)
|
|
{
|
|
}
|
|
|
|
OALdevice(const char *name) :
|
|
device_name(name), type(OAL_DEVICE_GENERIC), usable(false)
|
|
{
|
|
}
|
|
} OALdevice;
|
|
|
|
static SCP_vector<OALdevice> PlaybackDevices;
|
|
static SCP_vector<OALdevice> CaptureDevices;
|
|
|
|
|
|
// enumeration extension
|
|
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
|
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
|
#endif
|
|
|
|
// List of audio device names for the in-game option
|
|
SCP_vector<SCP_string> PlaybackDeviceList;
|
|
|
|
static int playbackdevice_deserializer(const json_t* value)
|
|
{
|
|
const char* device;
|
|
|
|
json_error_t err;
|
|
if (json_unpack_ex((json_t*)value, &err, 0, "s", &device) != 0) {
|
|
throw json_exception(err);
|
|
}
|
|
|
|
int id = openal_find_playback_device_by_name(device);
|
|
|
|
if (SCP_vector_inbounds(PlaybackDeviceList, id)) {
|
|
return id;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
static json_t* playbackdevice_serializer(int value)
|
|
{
|
|
if (!SCP_vector_inbounds(PlaybackDeviceList, value)) {
|
|
return json_pack("s", "");
|
|
}
|
|
|
|
return json_pack("s", PlaybackDeviceList[value].c_str());
|
|
}
|
|
static SCP_vector<int> playbackdevice_enumerator()
|
|
{
|
|
SCP_vector<int> vals;
|
|
for (int i = 0; i < static_cast<int>(PlaybackDeviceList.size()); ++i) {
|
|
vals.push_back(i);
|
|
}
|
|
return vals;
|
|
}
|
|
static SCP_string playbackdevice_display(int id)
|
|
{
|
|
if (!SCP_vector_inbounds(PlaybackDeviceList, id)) {
|
|
return "";
|
|
}
|
|
|
|
SCP_string out;
|
|
sprintf(out, "(%d) %s", id + 1, PlaybackDeviceList[id].c_str());
|
|
return out;
|
|
}
|
|
static bool playbackdevice_change(int /*device*/, bool initial)
|
|
{
|
|
if (initial) {
|
|
return false; // On game boot always return false
|
|
}
|
|
|
|
// Looking at ds.cpp's ds_init(), it's probably possible to not require a restart for this
|
|
// but for now let's keep this simple and just restart. If/when this is implemented the device
|
|
// parameter will be required. Currently playing audio will need to be copied from the old device
|
|
// to the new as part of the initialization. This will require deeper changes to the audio code
|
|
// which can be handled later in a follow-up PR.
|
|
|
|
return false;
|
|
}
|
|
// coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton
|
|
static auto PlaybackDeviceOption = options::OptionBuilder<int>("Audio.PlaybackDevice",
|
|
std::pair<const char*, int>{"Playback Device", 1834},
|
|
std::pair<const char*, int>{"The device used for audio playback", 1835})
|
|
.category(std::make_pair("Audio", 1826))
|
|
.level(options::ExpertLevel::Beginner)
|
|
.deserializer(playbackdevice_deserializer)
|
|
.serializer(playbackdevice_serializer)
|
|
.enumerator(playbackdevice_enumerator)
|
|
.display(playbackdevice_display)
|
|
.flags({options::OptionFlags::ForceMultiValueSelection})
|
|
.default_val(0)
|
|
.change_listener(playbackdevice_change)
|
|
.importance(99)
|
|
.finish();
|
|
|
|
// List of audio device names for the in-game option
|
|
SCP_vector<SCP_string> CaptureDeviceList;
|
|
|
|
static int capturedevice_deserializer(const json_t* value)
|
|
{
|
|
const char* device;
|
|
|
|
json_error_t err;
|
|
if (json_unpack_ex((json_t*)value, &err, 0, "s", &device) != 0) {
|
|
throw json_exception(err);
|
|
}
|
|
|
|
int id = openal_find_capture_device_by_name(device);
|
|
|
|
if (SCP_vector_inbounds(CaptureDeviceList, id)) {
|
|
return id;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
static json_t* capturedevice_serializer(int value)
|
|
{
|
|
if (!SCP_vector_inbounds(CaptureDeviceList, value)) {
|
|
return json_pack("s", "");
|
|
}
|
|
|
|
return json_pack("s", CaptureDeviceList[value].c_str());
|
|
}
|
|
static SCP_vector<int> capturedevice_enumerator()
|
|
{
|
|
SCP_vector<int> vals;
|
|
for (int i = 0; i < static_cast<int>(CaptureDeviceList.size()); ++i) {
|
|
vals.push_back(i);
|
|
}
|
|
return vals;
|
|
}
|
|
static SCP_string capturedevice_display(int id)
|
|
{
|
|
if (!SCP_vector_inbounds(CaptureDeviceList, id)) {
|
|
return "";
|
|
}
|
|
|
|
SCP_string out;
|
|
sprintf(out, "(%d) %s", id + 1, CaptureDeviceList[id].c_str());
|
|
return out;
|
|
}
|
|
static bool capturedevice_change(int /*device*/, bool initial)
|
|
{
|
|
if (initial) {
|
|
return false; // On game boot always return false
|
|
}
|
|
|
|
// Looking at ds.cpp's ds_init(), it's probably possible to not require a restart for this
|
|
// but for now let's keep this simple and just restart. If/when this is implemented the device
|
|
// parameter will be required. Currently playing audio will need to be copied from the old device
|
|
// to the new as part of the initialization. This will require deeper changes to the audio code
|
|
// which can be handled later in a follow-up PR.
|
|
|
|
return false;
|
|
}
|
|
// coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton
|
|
static auto CaptureDeviceOption = options::OptionBuilder<int>("Audio.CaptureDevice",
|
|
std::pair<const char*, int>{"Capture Device", 1836},
|
|
std::pair<const char*, int>{"The device used for audio capture", 1837})
|
|
.category(std::make_pair("Audio", 1826))
|
|
.level(options::ExpertLevel::Beginner)
|
|
.deserializer(capturedevice_deserializer)
|
|
.serializer(capturedevice_serializer)
|
|
.enumerator(capturedevice_enumerator)
|
|
.display(capturedevice_display)
|
|
.flags({options::OptionFlags::ForceMultiValueSelection})
|
|
.default_val(0)
|
|
.change_listener(capturedevice_change)
|
|
.importance(99)
|
|
.finish();
|
|
|
|
//--------------------------------------------------------------------------
|
|
// openal_error_string()
|
|
//
|
|
// Returns the human readable error string if there is an error or NULL if not
|
|
//
|
|
const char *openal_error_string(int get_alc)
|
|
{
|
|
int i;
|
|
|
|
if (get_alc) {
|
|
ALCdevice *device = alcGetContextsDevice( alcGetCurrentContext() );
|
|
|
|
i = alcGetError(device);
|
|
|
|
if ( i != ALC_NO_ERROR )
|
|
return (const char*) alcGetString(device, i);
|
|
}
|
|
else {
|
|
i = alGetError();
|
|
|
|
if ( i != AL_NO_ERROR )
|
|
return (const char*)alGetString(i);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ALenum openal_get_format(ALint bits, ALint n_channels)
|
|
{
|
|
ALenum format = AL_INVALID_VALUE;
|
|
|
|
if ( (n_channels < 1) || (n_channels > 2) ) {
|
|
return format;
|
|
}
|
|
|
|
switch (bits) {
|
|
case 32:
|
|
format = (n_channels == 1) ? AL_FORMAT_MONO_FLOAT32 : AL_FORMAT_STEREO_FLOAT32;
|
|
break;
|
|
|
|
case 16:
|
|
format = (n_channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
|
break;
|
|
|
|
case 8:
|
|
format = (n_channels == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
|
|
break;
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
static bool openal_device_sort_func(const OALdevice &d1, const OALdevice &d2)
|
|
{
|
|
if (d1.type > d2.type) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void find_playback_device(OpenALInformation* info)
|
|
{
|
|
// First, build a list of device names for the in-game option to use and pull from
|
|
for (auto& device : info->playback_devices) {
|
|
OALdevice new_device(device.c_str());
|
|
PlaybackDeviceList.push_back(new_device.device_name);
|
|
}
|
|
|
|
const char* user_device = os_config_read_string("Sound", "PlaybackDevice", nullptr);
|
|
|
|
if (Using_in_game_options) {
|
|
if (SCP_vector_inbounds(PlaybackDeviceList, PlaybackDeviceOption->getValue())) {
|
|
user_device = PlaybackDeviceList[PlaybackDeviceOption->getValue()].c_str();
|
|
}
|
|
}
|
|
|
|
const char *default_device = info->default_playback_device.c_str();
|
|
|
|
// in case they are the same, we only want to test it once
|
|
if ( (user_device && default_device) && !strcmp(user_device, default_device) ) {
|
|
user_device = NULL;
|
|
}
|
|
|
|
for (auto& device : info->playback_devices) {
|
|
OALdevice new_device(device.c_str());
|
|
|
|
if (user_device && !strcmp(device.c_str(), user_device)) {
|
|
new_device.type = OAL_DEVICE_USER;
|
|
} else if (default_device && !strcmp(device.c_str(), default_device)) {
|
|
new_device.type = OAL_DEVICE_DEFAULT;
|
|
}
|
|
|
|
PlaybackDevices.push_back( std::move(new_device) );
|
|
}
|
|
|
|
if ( PlaybackDevices.empty() ) {
|
|
return;
|
|
}
|
|
|
|
std::sort( PlaybackDevices.begin(), PlaybackDevices.end(), openal_device_sort_func );
|
|
|
|
|
|
ALCdevice *device = NULL;
|
|
ALCcontext *context = NULL;
|
|
|
|
// for each device that we have available, try and figure out which to use
|
|
for (size_t idx = 0; idx < PlaybackDevices.size(); idx++) {
|
|
OALdevice *pdev = &PlaybackDevices[idx];
|
|
|
|
// open our specfic device
|
|
device = alcOpenDevice( (const ALCchar*) pdev->device_name.c_str() );
|
|
|
|
if (device == NULL) {
|
|
continue;
|
|
}
|
|
|
|
context = alcCreateContext(device, NULL);
|
|
|
|
if (context == NULL) {
|
|
alcCloseDevice(device);
|
|
continue;
|
|
}
|
|
|
|
alcMakeContextCurrent(context);
|
|
alcGetError(device);
|
|
|
|
// check how many sources we can create
|
|
static const int MIN_SOURCES = 48; // MAX_CHANNELS + 16 spare
|
|
int si = 0;
|
|
|
|
for (si = 0; si < MIN_SOURCES; si++) {
|
|
ALuint source_id = 0;
|
|
alGenSources(1, &source_id);
|
|
|
|
if (alGetError() != AL_NO_ERROR) {
|
|
break;
|
|
}
|
|
|
|
alDeleteSources(1, &source_id);
|
|
}
|
|
|
|
if (si == MIN_SOURCES) {
|
|
// ok, it supports our minimum requirements
|
|
pdev->usable = true;
|
|
|
|
// need this for the future
|
|
Playback_device = pdev->device_name;
|
|
|
|
// done
|
|
break;
|
|
} else {
|
|
// clean up for next pass
|
|
alcMakeContextCurrent(NULL);
|
|
alcDestroyContext(context);
|
|
alcCloseDevice(device);
|
|
|
|
context = NULL;
|
|
device = NULL;
|
|
}
|
|
}
|
|
|
|
alcMakeContextCurrent(NULL);
|
|
|
|
if (context) {
|
|
alcDestroyContext(context);
|
|
}
|
|
|
|
if (device) {
|
|
alcCloseDevice(device);
|
|
}
|
|
}
|
|
|
|
static void find_capture_device(OpenALInformation* info)
|
|
{
|
|
// First, build a list of device names for the in-game option to use and pull from
|
|
for (auto& device : info->capture_devices) {
|
|
OALdevice new_device(device.c_str());
|
|
CaptureDeviceList.push_back(new_device.device_name);
|
|
}
|
|
|
|
const char* user_device = os_config_read_string("Sound", "CaptureDevice", nullptr);
|
|
|
|
if (Using_in_game_options) {
|
|
if (SCP_vector_inbounds(CaptureDevices, CaptureDeviceOption->getValue())) {
|
|
user_device = CaptureDevices[CaptureDeviceOption->getValue()].device_name.c_str();
|
|
}
|
|
}
|
|
|
|
const char *default_device = info->default_capture_device.c_str();
|
|
|
|
// in case they are the same, we only want to test it once
|
|
if ( (user_device && default_device) && !strcmp(user_device, default_device) ) {
|
|
user_device = NULL;
|
|
}
|
|
|
|
for (auto& device : info->capture_devices) {
|
|
OALdevice new_device(device.c_str());
|
|
|
|
if (user_device && !strcmp(device.c_str(), user_device)) {
|
|
new_device.type = OAL_DEVICE_USER;
|
|
} else if (default_device && !strcmp(device.c_str(), default_device)) {
|
|
new_device.type = OAL_DEVICE_DEFAULT;
|
|
}
|
|
|
|
CaptureDevices.push_back( std::move(new_device) );
|
|
}
|
|
|
|
if ( CaptureDevices.empty() ) {
|
|
return;
|
|
}
|
|
|
|
std::sort( CaptureDevices.begin(), CaptureDevices.end(), openal_device_sort_func );
|
|
|
|
|
|
// for each device that we have available, try and figure out which to use
|
|
for (size_t idx = 0; idx < CaptureDevices.size(); idx++) {
|
|
const ALCchar *device_name = CaptureDevices[idx].device_name.c_str();
|
|
|
|
ALCdevice *device = alcCaptureOpenDevice(device_name, 22050, AL_FORMAT_MONO8, 22050 * 2);
|
|
|
|
if (device == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (alcGetError(device) != ALC_NO_ERROR) {
|
|
alcCaptureCloseDevice(device);
|
|
continue;
|
|
}
|
|
|
|
// ok, we should be good with this one
|
|
Capture_device = CaptureDevices[idx].device_name;
|
|
|
|
alcCaptureCloseDevice(device);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// find a playback device's vector index
|
|
int openal_find_playback_device_by_name(const SCP_string& device)
|
|
{
|
|
for (int i = 0; i < static_cast<int>(PlaybackDeviceList.size()); i++) {
|
|
if (!stricmp(PlaybackDeviceList[i].c_str(), device.c_str())) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// find a capture device's vector index
|
|
int openal_find_capture_device_by_name(const SCP_string& device)
|
|
{
|
|
for (int i = 0; i < static_cast<int>(CaptureDeviceList.size()); i++) {
|
|
if (!stricmp(CaptureDeviceList[i].c_str(), device.c_str())) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// initializes hardware device from perferred/default/enumerated list
|
|
bool openal_init_device(SCP_string *playback, SCP_string *capture)
|
|
{
|
|
if ( !Playback_device.empty() ) {
|
|
if (playback) {
|
|
*playback = Playback_device;
|
|
}
|
|
|
|
if (capture) {
|
|
*capture = Capture_device;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (playback) {
|
|
playback->erase();
|
|
}
|
|
|
|
if (capture) {
|
|
capture->erase();
|
|
}
|
|
|
|
// This reuses the code for the launcher to make sure everything is consistent
|
|
auto platform_info = openal_get_platform_information();
|
|
|
|
if (platform_info.version_major <= 1 && platform_info.version_minor < 1) {
|
|
os::dialogs::Message(os::dialogs::MESSAGEBOX_ERROR,
|
|
"OpenAL 1.1 or newer is required for proper operation. On Linux and Windows OpenAL Soft is recommended. If you are on Mac OS X you need to upgrade your OS.");
|
|
return false;
|
|
}
|
|
|
|
// go through and find out what devices we actually want to use ...
|
|
find_playback_device(&platform_info);
|
|
find_capture_device(&platform_info);
|
|
|
|
if ( Playback_device.empty() ) {
|
|
return false;
|
|
}
|
|
|
|
|
|
#ifndef NDEBUG
|
|
if ( !PlaybackDevices.empty() ) {
|
|
nprintf(("OpenAL", " Available Playback Devices:\n"));
|
|
|
|
for (size_t idx = 0; idx < PlaybackDevices.size(); idx++) {
|
|
nprintf(("OpenAL", " %s", PlaybackDevices[idx].device_name.c_str()));
|
|
|
|
if (PlaybackDevices[idx].type == OAL_DEVICE_USER) {
|
|
nprintf(("OpenAL", " *preferred*\n"));
|
|
} else if (PlaybackDevices[idx].type == OAL_DEVICE_DEFAULT) {
|
|
nprintf(("OpenAL", " *default*\n"));
|
|
} else {
|
|
nprintf(("OpenAL", "\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !CaptureDevices.empty() ) {
|
|
if ( !PlaybackDevices.empty() ) {
|
|
nprintf(("OpenAL", "\n"));
|
|
}
|
|
|
|
nprintf(("OpenAL", " Available Capture Devices:\n"));
|
|
|
|
for (size_t idx = 0; idx < CaptureDevices.size(); idx++) {
|
|
nprintf(("OpenAL", " %s", CaptureDevices[idx].device_name.c_str()));
|
|
|
|
if (CaptureDevices[idx].type == OAL_DEVICE_USER) {
|
|
nprintf(("OpenAL", " *preferred*\n"));
|
|
} else if (CaptureDevices[idx].type == OAL_DEVICE_DEFAULT) {
|
|
nprintf(("OpenAL", " *default*\n"));
|
|
} else {
|
|
nprintf(("OpenAL", "\n"));
|
|
}
|
|
}
|
|
|
|
nprintf(("OpenAL", "\n"));
|
|
}
|
|
#endif
|
|
|
|
|
|
// cleanup
|
|
PlaybackDevices.clear();
|
|
CaptureDevices.clear();
|
|
|
|
|
|
if (playback) {
|
|
*playback = Playback_device;
|
|
}
|
|
|
|
if (capture) {
|
|
*capture = Capture_device;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void get_version_info(OpenALInformation* info) {
|
|
// initialize default setup first, for version check...
|
|
ALCdevice *device = alcOpenDevice(NULL);
|
|
|
|
if (device == NULL) {
|
|
return;
|
|
}
|
|
|
|
ALCcontext *context = alcCreateContext(device, NULL);
|
|
|
|
if (context == NULL) {
|
|
alcCloseDevice(device);
|
|
return;
|
|
}
|
|
|
|
alcMakeContextCurrent(context);
|
|
|
|
// version check (for 1.0 or 1.1)
|
|
ALCint AL_minor_version = 0;
|
|
ALCint AL_major_version = 0;
|
|
alcGetIntegerv(NULL, ALC_MAJOR_VERSION, sizeof(ALCint), &AL_major_version);
|
|
alcGetIntegerv(NULL, ALC_MINOR_VERSION, sizeof(ALCint), &AL_minor_version);
|
|
|
|
info->version_major = static_cast<uint32_t>(AL_major_version);
|
|
info->version_minor = static_cast<uint32_t>(AL_minor_version);
|
|
|
|
alcGetError(device);
|
|
|
|
// close default device
|
|
alcMakeContextCurrent(NULL);
|
|
alcDestroyContext(context);
|
|
alcCloseDevice(device);
|
|
}
|
|
|
|
static bool device_supports_efx(const char* device_name) {
|
|
ALCdevice *device = alcOpenDevice(device_name);
|
|
|
|
if (device == NULL) {
|
|
return false;
|
|
}
|
|
|
|
bool result = false;
|
|
if ( alcIsExtensionPresent(device, (const ALchar*)"ALC_EXT_EFX") == AL_TRUE ) {
|
|
result = true;
|
|
}
|
|
|
|
alcCloseDevice(device);
|
|
return result;
|
|
}
|
|
|
|
static void enumerate_playback_devices(OpenALInformation* info) {
|
|
info->playback_devices.clear();
|
|
info->default_playback_device.clear();
|
|
|
|
auto default_device = alcGetString( NULL, ALC_DEFAULT_DEVICE_SPECIFIER );
|
|
if (default_device != nullptr) {
|
|
info->default_playback_device = default_device;
|
|
info->efx_support.push_back(std::make_pair(SCP_string(default_device), device_supports_efx(default_device)));
|
|
}
|
|
|
|
if ( alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT") == AL_TRUE ) {
|
|
const char *all_devices = NULL;
|
|
|
|
if ( alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATE_ALL_EXT") == AL_TRUE ) {
|
|
all_devices = (const char*) alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
|
} else {
|
|
all_devices = (const char*) alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
|
}
|
|
|
|
const char *str_list = all_devices;
|
|
size_t ext_length = 0;
|
|
|
|
if ( (str_list != NULL) && ((ext_length = strlen(str_list)) > 0) ) {
|
|
while (ext_length) {
|
|
info->playback_devices.push_back( SCP_string(str_list) );
|
|
info->efx_support.push_back(std::make_pair(SCP_string(str_list), device_supports_efx(str_list)));
|
|
|
|
str_list += (ext_length + 1);
|
|
ext_length = strlen(str_list);
|
|
}
|
|
}
|
|
} else {
|
|
if (default_device) {
|
|
info->playback_devices.push_back(SCP_string(default_device));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void enumerate_capture_devices(OpenALInformation* info) {
|
|
info->capture_devices.clear();
|
|
info->default_capture_device.clear();
|
|
|
|
auto default_device = alcGetString( NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER );
|
|
if (default_device != nullptr) {
|
|
info->default_capture_device = default_device;
|
|
}
|
|
|
|
if ( alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT") == AL_TRUE ) {
|
|
const char *all_devices = (const char*) alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
|
|
|
const char *str_list = all_devices;
|
|
size_t ext_length = 0;
|
|
|
|
if ( (str_list != NULL) && ((ext_length = strlen(str_list)) > 0) ) {
|
|
while (ext_length) {
|
|
info->capture_devices.push_back( SCP_string(str_list));
|
|
|
|
str_list += (ext_length + 1);
|
|
ext_length = strlen(str_list);
|
|
}
|
|
}
|
|
} else {
|
|
if (default_device) {
|
|
info->capture_devices.push_back(SCP_string(default_device));
|
|
}
|
|
}
|
|
}
|
|
|
|
OpenALInformation openal_get_platform_information() {
|
|
OpenALInformation info;
|
|
|
|
get_version_info(&info);
|
|
|
|
enumerate_playback_devices(&info);
|
|
enumerate_capture_devices(&info);
|
|
|
|
return info;
|
|
}
|