2015-05-25 23:59:06 +02:00
|
|
|
/* audio_device.c
|
2014-06-24 23:45:14 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
|
|
*
|
|
|
|
* This file is part of Toxic.
|
|
|
|
*
|
|
|
|
* Toxic is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Toxic 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2015-05-25 23:59:06 +02:00
|
|
|
#include "audio_device.h"
|
2014-07-21 23:48:39 +02:00
|
|
|
|
2014-06-21 02:04:25 +02:00
|
|
|
#include "line_info.h"
|
2014-07-21 23:48:39 +02:00
|
|
|
#include "settings.h"
|
2020-03-30 18:56:42 +02:00
|
|
|
#include "misc_tools.h"
|
2014-06-21 02:04:25 +02:00
|
|
|
|
|
|
|
#include <AL/al.h>
|
|
|
|
#include <AL/alc.h>
|
2014-07-31 13:14:33 +02:00
|
|
|
/* compatibility with older versions of OpenAL */
|
|
|
|
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
|
|
|
#include <AL/alext.h>
|
2018-10-08 19:39:04 +02:00
|
|
|
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
2014-06-22 02:18:23 +02:00
|
|
|
|
2014-09-23 03:24:45 +02:00
|
|
|
#include <stdbool.h>
|
2014-06-21 02:04:25 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
2020-05-07 02:00:00 +02:00
|
|
|
#include <math.h>
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2014-09-23 03:24:45 +02:00
|
|
|
extern struct user_settings *user_settings;
|
2020-05-07 02:00:00 +02:00
|
|
|
extern struct Winthread Winthread;
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
typedef struct FrameInfo {
|
|
|
|
uint32_t samples_per_frame;
|
|
|
|
uint32_t sample_rate;
|
|
|
|
bool stereo;
|
|
|
|
} FrameInfo;
|
|
|
|
|
|
|
|
/* A virtual input/output device, abstracting the currently selected openal
|
|
|
|
* device (which may change during the lifetime of the virtual device).
|
|
|
|
* We refer to a virtual device as a "device", and refer to an underlying
|
|
|
|
* openal device as an "al_device".
|
|
|
|
* Multiple virtual devices may be open at once; the callback of each virtual
|
|
|
|
* input device has data captured from the input al_device passed to it, and
|
|
|
|
* each virtual output device acts as a source for the output al_device.
|
|
|
|
*/
|
2014-09-23 03:24:45 +02:00
|
|
|
typedef struct Device {
|
2020-04-04 02:00:00 +02:00
|
|
|
bool active;
|
2014-09-23 03:24:45 +02:00
|
|
|
bool muted;
|
2020-04-04 02:00:00 +02:00
|
|
|
|
|
|
|
FrameInfo frame_info;
|
|
|
|
|
|
|
|
// used only by input devices:
|
|
|
|
DataHandleCallback cb;
|
|
|
|
void *cb_data;
|
2020-05-07 02:00:00 +02:00
|
|
|
float VAD_threshold;
|
|
|
|
uint32_t VAD_samples_remaining;
|
2020-04-04 02:00:00 +02:00
|
|
|
|
|
|
|
// used only by output devices:
|
|
|
|
uint32_t source;
|
|
|
|
uint32_t buffers[OPENAL_BUFS];
|
|
|
|
bool source_open;
|
2014-06-21 02:04:25 +02:00
|
|
|
} Device;
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
typedef struct AudioState {
|
|
|
|
ALCdevice *al_device[2];
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
Device devices[2][MAX_DEVICES];
|
|
|
|
uint32_t num_devices[2];
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
FrameInfo capture_frame_info;
|
2020-05-07 02:00:00 +02:00
|
|
|
float input_volume;
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
// mutexes to prevent changes to input resp. output devices and al_devices
|
2020-05-07 02:00:00 +02:00
|
|
|
// during poll_input iterations resp. calls to write_out;
|
|
|
|
// mutex[input] also used to lock input_volume which poll_input writes to.
|
2020-04-04 02:00:00 +02:00
|
|
|
pthread_mutex_t mutex[2];
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
// TODO: unused
|
|
|
|
const char *default_al_device_name[2]; /* Default devices */
|
|
|
|
|
|
|
|
const char *al_device_names[2][MAX_OPENAL_DEVICES]; /* Available devices */
|
|
|
|
uint32_t num_al_devices[2];
|
|
|
|
char *current_al_device_name[2];
|
|
|
|
} AudioState;
|
|
|
|
|
|
|
|
static AudioState *audio_state;
|
|
|
|
|
|
|
|
static void lock(DeviceType type)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&audio_state->mutex[type]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unlock(DeviceType type)
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&audio_state->mutex[type]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool thread_running = true,
|
|
|
|
thread_paused = true; /* Thread control */
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
#ifdef AUDIO
|
2020-04-04 02:00:00 +02:00
|
|
|
static void *poll_input(void *);
|
2020-05-07 02:00:00 +02:00
|
|
|
#endif
|
2020-04-04 02:00:00 +02:00
|
|
|
|
|
|
|
static uint32_t sound_mode(bool stereo)
|
|
|
|
{
|
|
|
|
return stereo ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t sample_size(bool stereo)
|
|
|
|
{
|
|
|
|
return stereo ? 4 : 2;
|
|
|
|
}
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2018-09-08 19:23:07 +02:00
|
|
|
DeviceError init_devices(void)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
audio_state = calloc(1, sizeof(AudioState));
|
2014-09-27 09:01:23 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (audio_state == NULL) {
|
2014-09-24 04:51:56 +02:00
|
|
|
return de_InternalError;
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
get_al_device_names();
|
|
|
|
|
|
|
|
for (DeviceType type = input; type <= output; ++type) {
|
|
|
|
audio_state->al_device[type] = NULL;
|
|
|
|
|
|
|
|
if (pthread_mutex_init(&audio_state->mutex[type], NULL) != 0) {
|
|
|
|
return de_InternalError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
#ifdef AUDIO
|
2020-04-04 02:00:00 +02:00
|
|
|
// Start poll thread
|
2014-06-21 02:04:25 +02:00
|
|
|
pthread_t thread_id;
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (pthread_create(&thread_id, NULL, poll_input, NULL) != 0
|
|
|
|
|| pthread_detach(thread_id) != 0) {
|
2015-08-28 03:29:34 +02:00
|
|
|
return de_InternalError;
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
#endif
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
return de_None;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
|
|
|
|
2018-09-08 19:23:07 +02:00
|
|
|
DeviceError terminate_devices(void)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
lock(input);
|
2014-06-21 02:04:25 +02:00
|
|
|
thread_running = false;
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(input);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-11-03 18:17:48 +01:00
|
|
|
sleep_thread(20000L);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
for (DeviceType type = input; type <= output; ++type) {
|
|
|
|
if (pthread_mutex_destroy(&audio_state->mutex[type]) != 0) {
|
|
|
|
return de_InternalError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (audio_state->current_al_device_name[type] != NULL) {
|
|
|
|
free(audio_state->current_al_device_name[type]);
|
|
|
|
}
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
free(audio_state);
|
|
|
|
|
|
|
|
return de_None;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
void get_al_device_names(void)
|
2018-03-04 05:54:12 +01:00
|
|
|
{
|
2017-10-31 17:08:06 +01:00
|
|
|
const char *stringed_device_list;
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
for (DeviceType type = input; type <= output; ++type) {
|
|
|
|
audio_state->num_al_devices[type] = 0;
|
2017-10-31 17:08:06 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (type == input) {
|
|
|
|
stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
|
|
|
} else {
|
|
|
|
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) {
|
|
|
|
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
|
|
|
} else {
|
|
|
|
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
|
|
|
}
|
2017-10-31 17:08:06 +01:00
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (stringed_device_list != NULL) {
|
|
|
|
audio_state->default_al_device_name[type] = alcGetString(NULL,
|
|
|
|
type == input ? ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER : ALC_DEFAULT_DEVICE_SPECIFIER);
|
2017-10-31 17:08:06 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
for (; *stringed_device_list != '\0'
|
|
|
|
&& audio_state->num_al_devices[type] < MAX_OPENAL_DEVICES; ++audio_state->num_al_devices[type]) {
|
|
|
|
audio_state->al_device_names[type][audio_state->num_al_devices[type]] = stringed_device_list;
|
|
|
|
stringed_device_list += strlen(stringed_device_list) + 1;
|
|
|
|
}
|
2017-10-31 17:08:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-22 02:18:23 +02:00
|
|
|
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
|
|
|
{
|
2018-07-18 17:33:16 +02:00
|
|
|
if (device_idx >= MAX_DEVICES) {
|
|
|
|
return de_InvalidSelection;
|
|
|
|
}
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
Device *device = &audio_state->devices[type][device_idx];
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (!device->active) {
|
2014-06-22 02:18:23 +02:00
|
|
|
return de_DeviceNotActive;
|
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
lock(type);
|
|
|
|
|
2014-06-22 02:18:23 +02:00
|
|
|
device->muted = !device->muted;
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(type);
|
2014-06-22 02:18:23 +02:00
|
|
|
return de_None;
|
|
|
|
}
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
bool device_is_muted(DeviceType type, uint32_t device_idx)
|
|
|
|
{
|
|
|
|
if (device_idx >= MAX_DEVICES) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Device *device = &audio_state->devices[type][device_idx];
|
|
|
|
|
|
|
|
if (!device->active) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return device->muted;
|
|
|
|
}
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
DeviceError device_set_VAD_threshold(uint32_t device_idx, float value)
|
2014-06-22 02:18:23 +02:00
|
|
|
{
|
2018-07-18 17:33:16 +02:00
|
|
|
if (device_idx >= MAX_DEVICES) {
|
|
|
|
return de_InvalidSelection;
|
|
|
|
}
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
Device *device = &audio_state->devices[input][device_idx];
|
2014-11-29 15:36:31 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (!device->active) {
|
2014-06-22 02:18:23 +02:00
|
|
|
return de_DeviceNotActive;
|
|
|
|
}
|
2014-11-29 15:36:31 +01:00
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
if (value <= 0.0f) {
|
|
|
|
value = 0.0f;
|
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
lock(input);
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
device->VAD_threshold = value;
|
2014-11-29 15:36:31 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(input);
|
2014-06-22 02:18:23 +02:00
|
|
|
return de_None;
|
|
|
|
}
|
2020-05-07 02:00:00 +02:00
|
|
|
|
|
|
|
float device_get_VAD_threshold(uint32_t device_idx)
|
|
|
|
{
|
|
|
|
if (device_idx >= MAX_DEVICES) {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Device *device = &audio_state->devices[input][device_idx];
|
|
|
|
|
|
|
|
if (!device->active) {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return device->VAD_threshold;
|
|
|
|
}
|
2014-07-21 23:48:39 +02:00
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z)
|
|
|
|
{
|
|
|
|
if (device_idx >= MAX_DEVICES) {
|
|
|
|
return de_InvalidSelection;
|
|
|
|
}
|
|
|
|
|
|
|
|
Device *device = &audio_state->devices[output][device_idx];
|
|
|
|
|
|
|
|
if (!device->active) {
|
|
|
|
return de_DeviceNotActive;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock(output);
|
|
|
|
|
|
|
|
alSource3f(device->source, AL_POSITION, x, y, z);
|
|
|
|
|
|
|
|
unlock(output);
|
|
|
|
|
|
|
|
if (!audio_state->al_device[output] || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
|
|
|
return de_AlError;
|
|
|
|
}
|
|
|
|
|
|
|
|
return de_None;
|
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
static DeviceError close_al_device(DeviceType type)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
if (audio_state->al_device[type] == NULL) {
|
|
|
|
return de_None;
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (type == input) {
|
|
|
|
if (!alcCaptureCloseDevice(audio_state->al_device[type])) {
|
|
|
|
return de_AlError;
|
|
|
|
}
|
|
|
|
|
|
|
|
thread_paused = true;
|
|
|
|
} else {
|
|
|
|
ALCcontext *context = alcGetCurrentContext();
|
|
|
|
alcMakeContextCurrent(NULL);
|
|
|
|
alcDestroyContext(context);
|
|
|
|
|
|
|
|
if (!alcCloseDevice(audio_state->al_device[type])) {
|
|
|
|
return de_AlError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
audio_state->al_device[type] = NULL;
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2014-06-21 02:04:25 +02:00
|
|
|
return de_None;
|
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
static DeviceError open_al_device(DeviceType type, FrameInfo frame_info)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
audio_state->al_device[type] = type == input
|
|
|
|
? alcCaptureOpenDevice(audio_state->current_al_device_name[type],
|
|
|
|
frame_info.sample_rate, sound_mode(frame_info.stereo), frame_info.samples_per_frame * 2)
|
|
|
|
: alcOpenDevice(audio_state->current_al_device_name[type]);
|
|
|
|
|
|
|
|
if (audio_state->al_device[type] == NULL) {
|
|
|
|
return de_FailedStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == input) {
|
|
|
|
alcCaptureStart(audio_state->al_device[type]);
|
|
|
|
thread_paused = false;
|
|
|
|
|
|
|
|
audio_state->capture_frame_info = frame_info;
|
|
|
|
} else {
|
|
|
|
alcMakeContextCurrent(alcCreateContext(audio_state->al_device[type], NULL));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alcGetError(audio_state->al_device[type]) != AL_NO_ERROR) {
|
|
|
|
close_al_device(type);
|
|
|
|
return de_AlError;
|
|
|
|
}
|
|
|
|
|
|
|
|
return de_None;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
static void close_source(Device *device)
|
2014-11-29 15:36:31 +01:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
if (device->source_open) {
|
|
|
|
alDeleteSources(1, &device->source);
|
|
|
|
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
|
|
|
|
|
|
|
device->source_open = false;
|
|
|
|
}
|
2014-11-29 15:36:31 +01:00
|
|
|
}
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
static DeviceError open_source(Device *device)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
alGenBuffers(OPENAL_BUFS, device->buffers);
|
|
|
|
|
|
|
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
|
|
|
return de_FailedStart;
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2014-07-27 01:49:59 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
alGenSources((uint32_t)1, &device->source);
|
|
|
|
|
|
|
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
|
|
|
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
|
|
|
return de_FailedStart;
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
device->source_open = true;
|
2014-07-27 01:49:59 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
const uint32_t frame_size = device->frame_info.samples_per_frame * sample_size(device->frame_info.stereo);
|
|
|
|
size_t zeros_size = frame_size / 2;
|
|
|
|
uint16_t *zeros = calloc(1, zeros_size);
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (zeros == NULL) {
|
|
|
|
close_source(device);
|
|
|
|
return de_FailedStart;
|
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
for (int i = 0; i < OPENAL_BUFS; ++i) {
|
|
|
|
alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros,
|
|
|
|
frame_size, device->frame_info.sample_rate);
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
free(zeros);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
|
|
|
|
alSourcePlay(device->source);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
|
|
|
close_source(device);
|
|
|
|
return de_FailedStart;
|
2014-09-28 16:47:31 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
return de_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceError set_al_device(DeviceType type, int32_t selection)
|
|
|
|
{
|
|
|
|
if (audio_state->num_al_devices[type] <= selection || selection < 0) {
|
|
|
|
return de_InvalidSelection;
|
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
const char *name = audio_state->al_device_names[type][selection];
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
char **cur_name = &audio_state->current_al_device_name[type];
|
|
|
|
|
|
|
|
if (*cur_name != NULL) {
|
|
|
|
free(*cur_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
*cur_name = malloc(strlen(name) + 1);
|
|
|
|
|
|
|
|
if (*cur_name == NULL) {
|
2014-09-28 00:13:45 +02:00
|
|
|
return de_InternalError;
|
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
strcpy(*cur_name, name);
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (audio_state->num_devices[type] > 0) {
|
|
|
|
// close any existing al_device and try to open new one, reopening existing sources
|
|
|
|
lock(type);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (type == output) {
|
|
|
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
|
|
Device *device = &audio_state->devices[type][i];
|
2020-10-28 05:54:06 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (device->active) {
|
|
|
|
close_source(device);
|
|
|
|
}
|
|
|
|
}
|
2020-10-28 05:54:06 +01:00
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
close_al_device(type);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
DeviceError err = open_al_device(type, audio_state->capture_frame_info);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (err != de_None) {
|
|
|
|
unlock(type);
|
|
|
|
return err;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (type == output) {
|
|
|
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
|
|
Device *device = &audio_state->devices[type][i];
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (device->active) {
|
|
|
|
open_source(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(type);
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2014-06-21 02:04:25 +02:00
|
|
|
return de_None;
|
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
static DeviceError open_device(DeviceType type, uint32_t *device_idx,
|
|
|
|
DataHandleCallback cb, void *cb_data, bool enable_VAD,
|
|
|
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
if (channels != 1 && channels != 2) {
|
|
|
|
return de_UnsupportedMode;
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
const uint32_t samples_per_frame = (sample_rate * frame_duration / 1000);
|
|
|
|
FrameInfo frame_info = {samples_per_frame, sample_rate, channels == 2};
|
|
|
|
|
|
|
|
uint32_t i;
|
2020-10-27 20:20:21 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
for (i = 0; i < MAX_DEVICES && audio_state->devices[type][i].active; ++i);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (i == MAX_DEVICES) {
|
|
|
|
return de_AllDevicesBusy;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
*device_idx = i;
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
lock(type);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (audio_state->al_device[type] == NULL) {
|
|
|
|
DeviceError err = open_al_device(type, frame_info);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (err != de_None) {
|
|
|
|
unlock(type);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
} else if (type == input) {
|
|
|
|
// Use previously set frame info on existing capture device
|
|
|
|
frame_info = audio_state->capture_frame_info;
|
|
|
|
}
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
Device *device = &audio_state->devices[type][i];
|
|
|
|
device->active = true;
|
|
|
|
++audio_state->num_devices[type];
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
device->muted = false;
|
|
|
|
device->frame_info = frame_info;
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (type == input) {
|
|
|
|
device->cb = cb;
|
|
|
|
device->cb_data = cb_data;
|
|
|
|
#ifdef AUDIO
|
2020-05-07 02:00:00 +02:00
|
|
|
device->VAD_threshold = enable_VAD ? user_settings->VAD_threshold : 0.0f;
|
|
|
|
#else
|
|
|
|
device->VAD_threshold = 0.0f;
|
2020-04-04 02:00:00 +02:00
|
|
|
#endif
|
2018-07-18 17:33:16 +02:00
|
|
|
} else {
|
2020-04-04 02:00:00 +02:00
|
|
|
if (open_source(device) != de_None) {
|
|
|
|
device->active = false;
|
|
|
|
--audio_state->num_devices[type];
|
|
|
|
unlock(type);
|
|
|
|
return de_FailedStart;
|
|
|
|
}
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(type);
|
|
|
|
return de_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceError open_input_device(uint32_t *device_idx,
|
|
|
|
DataHandleCallback cb, void *cb_data, bool enable_VAD,
|
|
|
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
|
|
|
{
|
|
|
|
return open_device(input, device_idx,
|
|
|
|
cb, cb_data, enable_VAD,
|
|
|
|
sample_rate, frame_duration, channels);
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceError open_output_device(uint32_t *device_idx,
|
|
|
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
|
|
|
{
|
|
|
|
return open_device(output, device_idx,
|
|
|
|
0, 0, 0,
|
|
|
|
sample_rate, frame_duration, channels);
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
2015-08-28 03:29:34 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
if (device_idx >= MAX_DEVICES) {
|
2014-06-21 02:04:25 +02:00
|
|
|
return de_InvalidSelection;
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2014-12-10 00:29:07 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
lock(type);
|
2014-12-10 00:29:07 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
Device *device = &audio_state->devices[type][device_idx];
|
|
|
|
|
|
|
|
if (!device->active) {
|
|
|
|
return de_DeviceNotActive;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == output) {
|
|
|
|
close_source(device);
|
|
|
|
}
|
|
|
|
|
|
|
|
device->active = false;
|
|
|
|
--audio_state->num_devices[type];
|
|
|
|
|
|
|
|
DeviceError err = de_None;
|
|
|
|
|
|
|
|
if (audio_state->num_devices[type] == 0) {
|
|
|
|
err = close_al_device(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock(type);
|
|
|
|
return err;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
|
|
|
|
uint32_t sample_rate)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2018-07-18 17:33:16 +02:00
|
|
|
if (device_idx >= MAX_DEVICES) {
|
|
|
|
return de_InvalidSelection;
|
|
|
|
}
|
2014-12-10 00:29:07 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
lock(output);
|
2014-11-26 21:22:34 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
Device *device = &audio_state->devices[output][device_idx];
|
|
|
|
|
|
|
|
if (!device->active || device->muted) {
|
|
|
|
unlock(output);
|
2018-07-18 17:33:16 +02:00
|
|
|
return de_DeviceNotActive;
|
|
|
|
}
|
2014-11-26 21:22:34 +01:00
|
|
|
|
2014-07-10 01:24:14 +02:00
|
|
|
ALuint bufid;
|
|
|
|
ALint processed, queued;
|
|
|
|
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
|
|
|
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
2014-12-10 00:29:07 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (audio_state->al_device[output] == NULL || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
|
|
|
unlock(output);
|
|
|
|
return de_AlError;
|
|
|
|
}
|
|
|
|
|
2016-09-25 03:07:04 +02:00
|
|
|
if (processed) {
|
2020-10-28 05:54:06 +01:00
|
|
|
ALuint *bufids = malloc(processed * sizeof(ALuint));
|
|
|
|
|
|
|
|
if (bufids == NULL) {
|
2020-05-07 02:00:00 +02:00
|
|
|
unlock(output);
|
2020-10-28 05:54:06 +01:00
|
|
|
return de_InternalError;
|
|
|
|
}
|
|
|
|
|
2014-07-10 01:24:14 +02:00
|
|
|
alSourceUnqueueBuffers(device->source, processed, bufids);
|
|
|
|
alDeleteBuffers(processed - 1, bufids + 1);
|
|
|
|
bufid = bufids[0];
|
2020-10-28 05:54:06 +01:00
|
|
|
free(bufids);
|
2018-07-18 17:33:16 +02:00
|
|
|
} else if (queued < 16) {
|
|
|
|
alGenBuffers(1, &bufid);
|
|
|
|
} else {
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(output);
|
2014-07-10 01:24:14 +02:00
|
|
|
return de_Busy;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
2014-12-10 00:29:07 +01:00
|
|
|
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
const bool stereo = channels == 2;
|
|
|
|
alBufferData(bufid, sound_mode(stereo), data,
|
|
|
|
sample_count * sample_size(stereo),
|
2016-09-25 03:07:04 +02:00
|
|
|
sample_rate);
|
2014-07-10 01:24:14 +02:00
|
|
|
alSourceQueueBuffers(device->source, 1, &bufid);
|
2014-11-26 23:39:02 +01:00
|
|
|
|
2014-07-10 01:24:14 +02:00
|
|
|
ALint state;
|
|
|
|
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
|
2014-11-26 23:39:02 +01:00
|
|
|
|
2018-07-18 17:33:16 +02:00
|
|
|
if (state != AL_PLAYING) {
|
|
|
|
alSourcePlay(device->source);
|
|
|
|
}
|
2014-11-26 21:22:34 +01:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(output);
|
2014-07-10 01:24:14 +02:00
|
|
|
return de_None;
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
#ifdef AUDIO
|
|
|
|
/* Adapted from qtox,
|
|
|
|
* Copyright © 2014-2019 by The qTox Project Contributors
|
|
|
|
*
|
|
|
|
* return normalized volume of buffer in range 0.0-100.0
|
|
|
|
*/
|
|
|
|
float volume(int16_t *frame, uint32_t samples)
|
|
|
|
{
|
|
|
|
float sum_of_squares = 0;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < samples; i++) {
|
|
|
|
const float sample = (float)(frame[i]) / INT16_MAX;
|
|
|
|
sum_of_squares += powf(sample, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
const float root_mean_square = sqrtf(sum_of_squares / samples);
|
|
|
|
const float root_two = 1.414213562;
|
|
|
|
|
|
|
|
// normalizedVolume == 1.0 corresponds to a sine wave of maximal amplitude
|
|
|
|
const float normalized_volume = root_mean_square * root_two;
|
|
|
|
|
|
|
|
return 100.0f * fminf(1.0f, normalized_volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Time in ms for which we continue to capture audio after VAD is triggered:
|
|
|
|
#define VAD_TIME 250
|
|
|
|
|
2020-10-25 20:37:45 +01:00
|
|
|
#define FRAME_BUF_SIZE 16000
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
static void *poll_input(void *arg)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-03-30 18:56:42 +02:00
|
|
|
UNUSED_VAR(arg);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-10-25 20:37:45 +01:00
|
|
|
int16_t *frame_buf = malloc(FRAME_BUF_SIZE * sizeof(int16_t));
|
|
|
|
|
|
|
|
if (frame_buf == NULL) {
|
|
|
|
exit_toxic_err("failed in thread_poll", FATALERR_MEMORY);
|
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2016-09-25 03:07:04 +02:00
|
|
|
while (1) {
|
2020-04-04 02:00:00 +02:00
|
|
|
lock(input);
|
2016-09-25 03:07:04 +02:00
|
|
|
|
2015-08-28 03:29:34 +02:00
|
|
|
if (!thread_running) {
|
2020-10-27 20:20:21 +01:00
|
|
|
free(frame_buf);
|
2020-04-04 02:00:00 +02:00
|
|
|
unlock(input);
|
2015-08-28 03:29:34 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-09-23 00:00:14 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (thread_paused) {
|
|
|
|
unlock(input);
|
2020-11-03 18:17:48 +01:00
|
|
|
sleep_thread(10000L);
|
2020-04-04 02:00:00 +02:00
|
|
|
continue;
|
2016-09-23 00:00:14 +02:00
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (audio_state->al_device[input] != NULL) {
|
|
|
|
int32_t available_samples;
|
|
|
|
alcGetIntegerv(audio_state->al_device[input], ALC_CAPTURE_SAMPLES, sizeof(int32_t), &available_samples);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
const uint32_t f_size = audio_state->capture_frame_info.samples_per_frame;
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (available_samples >= f_size && f_size <= FRAME_BUF_SIZE) {
|
|
|
|
alcCaptureSamples(audio_state->al_device[input], frame_buf, f_size);
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
unlock(input);
|
|
|
|
pthread_mutex_lock(&Winthread.lock);
|
|
|
|
lock(input);
|
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
float frame_volume = volume(frame_buf, f_size);
|
|
|
|
|
|
|
|
audio_state->input_volume = frame_volume;
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
|
|
|
Device *device = &audio_state->devices[input][i];
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2020-05-07 02:00:00 +02:00
|
|
|
if (device->VAD_threshold != 0.0f) {
|
|
|
|
if (frame_volume >= device->VAD_threshold) {
|
|
|
|
device->VAD_samples_remaining = VAD_TIME * (audio_state->capture_frame_info.sample_rate / 1000);
|
|
|
|
} else if (device->VAD_samples_remaining < f_size) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
device->VAD_samples_remaining -= f_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
if (device->active && !device->muted && device->cb) {
|
2020-05-07 02:00:00 +02:00
|
|
|
device->cb(frame_buf, f_size, device->cb_data);
|
2018-07-18 17:33:16 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
}
|
2020-05-07 02:00:00 +02:00
|
|
|
|
|
|
|
pthread_mutex_unlock(&Winthread.lock);
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-04 02:00:00 +02:00
|
|
|
|
|
|
|
unlock(input);
|
|
|
|
sleep_thread(5000L);
|
2014-06-21 02:04:25 +02:00
|
|
|
}
|
2015-08-28 03:29:34 +02:00
|
|
|
|
2014-06-21 02:04:25 +02:00
|
|
|
pthread_exit(NULL);
|
|
|
|
}
|
2020-05-07 02:00:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
float get_input_volume(void)
|
|
|
|
{
|
|
|
|
float ret = 0.0f;
|
|
|
|
|
|
|
|
if (audio_state->al_device[input] != NULL) {
|
|
|
|
lock(input);
|
|
|
|
ret = audio_state->input_volume;
|
|
|
|
unlock(input);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2014-06-21 02:04:25 +02:00
|
|
|
|
2020-04-04 02:00:00 +02:00
|
|
|
void print_al_devices(ToxWindow *self, DeviceType type)
|
2014-06-21 02:04:25 +02:00
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
for (int i = 0; i < audio_state->num_al_devices[type]; ++i) {
|
|
|
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG,
|
|
|
|
audio_state->current_al_device_name[type]
|
|
|
|
&& strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0,
|
|
|
|
0, "%d: %s", i, audio_state->al_device_names[type][i]);
|
2017-10-31 16:32:34 +01:00
|
|
|
}
|
2014-07-25 04:43:32 +02:00
|
|
|
|
2014-06-21 02:04:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceError selection_valid(DeviceType type, int32_t selection)
|
|
|
|
{
|
2020-04-04 02:00:00 +02:00
|
|
|
return (audio_state->num_al_devices[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
2014-09-27 02:32:48 +02:00
|
|
|
}
|