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

View File

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

View File

@ -77,7 +77,7 @@ namespace audio {
) override; ) override;
std::unique_ptr<InputDevice> openInputDevice( std::unique_ptr<InputDevice> openInputDevice(
uint sampleRate, uint channels, uint bitsPerSample const char* deviceName, uint sampleRate, uint channels, uint bitsPerSample
) override { ) override {
return nullptr; return nullptr;
} }
@ -85,6 +85,12 @@ namespace audio {
std::vector<std::string> getInputDeviceNames() override { std::vector<std::string> getInputDeviceNames() override {
return {}; return {};
} }
std::vector<std::string> getOutputDeviceNames() override {
return {};
}
void setOutputDevice(const std::string& deviceName) override {
}
void setListener( void setListener(
glm::vec3 position, glm::vec3 velocity, glm::vec3 at, glm::vec3 up 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)); }, true));
} }
input_device = backend->openInputDevice(44100, 1, 16); input_device = backend->openInputDevice(nullptr, 44100, 1, 16);
if (input_device) { if (input_device) {
input_device->startCapture(); input_device->startCapture();
} }
@ -254,15 +254,43 @@ std::unique_ptr<Stream> audio::open_stream(
} }
std::unique_ptr<InputDevice> audio::open_input_device( 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() { std::vector<std::string> audio::get_input_devices_names() {
return backend->getInputDeviceNames(); 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( void audio::set_listener(
glm::vec3 position, glm::vec3 velocity, glm::vec3 lookAt, glm::vec3 up 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 std::shared_ptr<PCMStream> stream, bool keepSource
) = 0; ) = 0;
virtual std::unique_ptr<InputDevice> openInputDevice( virtual std::unique_ptr<InputDevice> openInputDevice(
uint sampleRate, uint channels, uint bitsPerSample const char* deviceName,
uint sampleRate,
uint channels,
uint bitsPerSample
) = 0; ) = 0;
virtual void setListener( virtual void setListener(
glm::vec3 position, glm::vec3 position,
@ -371,6 +374,8 @@ namespace audio {
glm::vec3 up glm::vec3 up
) = 0; ) = 0;
virtual std::vector<std::string> getInputDeviceNames() = 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; virtual void update(double delta) = 0;
/// @brief Check if backend is an abstraction that does not internally /// @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) /// @param bitsPerSample number of bits per sample (8 or 16)
/// @return new InputDevice instance or nullptr /// @return new InputDevice instance or nullptr
std::unique_ptr<InputDevice> open_input_device( 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 /// @brief Retrieve names of available audio input devices
/// @return list of device names /// @return list of device names
std::vector<std::string> get_input_devices_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 /// @brief Configure 3D listener
/// @param position listener position /// @param position listener position
/// @param velocity listener velocity (used for Doppler effect) /// @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; 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[] = { const luaL_Reg audiolib[] = {
{"play_sound", lua::wrap<l_audio_play_sound>}, {"play_sound", lua::wrap<l_audio_play_sound>},
{"play_sound_2d", lua::wrap<l_audio_play_sound_2d>}, {"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>}, {"count_streams", lua::wrap<l_audio_count_streams>},
{"fetch_input", lua::wrap<l_audio_fetch_input>}, {"fetch_input", lua::wrap<l_audio_fetch_input>},
{"get_input_devices_names", lua::wrap<l_audio_get_input_devices_names>}, {"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} {nullptr, nullptr}
}; };