add audio.get_output_devices_names, set_input_device, set_output_device

This commit is contained in:
MihailRis 2025-10-23 22:54:05 +03:00
parent b11bdf0bcc
commit 6df27b8992
6 changed files with 169 additions and 42 deletions

View File

@ -30,13 +30,14 @@ const char* alc_error_to_string(ALCenum error) {
}
}
static void check_alc_errors(ALCdevice* device, const char* context) {
static bool check_alc_errors(ALCdevice* device, const char* context) {
ALCenum error = alcGetError(device);
if (error == ALC_NO_ERROR) {
return;
return false;
}
logger.error() << context << ": " << alc_error_to_string(error) << "("
<< error << ")";
return true;
}
ALSound::ALSound(
@ -419,6 +420,8 @@ int ALSpeaker::getPriority() const {
return priority;
}
static bool alc_enumeration_ext = false;
ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
: device(device), context(context) {
ALCint size;
@ -431,9 +434,15 @@ ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
maxSources = attrs[i + 1];
}
}
auto devices = getAvailableDevices();
logger.info() << "devices:";
for (auto& name : devices) {
auto outputDevices = getOutputDeviceNames();
logger.info() << "output devices:";
for (auto& name : outputDevices) {
logger.info() << " " << name;
}
auto inputDevices = getInputDeviceNames();
logger.info() << "input devices:";
for (auto& name : inputDevices) {
logger.info() << " " << name;
}
}
@ -482,35 +491,59 @@ std::unique_ptr<Stream> ALAudio::openStream(
std::vector<std::string> ALAudio::getInputDeviceNames() {
std::vector<std::string> devices;
if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT")) {
auto deviceList = alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER);
if (deviceList == nullptr) {
logger.warning() << "no input devices found";
return devices;
}
while (*deviceList) {
std::string deviceName(deviceList);
devices.push_back(deviceName);
deviceList += deviceName.length() + 1;
}
} else {
if (!alc_enumeration_ext) {
logger.warning() << "enumeration extension is not available";
return devices;
}
auto deviceList = alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER);
if (deviceList == nullptr) {
logger.warning() << "no input devices found";
return devices;
}
while (*deviceList) {
std::string deviceName(deviceList);
devices.push_back(deviceName);
deviceList += deviceName.length() + 1;
}
return devices;
}
std::vector<std::string> ALAudio::getOutputDeviceNames() {
std::vector<std::string> devices;
if (!alc_enumeration_ext) {
logger.warning() << "enumeration extension is not available";
return devices;
}
auto deviceList = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
if (deviceList == nullptr) {
logger.warning() << "no input devices found";
return devices;
}
while (*deviceList) {
std::string deviceName(deviceList);
devices.push_back(deviceName);
deviceList += deviceName.length() + 1;
}
return devices;
}
std::unique_ptr<InputDevice> ALAudio::openInputDevice(
uint sampleRate, uint channels, uint bitsPerSample
const char* deviceName, uint sampleRate, uint channels, uint bitsPerSample
) {
uint bps = bitsPerSample >> 3;
ALCdevice* device = alcCaptureOpenDevice(
nullptr,
deviceName,
sampleRate,
AL::to_al_format(channels, bitsPerSample),
sampleRate * channels * bps / 8
);
check_alc_errors(device, "alcCaptureOpenDevice");
if (check_alc_errors(device, "alcCaptureOpenDevice"))
return nullptr;
return std::make_unique<ALInputDevice>(
this, device, channels, bitsPerSample
@ -518,6 +551,8 @@ std::unique_ptr<InputDevice> ALAudio::openInputDevice(
}
std::unique_ptr<ALAudio> ALAudio::create() {
alc_enumeration_ext = alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT");
ALCdevice* device = alcOpenDevice(nullptr);
if (device == nullptr) return nullptr;
ALCcontext* context = alcCreateContext(device, nullptr);
@ -574,22 +609,35 @@ void ALAudio::freeBuffer(uint buffer) {
freebuffers.push_back(buffer);
}
std::vector<std::string> ALAudio::getAvailableDevices() const {
std::vector<std::string> devicesVec;
const ALCchar* devices;
devices = alcGetString(device, ALC_DEVICE_SPECIFIER);
if (!AL_GET_ERROR()) {
return devicesVec;
void ALAudio::setOutputDevice(const std::string& deviceName) {
ALCdevice* newDevice = alcOpenDevice(deviceName.c_str());
if (newDevice == nullptr) {
logger.error() << "failed to open output device: " << deviceName;
return;
}
const char* ptr = devices;
do {
devicesVec.emplace_back(ptr);
ptr += devicesVec.back().size() + 1;
} while (ptr[0]);
ALCcontext* newContext = alcCreateContext(newDevice, nullptr);
if (!alcMakeContextCurrent(newContext)) {
logger.error() << "failed to make context current for device: "
<< deviceName;
alcCloseDevice(newDevice);
return;
}
return devicesVec;
// Clean up old device and context
alcMakeContextCurrent(nullptr);
check_alc_errors(device, "alcMakeContextCurrent");
alcDestroyContext(context);
check_alc_errors(device, "alcDestroyContext");
if (!alcCloseDevice(device)) {
logger.error() << "old device not closed";
}
// Update to new device and context
device = newDevice;
context = newContext;
logger.info() << "switched output device to: " << deviceName;
}
void ALAudio::setListener(

View File

@ -172,8 +172,6 @@ namespace audio {
void freeSource(uint source);
void freeBuffer(uint buffer);
std::vector<std::string> getAvailableDevices() const;
std::unique_ptr<Sound> createSound(
std::shared_ptr<PCM> pcm, bool keepPCM
) override;
@ -183,11 +181,17 @@ namespace audio {
) override;
std::unique_ptr<InputDevice> openInputDevice(
uint sampleRate, uint channels, uint bitsPerSample
const char* deviceName,
uint sampleRate,
uint channels,
uint bitsPerSample
) override;
std::vector<std::string> getOutputDeviceNames() override;
std::vector<std::string> getInputDeviceNames() override;
void setOutputDevice(const std::string& deviceName) override;
void setListener(
glm::vec3 position,
glm::vec3 velocity,

View File

@ -77,7 +77,7 @@ namespace audio {
) override;
std::unique_ptr<InputDevice> openInputDevice(
uint sampleRate, uint channels, uint bitsPerSample
const char* deviceName, uint sampleRate, uint channels, uint bitsPerSample
) override {
return nullptr;
}
@ -85,6 +85,12 @@ namespace audio {
std::vector<std::string> getInputDeviceNames() override {
return {};
}
std::vector<std::string> getOutputDeviceNames() override {
return {};
}
void setOutputDevice(const std::string& deviceName) override {
}
void setListener(
glm::vec3 position, glm::vec3 velocity, glm::vec3 at, glm::vec3 up

View File

@ -183,7 +183,7 @@ void audio::initialize(bool enabled, AudioSettings& settings) {
}, true));
}
input_device = backend->openInputDevice(44100, 1, 16);
input_device = backend->openInputDevice(nullptr, 44100, 1, 16);
if (input_device) {
input_device->startCapture();
}
@ -254,15 +254,43 @@ std::unique_ptr<Stream> audio::open_stream(
}
std::unique_ptr<InputDevice> audio::open_input_device(
uint sampleRate, uint channels, uint bitsPerSample
const char* deviceName, uint sampleRate, uint channels, uint bitsPerSample
) {
return backend->openInputDevice(sampleRate, channels, bitsPerSample);
return backend->openInputDevice(
deviceName, sampleRate, channels, bitsPerSample
);
}
std::vector<std::string> audio::get_input_devices_names() {
return backend->getInputDeviceNames();
}
std::vector<std::string> audio::get_output_devices_names() {
return backend->getOutputDeviceNames();
}
void audio::set_input_device(const std::string& deviceName) {
auto newDevice = backend->openInputDevice(
deviceName.empty() ? nullptr : deviceName.c_str(), 44100, 1, 16
);
if (newDevice == nullptr) {
logger.error() << "could not open input device: " << deviceName;
return;
}
if (input_device) {
input_device->stopCapture();
}
input_device = std::move(newDevice);
if (input_device) {
input_device->startCapture();
}
}
void audio::set_output_device(const std::string& deviceName) {
backend->setOutputDevice(deviceName);
}
void audio::set_listener(
glm::vec3 position, glm::vec3 velocity, glm::vec3 lookAt, glm::vec3 up
) {

View File

@ -362,7 +362,10 @@ namespace audio {
std::shared_ptr<PCMStream> stream, bool keepSource
) = 0;
virtual std::unique_ptr<InputDevice> openInputDevice(
uint sampleRate, uint channels, uint bitsPerSample
const char* deviceName,
uint sampleRate,
uint channels,
uint bitsPerSample
) = 0;
virtual void setListener(
glm::vec3 position,
@ -371,6 +374,8 @@ namespace audio {
glm::vec3 up
) = 0;
virtual std::vector<std::string> getInputDeviceNames() = 0;
virtual std::vector<std::string> getOutputDeviceNames() = 0;
virtual void setOutputDevice(const std::string& deviceName) = 0;
virtual void update(double delta) = 0;
/// @brief Check if backend is an abstraction that does not internally
@ -432,13 +437,23 @@ namespace audio {
/// @param bitsPerSample number of bits per sample (8 or 16)
/// @return new InputDevice instance or nullptr
std::unique_ptr<InputDevice> open_input_device(
uint sampleRate, uint channels, uint bitsPerSample
const char* deviceName,
uint sampleRate,
uint channels,
uint bitsPerSample
);
/// @brief Retrieve names of available audio input devices
/// @return list of device names
std::vector<std::string> get_input_devices_names();
/// @brief Retrieve names of available audio output devices
/// @return list of device names
std::vector<std::string> get_output_devices_names();
void set_input_device(const std::string& deviceName);
void set_output_device(const std::string& deviceName);
/// @brief Configure 3D listener
/// @param position listener position
/// @param velocity listener velocity (used for Doppler effect)

View File

@ -419,6 +419,29 @@ static int l_audio_get_input_devices_names(lua::State* L) {
return 1;
}
static int l_audio_get_output_devices_names(lua::State* L) {
auto device_names = audio::get_output_devices_names();
lua::createtable(L, device_names.size(), 0);
int index = 1;
for (const auto& name : device_names) {
lua::pushstring(L, name.c_str());
lua::rawseti(L, index++);
}
return 1;
}
static int l_audio_set_input_device(lua::State* L) {
auto device_name = lua::tostring(L, 1);
audio::set_input_device(device_name);
return 0;
}
static int l_audio_set_output_device(lua::State* L) {
auto device_name = lua::tostring(L, 1);
audio::set_output_device(device_name);
return 0;
}
const luaL_Reg audiolib[] = {
{"play_sound", lua::wrap<l_audio_play_sound>},
{"play_sound_2d", lua::wrap<l_audio_play_sound_2d>},
@ -446,5 +469,8 @@ const luaL_Reg audiolib[] = {
{"count_streams", lua::wrap<l_audio_count_streams>},
{"fetch_input", lua::wrap<l_audio_fetch_input>},
{"get_input_devices_names", lua::wrap<l_audio_get_input_devices_names>},
{"get_output_devices_names", lua::wrap<l_audio_get_output_devices_names>},
{"set_input_device", lua::wrap<l_audio_set_input_device>},
{"set_output_device", lua::wrap<l_audio_set_output_device>},
{nullptr, nullptr}
};