1
0
mirror of https://github.com/Tha14/toxic.git synced 2024-11-15 14:03:03 +01:00
toxic/src/audio_device.c

567 lines
14 KiB
C
Raw Normal View History

/* audio_device.c
*
*
* 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/>.
*
*/
#include "audio_device.h"
2014-07-21 23:48:39 +02:00
#ifdef AUDIO
2014-06-21 02:04:25 +02:00
#include "audio_call.h"
2014-07-21 23:48:39 +02:00
#endif
2014-06-21 02:04:25 +02:00
#include "line_info.h"
2014-07-21 23:48:39 +02:00
#include "settings.h"
#include "misc_tools.h"
2014-06-21 02:04:25 +02:00
#include <AL/al.h>
#include <AL/alc.h>
/* 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 */
#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>
#define inline__ inline __attribute__((always_inline))
2014-07-21 23:48:39 +02:00
extern struct user_settings *user_settings;
2014-06-21 02:04:25 +02:00
typedef struct Device {
2014-06-21 02:04:25 +02:00
ALCdevice *dhndl; /* Handle of device selected/opened */
ALCcontext *ctx; /* Device context */
DataHandleCallback cb; /* Use this to handle data from input device usually */
void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */
2015-08-28 03:29:34 +02:00
2014-07-21 23:48:39 +02:00
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
2014-09-28 16:47:31 +02:00
uint32_t ref_count;
2014-06-21 02:04:25 +02:00
int32_t selection;
bool enable_VAD;
bool muted;
2014-07-10 01:24:14 +02:00
pthread_mutex_t mutex[1];
2015-08-28 03:29:34 +02:00
uint32_t sample_rate;
2014-07-21 23:48:39 +02:00
uint32_t frame_duration;
int32_t sound_mode;
#ifdef AUDIO
2014-07-21 23:48:39 +02:00
float VAD_treshold; /* 40 is usually recommended value */
#endif
2014-06-21 02:04:25 +02:00
} Device;
const char *ddevice_names[2]; /* Default device */
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
static int size[2]; /* Size of above containers */
2014-09-28 16:47:31 +02:00
Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
uint32_t primary_device[2]; /* Primary device */
2014-06-21 02:04:25 +02:00
#ifdef AUDIO
static ToxAV *av = NULL;
#endif /* AUDIO */
2014-06-21 02:04:25 +02:00
/* q_mutex */
#define lock pthread_mutex_lock(&mutex)
#define unlock pthread_mutex_unlock(&mutex)
pthread_mutex_t mutex;
2015-08-28 03:29:34 +02:00
bool thread_running = true,
thread_paused = true; /* Thread control */
2014-06-21 02:04:25 +02:00
void *thread_poll(void *);
2014-06-21 02:04:25 +02:00
/* Meet devices */
#ifdef AUDIO
DeviceError init_devices(ToxAV *av_)
#else
DeviceError init_devices(void)
#endif /* AUDIO */
2014-06-21 02:04:25 +02:00
{
2017-10-31 17:08:06 +01:00
get_devices_names();
2014-09-27 09:01:23 +02:00
2014-06-21 02:04:25 +02:00
// Start poll thread
if (pthread_mutex_init(&mutex, NULL) != 0) {
2014-09-24 04:51:56 +02:00
return de_InternalError;
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
pthread_t thread_id;
if (pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) {
2015-08-28 03:29:34 +02:00
return de_InternalError;
}
2015-08-28 03:29:34 +02:00
#ifdef AUDIO
2014-06-21 02:04:25 +02:00
av = av_;
#endif /* AUDIO */
2015-08-28 03:29:34 +02:00
2014-07-21 23:48:39 +02:00
return (DeviceError) de_None;
2014-06-21 02:04:25 +02:00
}
DeviceError terminate_devices(void)
2014-06-21 02:04:25 +02:00
{
/* Cleanup if needed */
2015-08-28 03:29:34 +02:00
lock;
2014-06-21 02:04:25 +02:00
thread_running = false;
2015-08-28 03:29:34 +02:00
unlock;
2014-06-21 02:04:25 +02:00
usleep(20000);
2015-08-28 03:29:34 +02:00
if (pthread_mutex_destroy(&mutex) != 0) {
2014-09-24 04:51:56 +02:00
return (DeviceError) de_InternalError;
}
2015-08-28 03:29:34 +02:00
2014-07-21 23:48:39 +02:00
return (DeviceError) de_None;
2014-06-21 02:04:25 +02:00
}
void get_devices_names(void)
{
2017-10-31 17:08:06 +01:00
const char *stringed_device_list;
size[input] = 0;
if ((stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER))) {
2017-10-31 17:08:06 +01:00
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for (; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input]) {
2017-10-31 17:08:06 +01:00
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen(stringed_device_list) + 1;
2017-10-31 17:08:06 +01:00
}
}
size[output] = 0;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) {
2017-10-31 17:08:06 +01:00
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
} else {
2017-10-31 17:08:06 +01:00
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
}
2017-10-31 17:08:06 +01:00
if (stringed_device_list) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for (; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output]) {
2017-10-31 17:08:06 +01:00
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen(stringed_device_list) + 1;
2017-10-31 17:08:06 +01:00
}
}
}
DeviceError device_mute(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
lock;
2015-08-28 03:29:34 +02:00
Device *device = running[type][device_idx];
2015-08-28 03:29:34 +02:00
if (!device) {
unlock;
return de_DeviceNotActive;
}
2015-08-28 03:29:34 +02:00
device->muted = !device->muted;
2015-08-28 03:29:34 +02:00
unlock;
return de_None;
}
#ifdef AUDIO
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
{
if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
lock;
2014-11-29 15:36:31 +01:00
Device *device = running[input][device_idx];
2014-11-29 15:36:31 +01:00
2015-08-28 03:29:34 +02:00
if (!device) {
unlock;
return de_DeviceNotActive;
}
2014-11-29 15:36:31 +01:00
device->VAD_treshold = value;
2014-11-29 15:36:31 +01:00
unlock;
return de_None;
}
2014-07-21 23:48:39 +02:00
#endif
2014-06-21 02:04:25 +02:00
DeviceError set_primary_device(DeviceType type, int32_t selection)
{
if (size[type] <= selection || selection < 0) {
return de_InvalidSelection;
}
2014-06-21 02:04:25 +02:00
primary_device[type] = selection;
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
return de_None;
}
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
uint8_t channels)
2014-06-21 02:04:25 +02:00
{
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
2014-06-21 02:04:25 +02:00
}
2014-11-29 15:36:31 +01:00
void get_primary_device_name(DeviceType type, char *buf, int size)
{
memcpy(buf, ddevice_names[type], size);
}
2014-06-21 02:04:25 +02:00
// TODO: generate buffers separately
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
uint32_t frame_duration, uint8_t channels)
2014-06-21 02:04:25 +02:00
{
if (size[type] <= selection || selection < 0) {
return de_InvalidSelection;
}
if (channels != 1 && channels != 2) {
return de_UnsupportedMode;
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
lock;
2014-07-21 23:48:39 +02:00
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
uint32_t i;
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
2015-08-28 03:29:34 +02:00
if (i == MAX_DEVICES) {
unlock;
return de_AllDevicesBusy;
} else {
*device_idx = i;
}
2015-08-28 03:29:34 +02:00
2014-09-28 16:47:31 +02:00
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
if (running[type][i] && running[type][i]->selection == selection) {
2014-09-28 16:47:31 +02:00
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
2015-08-28 03:29:34 +02:00
running[type][*device_idx] = running[type][i];
2014-09-28 16:47:31 +02:00
running[type][i]->ref_count ++;
2015-08-28 03:29:34 +02:00
2014-09-28 16:47:31 +02:00
unlock;
return de_None;
}
}
2015-08-28 03:29:34 +02:00
Device *device = running[type][*device_idx] = calloc(1, sizeof(Device));
2014-06-21 02:04:25 +02:00
device->selection = selection;
2015-08-28 03:29:34 +02:00
2014-07-21 23:48:39 +02:00
device->sample_rate = sample_rate;
device->frame_duration = frame_duration;
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
2015-08-28 03:29:34 +02:00
if (pthread_mutex_init(device->mutex, NULL) != 0) {
free(device);
unlock;
return de_InternalError;
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
if (type == input) {
2015-08-28 03:29:34 +02:00
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
sample_rate, device->sound_mode, frame_size * 2);
#ifdef AUDIO
device->VAD_treshold = user_settings->VAD_treshold;
#endif
} else {
2014-06-21 02:04:25 +02:00
device->dhndl = alcOpenDevice(devices_names[type][selection]);
if (!device->dhndl) {
2014-06-21 02:04:25 +02:00
free(device);
running[type][*device_idx] = NULL;
unlock;
return de_FailedStart;
}
2015-08-28 03:29:34 +02:00
size_t zeros_size = frame_size * sizeof(uint16_t);
uint16_t *zeros = calloc(1, zeros_size);
if (zeros == NULL) {
free(device);
running[type][*device_idx] = NULL;
unlock;
return de_FailedStart;
}
2014-06-21 02:04:25 +02:00
device->ctx = alcCreateContext(device->dhndl, NULL);
alcMakeContextCurrent(device->ctx);
2015-08-28 03:29:34 +02:00
2014-07-21 23:48:39 +02:00
alGenBuffers(OPENAL_BUFS, device->buffers);
2014-06-21 02:04:25 +02:00
alGenSources((uint32_t)1, &device->source);
alSourcei(device->source, AL_LOOPING, AL_FALSE);
2015-08-28 03:29:34 +02:00
for (i = 0; i < OPENAL_BUFS; ++i) {
alBufferData(device->buffers[i], device->sound_mode, zeros, zeros_size, sample_rate);
2014-06-21 02:04:25 +02:00
}
2015-08-28 03:29:34 +02:00
free(zeros);
2014-07-21 23:48:39 +02:00
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
2014-06-21 02:04:25 +02:00
alSourcePlay(device->source);
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
if (alcGetError(device->dhndl) != AL_NO_ERROR) {
free(device);
running[type][*device_idx] = NULL;
unlock;
return de_FailedStart;
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
if (type == input) {
alcCaptureStart(device->dhndl);
thread_paused = false;
2014-06-21 02:04:25 +02:00
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
unlock;
return de_None;
}
DeviceError close_device(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
lock;
Device *device = running[type][device_idx];
DeviceError rc = de_None;
2015-08-28 03:29:34 +02:00
if (!device) {
2014-06-21 02:04:25 +02:00
unlock;
return de_DeviceNotActive;
}
2015-08-28 03:29:34 +02:00
running[type][device_idx] = NULL;
2015-08-28 03:29:34 +02:00
if (!device->ref_count) {
2014-06-21 02:04:25 +02:00
if (type == input) {
if (!alcCaptureCloseDevice(device->dhndl)) {
rc = de_AlError;
}
} else {
if (alcGetCurrentContext() != device->ctx) {
alcMakeContextCurrent(device->ctx);
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
alDeleteSources(1, &device->source);
2014-07-21 23:48:39 +02:00
alDeleteBuffers(OPENAL_BUFS, device->buffers);
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
alcMakeContextCurrent(NULL);
if (device->ctx) {
alcDestroyContext(device->ctx);
}
if (!alcCloseDevice(device->dhndl)) {
rc = de_AlError;
}
2014-06-21 02:04:25 +02:00
}
2015-08-28 03:29:34 +02:00
2014-09-28 16:47:31 +02:00
free(device);
} else {
device->ref_count--;
}
2015-08-28 03:29:34 +02:00
unlock;
return rc;
2014-06-21 02:04:25 +02:00
}
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void *data, bool enable_VAD)
2015-08-28 03:29:34 +02:00
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
2014-06-21 02:04:25 +02:00
return de_InvalidSelection;
}
2014-12-10 00:29:07 +01:00
2014-06-21 02:04:25 +02:00
lock;
running[input][device_idx]->cb = callback;
running[input][device_idx]->cb_data = data;
running[input][device_idx]->enable_VAD = enable_VAD;
running[input][device_idx]->friend_number = friend_number;
2014-06-21 02:04:25 +02:00
unlock;
2014-12-10 00:29:07 +01:00
2014-06-21 02:04:25 +02:00
return de_None;
}
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
2016-04-05 06:49:46 +02:00
uint32_t sample_rate)
2014-06-21 02:04:25 +02:00
{
if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
2014-12-10 00:29:07 +01:00
Device *device = running[output][device_idx];
2014-11-26 21:22:34 +01:00
if (!device || device->muted) {
return de_DeviceNotActive;
}
2014-11-26 21:22:34 +01:00
2014-07-10 01:24:14 +02:00
pthread_mutex_lock(device->mutex);
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
if (processed) {
ALuint *bufids = malloc(processed * sizeof(ALuint));
if (bufids == NULL) {
return de_InternalError;
}
2014-07-10 01:24:14 +02:00
alSourceUnqueueBuffers(device->source, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0];
free(bufids);
} else if (queued < 16) {
alGenBuffers(1, &bufid);
} else {
2014-07-10 01:24:14 +02:00
pthread_mutex_unlock(device->mutex);
return de_Busy;
2014-06-21 02:04:25 +02:00
}
2014-12-10 00:29:07 +01:00
alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels,
sample_rate);
2014-07-10 01:24:14 +02:00
alSourceQueueBuffers(device->source, 1, &bufid);
2014-07-10 01:24:14 +02:00
ALint state;
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) {
alSourcePlay(device->source);
}
2014-11-26 21:22:34 +01:00
2014-07-10 01:24:14 +02:00
pthread_mutex_unlock(device->mutex);
return de_None;
2014-06-21 02:04:25 +02:00
}
#define FRAME_BUF_SIZE 16000
void *thread_poll(void *arg) // TODO: maybe use thread for every input source
2014-06-21 02:04:25 +02:00
{
/*
* NOTE: We only need to poll input devices for data.
*/
UNUSED_VAR(arg);
2014-06-21 02:04:25 +02:00
int32_t sample = 0;
2015-08-28 03:29:34 +02: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
while (1) {
2015-08-28 03:29:34 +02:00
lock;
2015-08-28 03:29:34 +02:00
if (!thread_running) {
free(frame_buf);
2015-08-28 03:29:34 +02:00
unlock;
break;
}
2016-09-23 00:00:14 +02:00
bool paused = thread_paused;
2015-08-28 03:29:34 +02:00
unlock;
/* Wait for unpause. */
2016-09-23 00:00:14 +02:00
if (paused) {
usleep(10000);
}
else {
for (uint32_t i = 0; i < size[input]; ++i) {
2014-06-21 02:04:25 +02:00
lock;
Device *device = running[input][i];
if (device != NULL) {
alcGetIntegerv(device->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
2015-08-28 03:29:34 +02:00
int f_size = (device->sample_rate * device->frame_duration / 1000);
2015-08-28 03:29:34 +02:00
if (sample < f_size || f_size > FRAME_BUF_SIZE) {
2014-06-21 02:04:25 +02:00
unlock;
continue;
}
alcCaptureSamples(device->dhndl, frame_buf, f_size);
2015-08-28 03:29:34 +02:00
if (device->muted) {
2014-11-30 15:27:45 +01:00
unlock;
continue;
}
2015-08-28 03:29:34 +02:00
if (device->cb) {
device->cb(frame_buf, f_size, device->cb_data);
}
2015-08-28 03:29:34 +02:00
}
2014-06-21 02:04:25 +02:00
unlock;
}
usleep(5000);
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);
}
void print_devices(ToxWindow *self, DeviceType type)
2014-06-21 02:04:25 +02:00
{
2014-11-26 06:48:58 +01:00
int i;
for (i = 0; i < size[type]; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i,
devices_names[type][i]);
}
2014-06-21 02:04:25 +02:00
return;
}
DeviceError selection_valid(DeviceType type, int32_t selection)
{
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
}
void *get_device_callback_data(uint32_t device_idx)
2014-06-21 02:04:25 +02:00
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
2014-06-21 02:04:25 +02:00
return NULL;
}
2015-08-28 03:29:34 +02:00
2014-06-21 02:04:25 +02:00
return running[input][device_idx]->cb_data;
}