mirror of https://github.com/bjornbytes/lovr.git
Fix getDevices API
* Takes device type, so you only get either playback or capture devices * Doesn't store devices in state, reducing risk of dangling pointers * Uses names instead of identifiers, since miniaudio identifiers become invalid if you call "getDevices" again * Better diagnostics * Split up lovrAudioInitDevice to be per-type, cleaner that way * UseDevice now takes type and name, instead of just identifier
This commit is contained in:
parent
9c7bc7c8db
commit
138853ef01
|
@ -44,7 +44,7 @@ static int l_lovrAudioSetVolume(lua_State* L) {
|
|||
static int l_lovrAudioNewSource(lua_State* L) {
|
||||
Source* source = NULL;
|
||||
SoundData* soundData = luax_totype(L, 1, SoundData);
|
||||
|
||||
|
||||
bool spatial = true;
|
||||
if (lua_istable(L, 2)) {
|
||||
lua_getfield(L, 2, "spatial");
|
||||
|
@ -53,7 +53,7 @@ static int l_lovrAudioNewSource(lua_State* L) {
|
|||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (soundData) {
|
||||
source = lovrSourceCreate(soundData, spatial);
|
||||
|
@ -87,13 +87,13 @@ static int l_lovrAudioGetCaptureStream(lua_State* L) {
|
|||
}
|
||||
|
||||
static int l_lovrAudioGetDevices(lua_State *L) {
|
||||
AudioDevice *devices;
|
||||
size_t count;
|
||||
lovrAudioGetDevices(&devices, &count);
|
||||
AudioType type = luax_checkenum(L, 1, AudioType, "playback");
|
||||
|
||||
AudioDeviceArr *devices = lovrAudioGetDevices(type);
|
||||
lua_newtable(L);
|
||||
int listOfDevicesIdx = lua_gettop(L);
|
||||
for(int i = 0; i < count; i++) {
|
||||
AudioDevice *device = &devices[i];
|
||||
for(int i = 0; i < devices->length; i++) {
|
||||
AudioDevice *device = &devices->data[i];
|
||||
lua_pushinteger(L, i+1); // key in listOfDevicesIdx, for the bottom settable
|
||||
lua_newtable(L);
|
||||
luax_pushenum(L, AudioType, device->type);
|
||||
|
@ -102,24 +102,19 @@ static int l_lovrAudioGetDevices(lua_State *L) {
|
|||
lua_setfield(L, -2, "name");
|
||||
lua_pushboolean(L, device->isDefault);
|
||||
lua_setfield(L, -2, "isDefault");
|
||||
lua_pushlightuserdata(L, device->identifier);
|
||||
lua_setfield(L, -2, "identifier");
|
||||
lua_pushinteger(L, device->minChannels);
|
||||
lua_setfield(L, -2, "minChannels");
|
||||
lua_pushinteger(L, device->maxChannels);
|
||||
lua_setfield(L, -2, "maxChannels");
|
||||
|
||||
lua_settable(L, listOfDevicesIdx);
|
||||
}
|
||||
|
||||
lovrAudioFreeDevices(devices);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrUseDevice(lua_State *L) {
|
||||
AudioDeviceIdentifier ident = lua_touserdata(L, 1);
|
||||
int sampleRate = lua_tointeger(L, 2);
|
||||
SampleFormat format = luax_checkenum(L, 3, SampleFormat, "invalid");
|
||||
lovrAudioUseDevice(ident, sampleRate, format);
|
||||
AudioType type = luax_checkenum(L, 1, AudioType, "playback");
|
||||
const char *name = lua_tostring(L, 2);
|
||||
int sampleRate = lua_tointeger(L, 3);
|
||||
SampleFormat format = luax_checkenum(L, 4, SampleFormat, "invalid");
|
||||
lovrAudioUseDevice(type, name, sampleRate, format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -141,9 +136,9 @@ int luaopen_lovr_audio(lua_State* L) {
|
|||
lua_newtable(L);
|
||||
luax_register(L, lovrAudio);
|
||||
luax_registertype(L, Source);
|
||||
AudioConfig config[2] = {
|
||||
{ .enable = true, .start = true, .format = SAMPLE_F32, .sampleRate = 44100 },
|
||||
{ .enable = false, .start = false, .format = SAMPLE_F32, .sampleRate = 44100 }
|
||||
AudioConfig config[2] = {
|
||||
{ .enable = true, .start = true, .format = SAMPLE_F32, .sampleRate = 44100 },
|
||||
{ .enable = false, .start = false, .format = SAMPLE_F32, .sampleRate = 44100 }
|
||||
};
|
||||
if (lovrAudioInit(config)) {
|
||||
luax_atexit(L, lovrAudioDestroy);
|
||||
|
|
|
@ -45,8 +45,6 @@ static struct {
|
|||
SoundData *captureStream;
|
||||
arr_t(ma_data_converter*) converters;
|
||||
Spatializer* spatializer;
|
||||
|
||||
AudioDevice *deviceInfos;
|
||||
} state;
|
||||
|
||||
// Device callbacks
|
||||
|
@ -179,27 +177,59 @@ void lovrAudioDestroy() {
|
|||
free(state.converters.data[i]);
|
||||
}
|
||||
arr_free(&state.converters);
|
||||
free(state.config[0].deviceName);
|
||||
free(state.config[1].deviceName);
|
||||
memset(&state, 0, sizeof(state));
|
||||
}
|
||||
|
||||
bool lovrAudioInitDevice(AudioType type) {
|
||||
ma_device_type deviceType = (type == AUDIO_PLAYBACK) ? ma_device_type_playback : ma_device_type_capture;
|
||||
|
||||
ma_device_config config = ma_device_config_init(deviceType);
|
||||
config.sampleRate = state.config[type].sampleRate;
|
||||
lovrAssert(state.config[AUDIO_PLAYBACK].format == OUTPUT_FORMAT, "Only f32 playback format currently supported");
|
||||
config.playback.format = miniAudioFormatFromLovr[state.config[AUDIO_PLAYBACK].format];
|
||||
config.capture.format = miniAudioFormatFromLovr[state.config[AUDIO_CAPTURE].format];
|
||||
config.playback.pDeviceID = state.config[AUDIO_PLAYBACK].device;
|
||||
config.capture.pDeviceID = state.config[AUDIO_CAPTURE].device;
|
||||
config.playback.channels = OUTPUT_CHANNELS;
|
||||
config.capture.channels = CAPTURE_CHANNELS;
|
||||
config.dataCallback = callbacks[type];
|
||||
ma_device_info *playbackDevices;
|
||||
ma_uint32 playbackDeviceCount;
|
||||
ma_device_info *captureDevices;
|
||||
ma_uint32 captureDeviceCount;
|
||||
ma_result gettingStatus = ma_context_get_devices(&state.context, &playbackDevices, &playbackDeviceCount, &captureDevices, &captureDeviceCount);
|
||||
lovrAssert(gettingStatus == MA_SUCCESS, "Failed to enumerate audio devices during initialization: %s (%d)", ma_result_description(gettingStatus), gettingStatus);
|
||||
|
||||
ma_device_config config;
|
||||
if (type == AUDIO_PLAYBACK) {
|
||||
ma_device_type deviceType = ma_device_type_playback;
|
||||
config = ma_device_config_init(deviceType);
|
||||
|
||||
lovrAssert(state.config[AUDIO_PLAYBACK].format == OUTPUT_FORMAT, "Only f32 playback format currently supported");
|
||||
config.playback.format = miniAudioFormatFromLovr[state.config[AUDIO_PLAYBACK].format];
|
||||
for(int i = 0; i < playbackDeviceCount && state.config[AUDIO_PLAYBACK].deviceName; i++) {
|
||||
if (strcmp(playbackDevices[i].name, state.config[AUDIO_PLAYBACK].deviceName) == 0) {
|
||||
config.playback.pDeviceID = &playbackDevices[i].id;
|
||||
}
|
||||
}
|
||||
if (state.config[AUDIO_PLAYBACK].deviceName && config.playback.pDeviceID == NULL) {
|
||||
lovrLog(LOG_WARN, "No audio playback device called '%s'; falling back to default.", state.config[AUDIO_PLAYBACK].deviceName);
|
||||
}
|
||||
config.playback.channels = OUTPUT_CHANNELS;
|
||||
} else {
|
||||
ma_device_type deviceType = ma_device_type_capture;
|
||||
config = ma_device_config_init(deviceType);
|
||||
|
||||
config.capture.format = miniAudioFormatFromLovr[state.config[AUDIO_CAPTURE].format];
|
||||
for(int i = 0; i < captureDeviceCount && state.config[AUDIO_CAPTURE].deviceName; i++) {
|
||||
if (strcmp(captureDevices[i].name, state.config[AUDIO_CAPTURE].deviceName) == 0) {
|
||||
config.capture.pDeviceID = &playbackDevices[i].id;
|
||||
}
|
||||
}
|
||||
if (state.config[AUDIO_CAPTURE].deviceName && config.capture.pDeviceID == NULL) {
|
||||
lovrLog(LOG_WARN, "No audio capture device called '%s'; falling back to default.", state.config[AUDIO_CAPTURE].deviceName);
|
||||
}
|
||||
config.capture.channels = CAPTURE_CHANNELS;
|
||||
}
|
||||
config.performanceProfile = ma_performance_profile_low_latency;
|
||||
config.dataCallback = callbacks[type];
|
||||
config.sampleRate = state.config[type].sampleRate;
|
||||
|
||||
int err = ma_device_init(&state.context, &config, &state.devices[type]);
|
||||
|
||||
ma_result err = ma_device_init(&state.context, &config, &state.devices[type]);
|
||||
if (err != MA_SUCCESS) {
|
||||
lovrLog(LOG_WARN, "audio", "Failed to enable audio device %d: %d\n", type, err);
|
||||
lovrLog(LOG_WARN, "audio", "Failed to enable %s audio device: %s (%d)\n", type == AUDIO_PLAYBACK ? "playback" : "capture", ma_result_description(err), err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -295,7 +325,7 @@ static void _lovrSourceAssignConverter(Source *source) {
|
|||
ma_data_converter *converter = malloc(sizeof(ma_data_converter));
|
||||
ma_result converterStatus = ma_data_converter_init(&config, converter);
|
||||
lovrAssert(converterStatus == MA_SUCCESS, "Problem creating Source data converter #%d: %d", state.converters.length, converterStatus);
|
||||
|
||||
|
||||
arr_expand(&state.converters, 1);
|
||||
state.converters.data[state.converters.length++] = source->converter = converter;
|
||||
}
|
||||
|
@ -306,7 +336,7 @@ Source* lovrSourceCreate(SoundData* sound, bool spatial) {
|
|||
source->sound = sound;
|
||||
lovrRetain(source->sound);
|
||||
source->volume = 1.f;
|
||||
|
||||
|
||||
source->spatial = spatial;
|
||||
mat4_identity(source->transform);
|
||||
_lovrSourceAssignConverter(source);
|
||||
|
@ -405,43 +435,45 @@ struct SoundData* lovrAudioGetCaptureStream()
|
|||
|
||||
// Devices
|
||||
|
||||
void lovrAudioGetDevices(AudioDevice **outDevices, size_t *outCount) {
|
||||
if(state.deviceInfos)
|
||||
free(state.deviceInfos);
|
||||
|
||||
ma_result gettingStatus = ma_context_get_devices(&state.context, NULL, NULL, NULL, NULL);
|
||||
lovrAssert(gettingStatus == MA_SUCCESS, "Failed to enumerate audio devices: %d", gettingStatus);
|
||||
*outCount = state.context.playbackDeviceInfoCount + state.context.captureDeviceInfoCount;
|
||||
*outDevices = state.deviceInfos = calloc(*outCount, sizeof(AudioDevice));
|
||||
for(int i = 0; i < *outCount; i++) {
|
||||
ma_device_info *mainfo = &state.context.pDeviceInfos[i];
|
||||
AudioDevice *lovrInfo = &state.deviceInfos[i];
|
||||
lovrInfo->name = mainfo->name;
|
||||
lovrInfo->type = i < state.context.playbackDeviceInfoCount ? AUDIO_PLAYBACK : AUDIO_CAPTURE;
|
||||
lovrInfo->isDefault = mainfo->_private.isDefault; // remove _private after bumping miniaudio
|
||||
lovrInfo->identifier = &mainfo->id;
|
||||
lovrInfo->minChannels = mainfo->minChannels;
|
||||
lovrInfo->maxChannels = mainfo->maxChannels;
|
||||
AudioDeviceArr* lovrAudioGetDevices(AudioType type) {
|
||||
ma_device_info *playbackDevices;
|
||||
ma_uint32 playbackDeviceCount;
|
||||
ma_device_info *captureDevices;
|
||||
ma_uint32 captureDeviceCount;
|
||||
ma_result gettingStatus = ma_context_get_devices(&state.context, &playbackDevices, &playbackDeviceCount, &captureDevices, &captureDeviceCount);
|
||||
lovrAssert(gettingStatus == MA_SUCCESS, "Failed to enumerate audio devices: %s (%d)", ma_result_description(gettingStatus), gettingStatus);
|
||||
|
||||
ma_uint32 count = type == AUDIO_PLAYBACK ? playbackDeviceCount : captureDeviceCount;
|
||||
ma_device_info *madevices = type == AUDIO_PLAYBACK ? playbackDevices : captureDevices;
|
||||
AudioDeviceArr *devices = calloc(1, sizeof(AudioDeviceArr));
|
||||
devices->capacity = devices->length = count;
|
||||
devices->data = calloc(count, sizeof(AudioDevice));
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
ma_device_info *mainfo = &madevices[i];
|
||||
AudioDevice *lovrInfo = &devices->data[i];
|
||||
lovrInfo->name = strdup(mainfo->name);
|
||||
lovrInfo->type = type;
|
||||
lovrInfo->isDefault = mainfo->isDefault;
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
void lovrAudioUseDevice(AudioDeviceIdentifier identifier, int sampleRate, SampleFormat format) {
|
||||
int deviceCount = state.context.playbackDeviceInfoCount + state.context.captureDeviceInfoCount;
|
||||
for(int i = 0; i < deviceCount; i++) {
|
||||
if (identifier == &state.context.pDeviceInfos[i].id) {
|
||||
AudioType type = i < state.context.playbackDeviceInfoCount ? AUDIO_PLAYBACK : AUDIO_CAPTURE;
|
||||
state.config[type].device = identifier;
|
||||
if (sampleRate) state.config[type].sampleRate = sampleRate;
|
||||
if (format != SAMPLE_INVALID) state.config[type].format = format;
|
||||
lovrLog(LOG_INFO, "audio", "Switching to %s device %s (%p)", type?"capture":"playback", state.context.pDeviceInfos[i].name, identifier);
|
||||
ma_device_uninit(&state.devices[type]);
|
||||
if(state.config[type].enable)
|
||||
lovrAudioInitDevice(type);
|
||||
if(state.config[type].start)
|
||||
ma_device_start(&state.devices[type]);
|
||||
|
||||
return;
|
||||
}
|
||||
void lovrAudioFreeDevices(AudioDeviceArr *devices) {
|
||||
for(int i = 0; i < devices->length; i++) {
|
||||
free((void*)devices->data[i].name);
|
||||
}
|
||||
lovrAssert(false, "Couldn't find the given identifier");
|
||||
}
|
||||
arr_free(devices);
|
||||
}
|
||||
|
||||
void lovrAudioUseDevice(AudioType type, const char *deviceName, int sampleRate, SampleFormat format) {
|
||||
free(state.config[type].deviceName);
|
||||
state.config[type].deviceName = strdup(deviceName);
|
||||
if (sampleRate) state.config[type].sampleRate = sampleRate;
|
||||
if (format != SAMPLE_INVALID) state.config[type].format = format;
|
||||
ma_device_uninit(&state.devices[type]);
|
||||
if(state.config[type].enable)
|
||||
lovrAudioInitDevice(type);
|
||||
if(state.config[type].start)
|
||||
ma_device_start(&state.devices[type]);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "modules/data/soundData.h"
|
||||
#include "core/arr.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
|
@ -26,7 +27,7 @@ typedef void* AudioDeviceIdentifier;
|
|||
typedef struct {
|
||||
bool enable;
|
||||
bool start;
|
||||
AudioDeviceIdentifier device;
|
||||
char *deviceName;
|
||||
int sampleRate;
|
||||
SampleFormat format;
|
||||
} AudioConfig;
|
||||
|
@ -35,9 +36,8 @@ typedef struct {
|
|||
AudioType type;
|
||||
const char *name;
|
||||
bool isDefault;
|
||||
AudioDeviceIdentifier identifier;
|
||||
int minChannels, maxChannels;
|
||||
} AudioDevice;
|
||||
typedef arr_t(AudioDevice) AudioDeviceArr;
|
||||
|
||||
bool lovrAudioInit(AudioConfig config[2]);
|
||||
void lovrAudioDestroy(void);
|
||||
|
@ -67,5 +67,9 @@ struct SoundData* lovrSourceGetSoundData(Source* source);
|
|||
|
||||
struct SoundData* lovrAudioGetCaptureStream();
|
||||
|
||||
void lovrAudioGetDevices(AudioDevice **outDevices, size_t *outCount);
|
||||
void lovrAudioUseDevice(AudioDeviceIdentifier identifier, int sampleRate, SampleFormat format);
|
||||
// Return a list of devices for the given type. Must be freed with lovrAudioFreeDevices.
|
||||
AudioDeviceArr* lovrAudioGetDevices(AudioType type);
|
||||
// free a list of devices returned from above call
|
||||
void lovrAudioFreeDevices(AudioDeviceArr *devices);
|
||||
|
||||
void lovrAudioUseDevice(AudioType type, const char *deviceName, int sampleRate, SampleFormat format);
|
||||
|
|
Loading…
Reference in New Issue