mirror of
https://github.com/Tha14/toxic.git
synced 2024-11-22 22:23:03 +01:00
Rework audio device handling
We now have at most one input and one output device open at any time, but can have multiple capture callbacks and multiple output sources.
This commit is contained in:
parent
dac0124f0f
commit
daf794c4a2
115
src/audio_call.c
115
src/audio_call.c
@ -61,6 +61,8 @@
|
|||||||
extern FriendsList Friends;
|
extern FriendsList Friends;
|
||||||
extern ToxWindow *windows[MAX_WINDOWS_NUM];
|
extern ToxWindow *windows[MAX_WINDOWS_NUM];
|
||||||
|
|
||||||
|
extern pthread_mutex_t tox_lock;
|
||||||
|
|
||||||
struct CallControl CallControl;
|
struct CallControl CallControl;
|
||||||
|
|
||||||
#define cbend pthread_exit(NULL)
|
#define cbend pthread_exit(NULL)
|
||||||
@ -146,7 +148,7 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init_devices(CallControl.av) == de_InternalError) {
|
if (init_devices() == de_InternalError) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
|
||||||
toxav_kill(CallControl.av);
|
toxav_kill(CallControl.av);
|
||||||
|
|
||||||
@ -182,11 +184,18 @@ void read_device_callback(const int16_t *captured, uint32_t size, void *data)
|
|||||||
int64_t sample_count = ((int64_t) CallControl.audio_sample_rate) * \
|
int64_t sample_count = ((int64_t) CallControl.audio_sample_rate) * \
|
||||||
((int64_t) CallControl.audio_frame_duration) / 1000;
|
((int64_t) CallControl.audio_frame_duration) / 1000;
|
||||||
|
|
||||||
if (sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number,
|
if (sample_count <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&tox_lock);
|
||||||
|
|
||||||
|
toxav_audio_send_frame(CallControl.av, friend_number,
|
||||||
captured, sample_count,
|
captured, sample_count,
|
||||||
CallControl.audio_channels,
|
CallControl.audio_channels,
|
||||||
CallControl.audio_sample_rate, &error) == false) {
|
CallControl.audio_sample_rate, &error);
|
||||||
}
|
|
||||||
|
pthread_mutex_unlock(&tox_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
|
void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
|
||||||
@ -208,8 +217,9 @@ int start_transmission(ToxWindow *self, Call *call)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError error = open_primary_device(input, &call->in_idx,
|
DeviceError error = open_input_device(&call->in_idx, read_device_callback, &self->num, true,
|
||||||
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
|
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
|
||||||
|
/* Set VAD as true for all; TODO: Make it more dynamic */
|
||||||
|
|
||||||
if (error != de_None) {
|
if (error != de_None) {
|
||||||
if (error == de_FailedStart) {
|
if (error == de_FailedStart) {
|
||||||
@ -221,14 +231,7 @@ int start_transmission(ToxWindow *self, Call *call)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (register_device_callback(self->num, call->in_idx,
|
if (open_output_device(&call->out_idx,
|
||||||
read_device_callback, &self->num, true) != de_None)
|
|
||||||
/* Set VAD as true for all; TODO: Make it more dynamic */
|
|
||||||
{
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (open_primary_device(output, &call->out_idx,
|
|
||||||
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None) {
|
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
|
||||||
call->has_output = 0;
|
call->has_output = 0;
|
||||||
@ -678,9 +681,9 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Refresh device list.
|
// Refresh device list.
|
||||||
get_devices_names();
|
get_al_device_names();
|
||||||
|
|
||||||
print_devices(self, type);
|
print_al_devices(self, type);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
on_error:
|
on_error:
|
||||||
@ -731,7 +734,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (
|
|||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_primary_device(type, selection) == de_InvalidSelection) {
|
if (set_al_device(type, selection) == de_InvalidSelection) {
|
||||||
error_str = "Invalid selection!";
|
error_str = "Invalid selection!";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
@ -741,86 +744,6 @@ on_error:
|
|||||||
print_err(self, error_str);
|
print_err(self, error_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
|
||||||
{
|
|
||||||
UNUSED_VAR(window);
|
|
||||||
UNUSED_VAR(m);
|
|
||||||
|
|
||||||
const char *error_str;
|
|
||||||
|
|
||||||
if (argc != 2) {
|
|
||||||
if (argc < 1) {
|
|
||||||
error_str = "Type must be specified!";
|
|
||||||
} else if (argc < 2) {
|
|
||||||
error_str = "Must have id!";
|
|
||||||
} else {
|
|
||||||
error_str = "Only two arguments allowed!";
|
|
||||||
}
|
|
||||||
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceType type;
|
|
||||||
|
|
||||||
if (strcmp(argv[1], "in") == 0) { /* Input devices */
|
|
||||||
type = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (strcmp(argv[1], "out") == 0) { /* Output devices */
|
|
||||||
type = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char *end;
|
|
||||||
long int selection = strtol(argv[2], &end, 10);
|
|
||||||
|
|
||||||
if (*end) {
|
|
||||||
error_str = "Invalid input";
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selection_valid(type, selection) == de_InvalidSelection) {
|
|
||||||
error_str = "Invalid selection!";
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If call is active, change device */
|
|
||||||
if (self->is_call) {
|
|
||||||
Call *this_call = &CallControl.calls[self->num];
|
|
||||||
|
|
||||||
if (this_call->ttas) {
|
|
||||||
|
|
||||||
|
|
||||||
if (type == output) {
|
|
||||||
pthread_mutex_lock(&this_call->mutex);
|
|
||||||
close_device(output, this_call->out_idx);
|
|
||||||
this_call->has_output = open_device(output, selection, &this_call->out_idx,
|
|
||||||
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
|
|
||||||
== de_None ? 1 : 0;
|
|
||||||
pthread_mutex_unlock(&this_call->mutex);
|
|
||||||
} else {
|
|
||||||
/* TODO: check for failure */
|
|
||||||
close_device(input, this_call->in_idx);
|
|
||||||
open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate,
|
|
||||||
CallControl.audio_frame_duration, CallControl.audio_channels);
|
|
||||||
/* Set VAD as true for all; TODO: Make it more dynamic */
|
|
||||||
register_device_callback(self->num, this_call->in_idx, read_device_callback, &self->num, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self->device_selection[type] = selection;
|
|
||||||
|
|
||||||
return;
|
|
||||||
on_error:
|
|
||||||
print_err(self, error_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
UNUSED_VAR(window);
|
UNUSED_VAR(window);
|
||||||
|
@ -48,123 +48,168 @@
|
|||||||
|
|
||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
typedef struct Device {
|
typedef struct FrameInfo {
|
||||||
ALCdevice *dhndl; /* Handle of device selected/opened */
|
uint32_t samples_per_frame;
|
||||||
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 */
|
|
||||||
|
|
||||||
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
|
|
||||||
uint32_t ref_count;
|
|
||||||
int32_t selection;
|
|
||||||
bool enable_VAD;
|
|
||||||
bool muted;
|
|
||||||
pthread_mutex_t mutex[1];
|
|
||||||
uint32_t sample_rate;
|
uint32_t sample_rate;
|
||||||
uint32_t frame_duration;
|
bool stereo;
|
||||||
int32_t sound_mode;
|
} 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.
|
||||||
|
*/
|
||||||
|
typedef struct Device {
|
||||||
|
bool active;
|
||||||
|
bool muted;
|
||||||
|
|
||||||
|
FrameInfo frame_info;
|
||||||
|
|
||||||
|
// used only by input devices:
|
||||||
|
DataHandleCallback cb;
|
||||||
|
void *cb_data;
|
||||||
|
// TODO: implement VAD, and fix the typo, or remove these.
|
||||||
|
bool enable_VAD;
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
float VAD_treshold; /* 40 is usually recommended value */
|
float VAD_treshold; /* 40 is usually recommended value */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// used only by output devices:
|
||||||
|
uint32_t source;
|
||||||
|
uint32_t buffers[OPENAL_BUFS];
|
||||||
|
bool source_open;
|
||||||
} Device;
|
} Device;
|
||||||
|
|
||||||
const char *ddevice_names[2]; /* Default device */
|
typedef struct AudioState {
|
||||||
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
|
ALCdevice *al_device[2];
|
||||||
static int size[2]; /* Size of above containers */
|
|
||||||
Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
|
|
||||||
uint32_t primary_device[2]; /* Primary device */
|
|
||||||
|
|
||||||
#ifdef AUDIO
|
Device devices[2][MAX_DEVICES];
|
||||||
static ToxAV *av = NULL;
|
uint32_t num_devices[2];
|
||||||
#endif /* AUDIO */
|
|
||||||
|
|
||||||
/* q_mutex */
|
FrameInfo capture_frame_info;
|
||||||
#define lock pthread_mutex_lock(&mutex)
|
|
||||||
#define unlock pthread_mutex_unlock(&mutex)
|
pthread_mutex_t mutex[2];
|
||||||
pthread_mutex_t mutex;
|
|
||||||
|
// 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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool thread_running = true,
|
static bool thread_running = true,
|
||||||
thread_paused = true; /* Thread control */
|
thread_paused = true; /* Thread control */
|
||||||
|
|
||||||
void *thread_poll(void *);
|
static void *poll_input(void *);
|
||||||
/* Meet devices */
|
|
||||||
#ifdef AUDIO
|
static uint32_t sound_mode(bool stereo)
|
||||||
DeviceError init_devices(ToxAV *av_)
|
|
||||||
#else
|
|
||||||
DeviceError init_devices(void)
|
|
||||||
#endif /* AUDIO */
|
|
||||||
{
|
{
|
||||||
get_devices_names();
|
return stereo ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sample_size(bool stereo)
|
||||||
|
{
|
||||||
|
return stereo ? 4 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError init_devices(void)
|
||||||
|
{
|
||||||
|
audio_state = calloc(1, sizeof(AudioState));
|
||||||
|
|
||||||
|
if (audio_state == NULL) {
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start poll thread
|
// Start poll thread
|
||||||
if (pthread_mutex_init(&mutex, NULL) != 0) {
|
|
||||||
return de_InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_t thread_id;
|
pthread_t thread_id;
|
||||||
|
|
||||||
if (pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) {
|
if (pthread_create(&thread_id, NULL, poll_input, NULL) != 0
|
||||||
|
|| pthread_detach(thread_id) != 0) {
|
||||||
return de_InternalError;
|
return de_InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AUDIO
|
return de_None;
|
||||||
av = av_;
|
|
||||||
#endif /* AUDIO */
|
|
||||||
|
|
||||||
return (DeviceError) de_None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError terminate_devices(void)
|
DeviceError terminate_devices(void)
|
||||||
{
|
{
|
||||||
/* Cleanup if needed */
|
lock(input);
|
||||||
lock;
|
|
||||||
thread_running = false;
|
thread_running = false;
|
||||||
unlock;
|
unlock(input);
|
||||||
|
|
||||||
sleep_thread(20000L);
|
sleep_thread(20000L);
|
||||||
|
|
||||||
if (pthread_mutex_destroy(&mutex) != 0) {
|
for (DeviceType type = input; type <= output; ++type) {
|
||||||
return (DeviceError) de_InternalError;
|
if (pthread_mutex_destroy(&audio_state->mutex[type]) != 0) {
|
||||||
|
return de_InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (DeviceError) de_None;
|
if (audio_state->current_al_device_name[type] != NULL) {
|
||||||
|
free(audio_state->current_al_device_name[type]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_devices_names(void)
|
free(audio_state);
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_al_device_names(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
const char *stringed_device_list;
|
const char *stringed_device_list;
|
||||||
|
|
||||||
size[input] = 0;
|
for (DeviceType type = input; type <= output; ++type) {
|
||||||
|
audio_state->num_al_devices[type] = 0;
|
||||||
if ((stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER))) {
|
|
||||||
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
|
||||||
|
|
||||||
for (; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input]) {
|
|
||||||
devices_names[input][size[input]] = stringed_device_list;
|
|
||||||
stringed_device_list += strlen(stringed_device_list) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size[output] = 0;
|
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
||||||
|
} else {
|
||||||
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) {
|
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) {
|
||||||
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||||
} else {
|
} else {
|
||||||
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (stringed_device_list) {
|
if (stringed_device_list != NULL) {
|
||||||
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
audio_state->default_al_device_name[type] = alcGetString(NULL,
|
||||||
|
type == input ? ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER : ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
for (; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output]) {
|
for (; *stringed_device_list != '\0'
|
||||||
devices_names[output][size[output]] = stringed_device_list;
|
&& 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;
|
stringed_device_list += strlen(stringed_device_list) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
||||||
{
|
{
|
||||||
@ -172,18 +217,17 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
|||||||
return de_InvalidSelection;
|
return de_InvalidSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock;
|
Device *device = &audio_state->devices[type][device_idx];
|
||||||
|
|
||||||
Device *device = running[type][device_idx];
|
if (!device->active) {
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
unlock;
|
|
||||||
return de_DeviceNotActive;
|
return de_DeviceNotActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock(type);
|
||||||
|
|
||||||
device->muted = !device->muted;
|
device->muted = !device->muted;
|
||||||
|
|
||||||
unlock;
|
unlock(type);
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,222 +238,301 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
|||||||
return de_InvalidSelection;
|
return de_InvalidSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock;
|
Device *device = &audio_state->devices[input][device_idx];
|
||||||
|
|
||||||
Device *device = running[input][device_idx];
|
if (!device->active) {
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
unlock;
|
|
||||||
return de_DeviceNotActive;
|
return de_DeviceNotActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock(input);
|
||||||
|
|
||||||
device->VAD_treshold = value;
|
device->VAD_treshold = value;
|
||||||
|
|
||||||
unlock;
|
unlock(input);
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static DeviceError close_al_device(DeviceType type)
|
||||||
DeviceError set_primary_device(DeviceType type, int32_t selection)
|
|
||||||
{
|
{
|
||||||
if (size[type] <= selection || selection < 0) {
|
if (audio_state->al_device[type] == NULL) {
|
||||||
return de_InvalidSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
primary_device[type] = selection;
|
|
||||||
|
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
|
|
||||||
uint8_t channels)
|
|
||||||
{
|
|
||||||
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_primary_device_name(DeviceType type, char *buf, int size)
|
|
||||||
{
|
|
||||||
memcpy(buf, ddevice_names[type], size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
if (size[type] <= selection || selection < 0) {
|
|
||||||
return de_InvalidSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channels != 1 && channels != 2) {
|
|
||||||
return de_UnsupportedMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock;
|
|
||||||
|
|
||||||
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
|
|
||||||
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
|
|
||||||
|
|
||||||
if (i == MAX_DEVICES) {
|
|
||||||
unlock;
|
|
||||||
return de_AllDevicesBusy;
|
|
||||||
} else {
|
|
||||||
*device_idx = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
|
|
||||||
if (running[type][i] && running[type][i]->selection == selection) {
|
|
||||||
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
|
|
||||||
|
|
||||||
running[type][*device_idx] = running[type][i];
|
|
||||||
running[type][i]->ref_count ++;
|
|
||||||
|
|
||||||
unlock;
|
|
||||||
return de_None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Device *device = running[type][*device_idx] = calloc(1, sizeof(Device));
|
|
||||||
device->selection = selection;
|
|
||||||
|
|
||||||
device->sample_rate = sample_rate;
|
|
||||||
device->frame_duration = frame_duration;
|
|
||||||
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
|
||||||
|
|
||||||
if (pthread_mutex_init(device->mutex, NULL) != 0) {
|
|
||||||
free(device);
|
|
||||||
unlock;
|
|
||||||
return de_InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == input) {
|
if (type == input) {
|
||||||
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
|
if (!alcCaptureCloseDevice(audio_state->al_device[type])) {
|
||||||
sample_rate, device->sound_mode, frame_size * 2);
|
return de_AlError;
|
||||||
#ifdef AUDIO
|
}
|
||||||
device->VAD_treshold = user_settings->VAD_treshold;
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
device->dhndl = alcOpenDevice(devices_names[type][selection]);
|
|
||||||
|
|
||||||
if (!device->dhndl) {
|
thread_paused = true;
|
||||||
free(device);
|
} else {
|
||||||
running[type][*device_idx] = NULL;
|
ALCcontext *context = alcGetCurrentContext();
|
||||||
unlock;
|
alcMakeContextCurrent(NULL);
|
||||||
|
alcDestroyContext(context);
|
||||||
|
|
||||||
|
if (!alcCloseDevice(audio_state->al_device[type])) {
|
||||||
|
return de_AlError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_state->al_device[type] = NULL;
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeviceError open_al_device(DeviceType type, FrameInfo frame_info)
|
||||||
|
{
|
||||||
|
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;
|
return de_FailedStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t zeros_size = frame_size * sizeof(uint16_t);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_source(Device *device)
|
||||||
|
{
|
||||||
|
if (device->source_open) {
|
||||||
|
alDeleteSources(1, &device->source);
|
||||||
|
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
||||||
|
|
||||||
|
device->source_open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DeviceError open_source(Device *device)
|
||||||
|
{
|
||||||
|
alGenBuffers(OPENAL_BUFS, device->buffers);
|
||||||
|
|
||||||
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
alGenSources((uint32_t)1, &device->source);
|
||||||
|
|
||||||
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->source_open = true;
|
||||||
|
|
||||||
|
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
||||||
|
|
||||||
|
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);
|
uint16_t *zeros = calloc(1, zeros_size);
|
||||||
|
|
||||||
if (zeros == NULL) {
|
if (zeros == NULL) {
|
||||||
free(device);
|
close_source(device);
|
||||||
running[type][*device_idx] = NULL;
|
|
||||||
unlock;
|
|
||||||
return de_FailedStart;
|
return de_FailedStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->ctx = alcCreateContext(device->dhndl, NULL);
|
for (int i = 0; i < OPENAL_BUFS; ++i) {
|
||||||
alcMakeContextCurrent(device->ctx);
|
alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros,
|
||||||
|
frame_size, device->frame_info.sample_rate);
|
||||||
alGenBuffers(OPENAL_BUFS, device->buffers);
|
|
||||||
alGenSources((uint32_t)1, &device->source);
|
|
||||||
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
|
||||||
|
|
||||||
for (i = 0; i < OPENAL_BUFS; ++i) {
|
|
||||||
alBufferData(device->buffers[i], device->sound_mode, zeros, zeros_size, sample_rate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(zeros);
|
free(zeros);
|
||||||
|
|
||||||
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
|
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
|
||||||
alSourcePlay(device->source);
|
alSourcePlay(device->source);
|
||||||
}
|
|
||||||
|
|
||||||
if (alcGetError(device->dhndl) != AL_NO_ERROR) {
|
if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
free(device);
|
close_source(device);
|
||||||
running[type][*device_idx] = NULL;
|
|
||||||
unlock;
|
|
||||||
return de_FailedStart;
|
return de_FailedStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == input) {
|
return de_None;
|
||||||
alcCaptureStart(device->dhndl);
|
}
|
||||||
thread_paused = false;
|
|
||||||
|
DeviceError set_al_device(DeviceType type, int32_t selection)
|
||||||
|
{
|
||||||
|
if (audio_state->num_al_devices[type] <= selection || selection < 0) {
|
||||||
|
return de_InvalidSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *name = audio_state->al_device_names[type][selection];
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return de_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(*cur_name, name);
|
||||||
|
|
||||||
|
if (audio_state->num_devices[type] > 0) {
|
||||||
|
// close any existing al_device and try to open new one, reopening existing sources
|
||||||
|
lock(type);
|
||||||
|
|
||||||
|
if (type == output) {
|
||||||
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
Device *device = &audio_state->devices[type][i];
|
||||||
|
|
||||||
|
if (device->active) {
|
||||||
|
close_source(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close_al_device(type);
|
||||||
|
|
||||||
|
DeviceError err = open_al_device(type, audio_state->capture_frame_info);
|
||||||
|
|
||||||
|
if (err != de_None) {
|
||||||
|
unlock(type);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == output) {
|
||||||
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
Device *device = &audio_state->devices[type][i];
|
||||||
|
|
||||||
|
if (device->active) {
|
||||||
|
open_source(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock;
|
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (channels != 1 && channels != 2) {
|
||||||
|
return de_UnsupportedMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t samples_per_frame = (sample_rate * frame_duration / 1000);
|
||||||
|
FrameInfo frame_info = {samples_per_frame, sample_rate, channels == 2};
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DEVICES && audio_state->devices[type][i].active; ++i);
|
||||||
|
|
||||||
|
if (i == MAX_DEVICES) {
|
||||||
|
return de_AllDevicesBusy;
|
||||||
|
}
|
||||||
|
|
||||||
|
*device_idx = i;
|
||||||
|
|
||||||
|
lock(type);
|
||||||
|
|
||||||
|
if (audio_state->al_device[type] == NULL) {
|
||||||
|
DeviceError err = open_al_device(type, frame_info);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device *device = &audio_state->devices[type][i];
|
||||||
|
device->active = true;
|
||||||
|
++audio_state->num_devices[type];
|
||||||
|
|
||||||
|
device->muted = false;
|
||||||
|
device->frame_info = frame_info;
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
device->cb = cb;
|
||||||
|
device->cb_data = cb_data;
|
||||||
|
device->enable_VAD = enable_VAD;
|
||||||
|
#ifdef AUDIO
|
||||||
|
device->VAD_treshold = user_settings->VAD_treshold;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
if (open_source(device) != de_None) {
|
||||||
|
device->active = false;
|
||||||
|
--audio_state->num_devices[type];
|
||||||
|
unlock(type);
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
||||||
{
|
{
|
||||||
if (device_idx >= MAX_DEVICES) {
|
if (device_idx >= MAX_DEVICES) {
|
||||||
return de_InvalidSelection;
|
return de_InvalidSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock;
|
lock(type);
|
||||||
|
|
||||||
Device *device = running[type][device_idx];
|
Device *device = &audio_state->devices[type][device_idx];
|
||||||
DeviceError rc = de_None;
|
|
||||||
|
|
||||||
if (!device) {
|
if (!device->active) {
|
||||||
unlock;
|
|
||||||
return de_DeviceNotActive;
|
return de_DeviceNotActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
running[type][device_idx] = NULL;
|
if (type == output) {
|
||||||
|
close_source(device);
|
||||||
if (!device->ref_count) {
|
|
||||||
if (type == input) {
|
|
||||||
if (!alcCaptureCloseDevice(device->dhndl)) {
|
|
||||||
rc = de_AlError;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (alcGetCurrentContext() != device->ctx) {
|
|
||||||
alcMakeContextCurrent(device->ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
alDeleteSources(1, &device->source);
|
device->active = false;
|
||||||
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
--audio_state->num_devices[type];
|
||||||
|
|
||||||
alcMakeContextCurrent(NULL);
|
DeviceError err = de_None;
|
||||||
|
|
||||||
if (device->ctx) {
|
if (audio_state->num_devices[type] == 0) {
|
||||||
alcDestroyContext(device->ctx);
|
err = close_al_device(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alcCloseDevice(device->dhndl)) {
|
unlock(type);
|
||||||
rc = de_AlError;
|
return err;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(device);
|
|
||||||
} else {
|
|
||||||
device->ref_count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock;
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
|
|
||||||
void *data, bool enable_VAD)
|
|
||||||
{
|
|
||||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
|
|
||||||
return de_InvalidSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
unlock;
|
|
||||||
|
|
||||||
return de_None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
|
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
|
||||||
@ -419,20 +542,25 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_
|
|||||||
return de_InvalidSelection;
|
return de_InvalidSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device *device = running[output][device_idx];
|
lock(output);
|
||||||
|
|
||||||
if (!device || device->muted) {
|
Device *device = &audio_state->devices[output][device_idx];
|
||||||
|
|
||||||
|
if (!device->active || device->muted) {
|
||||||
|
unlock(output);
|
||||||
return de_DeviceNotActive;
|
return de_DeviceNotActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(device->mutex);
|
|
||||||
|
|
||||||
|
|
||||||
ALuint bufid;
|
ALuint bufid;
|
||||||
ALint processed, queued;
|
ALint processed, queued;
|
||||||
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
||||||
|
|
||||||
|
if (audio_state->al_device[output] == NULL || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||||
|
unlock(output);
|
||||||
|
return de_AlError;
|
||||||
|
}
|
||||||
|
|
||||||
if (processed) {
|
if (processed) {
|
||||||
ALuint *bufids = malloc(processed * sizeof(ALuint));
|
ALuint *bufids = malloc(processed * sizeof(ALuint));
|
||||||
|
|
||||||
@ -448,12 +576,14 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_
|
|||||||
} else if (queued < 16) {
|
} else if (queued < 16) {
|
||||||
alGenBuffers(1, &bufid);
|
alGenBuffers(1, &bufid);
|
||||||
} else {
|
} else {
|
||||||
pthread_mutex_unlock(device->mutex);
|
unlock(output);
|
||||||
return de_Busy;
|
return de_Busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels,
|
const bool stereo = channels == 2;
|
||||||
|
alBufferData(bufid, sound_mode(stereo), data,
|
||||||
|
sample_count * sample_size(stereo),
|
||||||
sample_rate);
|
sample_rate);
|
||||||
alSourceQueueBuffers(device->source, 1, &bufid);
|
alSourceQueueBuffers(device->source, 1, &bufid);
|
||||||
|
|
||||||
@ -464,20 +594,15 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_
|
|||||||
alSourcePlay(device->source);
|
alSourcePlay(device->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock(output);
|
||||||
pthread_mutex_unlock(device->mutex);
|
|
||||||
return de_None;
|
return de_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FRAME_BUF_SIZE 16000
|
#define FRAME_BUF_SIZE 16000
|
||||||
|
|
||||||
void *thread_poll(void *arg) // TODO: maybe use thread for every input source
|
static void *poll_input(void *arg)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* NOTE: We only need to poll input devices for data.
|
|
||||||
*/
|
|
||||||
UNUSED_VAR(arg);
|
UNUSED_VAR(arg);
|
||||||
int32_t sample = 0;
|
|
||||||
|
|
||||||
int16_t *frame_buf = malloc(FRAME_BUF_SIZE * sizeof(int16_t));
|
int16_t *frame_buf = malloc(FRAME_BUF_SIZE * sizeof(int16_t));
|
||||||
|
|
||||||
@ -486,67 +611,57 @@ void *thread_poll(void *arg) // TODO: maybe use thread for every input source
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
lock;
|
lock(input);
|
||||||
|
|
||||||
if (!thread_running) {
|
if (!thread_running) {
|
||||||
free(frame_buf);
|
free(frame_buf);
|
||||||
unlock;
|
unlock(input);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool paused = thread_paused;
|
if (thread_paused) {
|
||||||
unlock;
|
unlock(input);
|
||||||
|
|
||||||
/* Wait for unpause. */
|
|
||||||
if (paused) {
|
|
||||||
sleep_thread(10000L);
|
sleep_thread(10000L);
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
for (uint32_t i = 0; i < size[input]; ++i) {
|
|
||||||
lock;
|
|
||||||
|
|
||||||
Device *device = running[input][i];
|
|
||||||
|
|
||||||
if (device != NULL) {
|
|
||||||
alcGetIntegerv(device->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
|
|
||||||
|
|
||||||
int f_size = (device->sample_rate * device->frame_duration / 1000);
|
|
||||||
|
|
||||||
if (sample < f_size || f_size > FRAME_BUF_SIZE) {
|
|
||||||
unlock;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
alcCaptureSamples(device->dhndl, frame_buf, f_size);
|
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);
|
||||||
|
|
||||||
if (device->muted) {
|
const uint32_t f_size = audio_state->capture_frame_info.samples_per_frame;
|
||||||
unlock;
|
|
||||||
continue;
|
if (available_samples >= f_size && f_size <= FRAME_BUF_SIZE) {
|
||||||
|
alcCaptureSamples(audio_state->al_device[input], frame_buf, f_size);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||||
|
Device *device = &audio_state->devices[input][i];
|
||||||
|
|
||||||
|
if (device->active && !device->muted && device->cb) {
|
||||||
|
const DataHandleCallback cb = device->cb;
|
||||||
|
void *const cb_data = device->cb_data;
|
||||||
|
unlock(input);
|
||||||
|
cb(frame_buf, f_size, cb_data);
|
||||||
|
lock(input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device->cb) {
|
|
||||||
device->cb(frame_buf, f_size, device->cb_data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock;
|
unlock(input);
|
||||||
}
|
|
||||||
|
|
||||||
sleep_thread(5000L);
|
sleep_thread(5000L);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_devices(ToxWindow *self, DeviceType type)
|
void print_al_devices(ToxWindow *self, DeviceType type)
|
||||||
{
|
{
|
||||||
int i;
|
for (int i = 0; i < audio_state->num_al_devices[type]; ++i) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG,
|
||||||
for (i = 0; i < size[type]; ++i) {
|
audio_state->current_al_device_name[type]
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i,
|
&& strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0,
|
||||||
devices_names[type][i]);
|
0, "%d: %s", i, audio_state->al_device_names[type][i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -554,14 +669,5 @@ void print_devices(ToxWindow *self, DeviceType type)
|
|||||||
|
|
||||||
DeviceError selection_valid(DeviceType type, int32_t selection)
|
DeviceError selection_valid(DeviceType type, int32_t selection)
|
||||||
{
|
{
|
||||||
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
return (audio_state->num_al_devices[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
||||||
}
|
|
||||||
|
|
||||||
void *get_device_callback_data(uint32_t device_idx)
|
|
||||||
{
|
|
||||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return running[input][device_idx]->cb_data;
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#define AUDIO_DEVICE_H
|
#define AUDIO_DEVICE_H
|
||||||
|
|
||||||
#define OPENAL_BUFS 5
|
#define OPENAL_BUFS 5
|
||||||
|
#define MAX_OPENAL_DEVICES 32
|
||||||
#define MAX_DEVICES 32
|
#define MAX_DEVICES 32
|
||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
@ -55,20 +56,11 @@ typedef enum DeviceError {
|
|||||||
typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data);
|
typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data);
|
||||||
|
|
||||||
|
|
||||||
#ifdef AUDIO
|
|
||||||
DeviceError init_devices(ToxAV *av);
|
|
||||||
#else
|
|
||||||
DeviceError init_devices(void);
|
DeviceError init_devices(void);
|
||||||
#endif /* AUDIO */
|
|
||||||
|
|
||||||
void get_devices_names(void);
|
void get_al_device_names(void);
|
||||||
DeviceError terminate_devices(void);
|
DeviceError terminate_devices(void);
|
||||||
|
|
||||||
/* Callback handles ready data from INPUT device */
|
|
||||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
|
|
||||||
void *data, bool enable_VAD);
|
|
||||||
void *get_device_callback_data(uint32_t device_idx);
|
|
||||||
|
|
||||||
/* toggle device mute */
|
/* toggle device mute */
|
||||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
@ -76,21 +68,23 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
|||||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DeviceError set_primary_device(DeviceType type, int32_t selection);
|
DeviceError set_al_device(DeviceType type, int32_t selection);
|
||||||
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
|
|
||||||
uint8_t channels);
|
|
||||||
/* Start device */
|
/* Start device */
|
||||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
|
DeviceError open_input_device(uint32_t *device_idx,
|
||||||
uint32_t frame_duration, uint8_t channels);
|
DataHandleCallback cb, void *cb_data, bool enable_VAD,
|
||||||
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||||
|
DeviceError open_output_device(uint32_t *device_idx,
|
||||||
|
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||||
|
|
||||||
/* Stop device */
|
/* Stop device */
|
||||||
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
/* Write data to device */
|
/* Write data to output device */
|
||||||
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
|
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
|
||||||
uint32_t sample_rate);
|
uint32_t sample_rate);
|
||||||
|
|
||||||
void print_devices(ToxWindow *self, DeviceType type);
|
void print_al_devices(ToxWindow *self, DeviceType type);
|
||||||
void get_primary_device_name(DeviceType type, char *buf, int size);
|
|
||||||
|
|
||||||
DeviceError selection_valid(DeviceType type, int32_t selection);
|
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||||
#endif /* AUDIO_DEVICE_H */
|
#endif /* AUDIO_DEVICE_H */
|
||||||
|
@ -1427,7 +1427,6 @@ ToxWindow *new_chat(Tox *m, uint32_t friendnum)
|
|||||||
ret->onEnd = &chat_onEnd;
|
ret->onEnd = &chat_onEnd;
|
||||||
|
|
||||||
ret->is_call = false;
|
ret->is_call = false;
|
||||||
ret->device_selection[0] = ret->device_selection[1] = -1;
|
|
||||||
ret->ringing_sound = -1;
|
ret->ringing_sound = -1;
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
@ -1364,7 +1364,6 @@ ToxWindow *new_friendlist(void)
|
|||||||
ret->onEnd = &friendlist_onAV;
|
ret->onEnd = &friendlist_onAV;
|
||||||
|
|
||||||
ret->is_call = false;
|
ret->is_call = false;
|
||||||
ret->device_selection[0] = ret->device_selection[1] = -1;
|
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
ret->num = -1;
|
ret->num = -1;
|
||||||
|
@ -180,7 +180,7 @@ void m_open_device(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Blah error check */
|
/* Blah error check */
|
||||||
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
|
open_output_device(&Control.device_idx, 48000, 20, 1);
|
||||||
|
|
||||||
device_opened = true;
|
device_opened = true;
|
||||||
}
|
}
|
||||||
|
14
src/toxic.c
14
src/toxic.c
@ -103,6 +103,7 @@ struct av_thread av_thread;
|
|||||||
struct arg_opts arg_opts;
|
struct arg_opts arg_opts;
|
||||||
struct user_settings *user_settings = NULL;
|
struct user_settings *user_settings = NULL;
|
||||||
|
|
||||||
|
pthread_mutex_t tox_lock;
|
||||||
|
|
||||||
static struct user_password {
|
static struct user_password {
|
||||||
bool data_is_encrypted;
|
bool data_is_encrypted;
|
||||||
@ -945,9 +946,12 @@ static void do_toxic(Tox *m)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&tox_lock);
|
||||||
|
|
||||||
tox_iterate(m, NULL);
|
tox_iterate(m, NULL);
|
||||||
do_tox_connection(m);
|
do_tox_connection(m);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&tox_lock);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1392,6 +1396,10 @@ int main(int argc, char **argv)
|
|||||||
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&tox_lock, NULL) != 0) {
|
||||||
|
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
||||||
|
|
||||||
if (settings_load(user_settings, p) == -1) {
|
if (settings_load(user_settings, p) == -1) {
|
||||||
@ -1457,12 +1465,12 @@ int main(int argc, char **argv)
|
|||||||
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_primary_device(input, user_settings->audio_in_dev);
|
set_al_device(input, user_settings->audio_in_dev);
|
||||||
set_primary_device(output, user_settings->audio_out_dev);
|
set_al_device(output, user_settings->audio_out_dev);
|
||||||
|
|
||||||
#elif SOUND_NOTIFY
|
#elif SOUND_NOTIFY
|
||||||
|
|
||||||
if (init_devices() == de_InternalError) {
|
if (init_audio() == de_InternalError) {
|
||||||
queue_init_message("Failed to init audio devices");
|
queue_init_message("Failed to init audio devices");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,8 +455,6 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->video_device_selection[type] = selection;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
on_error:
|
on_error:
|
||||||
print_err(self, error_str);
|
print_err(self, error_str);
|
||||||
|
@ -157,16 +157,9 @@ struct ToxWindow {
|
|||||||
void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int);
|
void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int);
|
void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int);
|
||||||
|
|
||||||
int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
|
|
||||||
bool is_call;
|
bool is_call;
|
||||||
int ringing_sound;
|
int ringing_sound;
|
||||||
|
|
||||||
#ifdef VIDEO
|
|
||||||
|
|
||||||
int video_device_selection[2]; /* -1 if not set, if set uses these selections instead of primary video device */
|
|
||||||
|
|
||||||
#endif /* VIDEO */
|
|
||||||
|
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
int active_box; /* For box notify */
|
int active_box; /* For box notify */
|
||||||
|
Loading…
Reference in New Issue
Block a user