Add files via upload

This commit is contained in:
billythehippo 2022-10-13 19:23:02 +03:00 committed by GitHub
parent 68ccaf20c1
commit 1f345af128
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1628 additions and 0 deletions

View File

@ -0,0 +1,224 @@
#include "alsa_backend.h"
#include <alsa/asoundlib.h>
#include "../../common/logger.h"
#define ALSA_DEVICE_NAME_DEFAULT "default"
struct alsa_backend_t
{
struct audio_backend_t parent;
snd_pcm_t* alsa_handle;
size_t frame_size;
};
static int alsa_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config);
static int alsa_close(audio_backend_handle_t handle);
static int alsa_write(audio_backend_handle_t handle, char const* data, size_t size);
static int alsa_read(audio_backend_handle_t handle, char* data, size_t size);
static snd_pcm_format_t vban_to_alsa_format(enum VBanBitResolution bit_resolution)
{
switch (bit_resolution)
{
case VBAN_BITFMT_8_INT:
return SND_PCM_FORMAT_S8;
case VBAN_BITFMT_16_INT:
return SND_PCM_FORMAT_S16;
case VBAN_BITFMT_24_INT:
return SND_PCM_FORMAT_S24;
case VBAN_BITFMT_32_INT:
return SND_PCM_FORMAT_S32;
case VBAN_BITFMT_32_FLOAT:
return SND_PCM_FORMAT_FLOAT;
case VBAN_BITFMT_64_FLOAT:
return SND_PCM_FORMAT_FLOAT64;
default:
return SND_PCM_FORMAT_UNKNOWN;
}
}
int alsa_backend_init(audio_backend_handle_t* handle)
{
struct alsa_backend_t* alsa_backend = 0;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: null handle pointer", __func__);
return -EINVAL;
}
alsa_backend = calloc(1, sizeof(struct alsa_backend_t));
if (alsa_backend == 0)
{
logger_log(LOG_FATAL, "%s: could not allocate memory", __func__);
return -ENOMEM;
}
alsa_backend->parent.open = alsa_open;
alsa_backend->parent.close = alsa_close;
alsa_backend->parent.write = alsa_write;
alsa_backend->parent.read = alsa_read;
*handle = (audio_backend_handle_t)alsa_backend;
return 0;
}
int alsa_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config)
{
int ret;
struct alsa_backend_t* const alsa_backend = (struct alsa_backend_t*)handle;
size_t frame_nb = 0;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
alsa_backend->frame_size = VBanBitResolutionSize[config->bit_fmt] * config->nb_channels;
frame_nb = buffer_size / alsa_backend->frame_size;
ret = snd_pcm_open(&alsa_backend->alsa_handle, (output_name[0] == '\0') ? ALSA_DEVICE_NAME_DEFAULT : output_name,
(direction == AUDIO_OUT) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0);
if (ret < 0)
{
logger_log(LOG_FATAL, "%s: open error: %s", __func__, snd_strerror(ret));
alsa_backend->alsa_handle = 0;
return ret;
}
logger_log(LOG_DEBUG, "%s: snd_pcm_open", __func__);
ret = snd_pcm_set_params(alsa_backend->alsa_handle,
vban_to_alsa_format(config->bit_fmt),
SND_PCM_ACCESS_RW_INTERLEAVED,
config->nb_channels,
config->sample_rate,
1,
((unsigned int)frame_nb* 1000000) / config->sample_rate);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: set_params error: %s", __func__, snd_strerror(ret));
alsa_close(handle);
return ret;
}
ret = snd_pcm_prepare(alsa_backend->alsa_handle);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: prepare error: %s", __func__, snd_strerror(ret));
alsa_close(handle);
return ret;
}
return 0;
}
int alsa_close(audio_backend_handle_t handle)
{
int ret = 0;
struct alsa_backend_t* const alsa_backend = (struct alsa_backend_t*)handle;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
if (alsa_backend->alsa_handle == 0)
{
/** nothing to do */
return 0;
}
ret = snd_pcm_close(alsa_backend->alsa_handle);
alsa_backend->alsa_handle = 0;
return ret;
}
int alsa_write(audio_backend_handle_t handle, char const* data, size_t size)
{
int ret = 0;
struct alsa_backend_t* const alsa_backend = (struct alsa_backend_t*)handle;
size_t nb_frame = 0;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
if (alsa_backend->alsa_handle == 0)
{
logger_log(LOG_ERROR, "%s: device not open", __func__);
return -ENODEV;
}
nb_frame = size / alsa_backend->frame_size;
ret = snd_pcm_writei(alsa_backend->alsa_handle, data, nb_frame);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: snd_pcm_writei failed: %s", __func__, snd_strerror(ret));
ret = snd_pcm_recover(alsa_backend->alsa_handle, ret, 0);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: snd_pcm_recover failed: %s", __func__, snd_strerror(ret));
}
}
else if (ret > 0 && ret < nb_frame)
{
logger_log(LOG_ERROR, "%s: short write (expected %lu, wrote %i)", __func__, nb_frame, ret);
}
return ret * alsa_backend->frame_size;
}
int alsa_read(audio_backend_handle_t handle, char* data, size_t size)
{
int ret = 0;
struct alsa_backend_t* const alsa_backend = (struct alsa_backend_t*)handle;
size_t nb_frame = 0;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
if (alsa_backend->alsa_handle == 0)
{
logger_log(LOG_ERROR, "%s: device not open", __func__);
return -ENODEV;
}
nb_frame = size / alsa_backend->frame_size;
ret = snd_pcm_readi(alsa_backend->alsa_handle, data, nb_frame);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: snd_pcm_writei failed: %s", __func__, snd_strerror(ret));
ret = snd_pcm_recover(alsa_backend->alsa_handle, ret, 0);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: snd_pcm_writei failed: %s", __func__, snd_strerror(ret));
}
}
else if (ret > 0 && ret < nb_frame)
{
logger_log(LOG_ERROR, "%s: short read (expected %lu, wrote %i)", __func__, nb_frame, ret);
}
return ret * alsa_backend->frame_size;
}

View File

@ -0,0 +1,11 @@
#ifndef __ALSA_BACKEND_H__
#define __ALSA_BACKEND_H__
#include "audio_backend.h"
#define ALSA_BACKEND_NAME "alsa"
int alsa_backend_init(audio_backend_handle_t* handle);
#endif /*__ALSA_BACKEND_H__*/

View File

@ -0,0 +1,82 @@
#include "audio_backend.h"
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include "../../common/logger.h"
#include "pipe_backend.h"
#include "file_backend.h"
#if ALSA
#include "alsa_backend.h"
#endif
#if PULSEAUDIO
#include "pulseaudio_backend.h"
#endif
#if JACK
#include "jack_backend.h"
#endif
#define HELP_TEXT_LEN 2048
struct backend_list_item_t
{
char name[AUDIO_BACKEND_NAME_SIZE];
audio_backend_init_f init_function;
};
static struct backend_list_item_t const backend_list[] =
{
#if ALSA
{ ALSA_BACKEND_NAME, alsa_backend_init },
#endif
#if PULSEAUDIO
{ PULSEAUDIO_BACKEND_NAME, pulseaudio_backend_init },
#endif
#if JACK
{ JACK_BACKEND_NAME, jack_backend_init },
#endif
{ PIPE_BACKEND_NAME, pipe_backend_init },
{ FILE_BACKEND_NAME, file_backend_init }
};
int audio_backend_get_by_name(char const* name, audio_backend_handle_t* backend)
{
size_t index;
if (name[0] == '\0')
{
logger_log(LOG_INFO, "%s: taking default backend %s", __func__, backend_list[0].name);
return backend_list[0].init_function(backend);
}
for (index = 0; index != sizeof(backend_list) / sizeof(struct backend_list_item_t); ++index)
{
if (!strcmp(name, backend_list[index].name))
{
logger_log(LOG_INFO, "%s: found backend %s", __func__, name);
return backend_list[index].init_function(backend);
}
}
logger_log(LOG_ERROR, "%s: no backend found with name %s", __func__, name);
return -EINVAL;
}
char const* audio_backend_get_help()
{
static char help_text[HELP_TEXT_LEN];
size_t index = 0;
size_t offset = 0;
offset += snprintf(help_text + offset, HELP_TEXT_LEN - offset, "Available audio backends are: ");
for (index = 0; index < sizeof(backend_list) / sizeof(struct backend_list_item_t); ++index)
{
offset += snprintf(help_text + offset, HELP_TEXT_LEN - offset, "%s ", backend_list[index].name);
}
offset += snprintf(help_text + offset, HELP_TEXT_LEN - offset, ". default is %s.", backend_list[0].name);
return help_text;
}

View File

@ -0,0 +1,36 @@
#ifndef __AUDIO_BACKEND_H__
#define __AUDIO_BACKEND_H__
#include <stdlib.h>
#include "../../vban/vban.h"
#include "../../common/audio.h"
struct audio_backend_t;
typedef struct audio_backend_t* audio_backend_handle_t;
typedef int (*audio_backend_init_f) (audio_backend_handle_t* handle);
typedef int (*audio_backend_open_f) (audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config);
typedef int (*audio_backend_close_f) (audio_backend_handle_t handle);
typedef int (*audio_backend_write_f) (audio_backend_handle_t handle, char const* data, size_t size);
typedef int (*audio_backend_read_f) (audio_backend_handle_t handle, char* data, size_t size);
struct audio_backend_t
{
audio_backend_open_f open;
audio_backend_close_f close;
audio_backend_write_f write;
audio_backend_read_f read;
};
enum autoconnect
{
NO = 0,
YES,
CARD
};
int audio_backend_get_by_name(char const* name, audio_backend_handle_t* backend);
char const* audio_backend_get_help();
#endif /*__AUDIO_BACKEND_H__*/

View File

@ -0,0 +1,159 @@
/*
* This file is part of vban.
* Copyright (c) 2015 by Benoît Quiniou <quiniouben@yahoo.fr>
* Copyright (c) 2017 by Markus Buschhoff <markus.buschhoff@tu-dortmund.de>
*
* vban 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.
*
* vban 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 vban. If not, see <http://www.gnu.org/licenses/>.
*/
#include "file_backend.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "../../common/logger.h"
struct file_backend_t
{
struct audio_backend_t parent;
int fd;
};
static int file_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config);
static int file_close(audio_backend_handle_t handle);
static int file_write(audio_backend_handle_t handle, char const* data, size_t size);
static int file_read(audio_backend_handle_t handle, char* data, size_t size);
int file_backend_init(audio_backend_handle_t* handle)
{
struct file_backend_t* file_backend = 0;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: null handle pointer", __func__);
return -EINVAL;
}
file_backend = calloc(1, sizeof(struct file_backend_t));
if (file_backend == 0)
{
logger_log(LOG_FATAL, "%s: could not allocate memory", __func__);
return -ENOMEM;
}
file_backend->parent.open = file_open;
file_backend->parent.close = file_close;
file_backend->parent.write = file_write;
file_backend->parent.read = file_read;
*handle = (audio_backend_handle_t)file_backend;
return 0;
}
int file_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config)
{
struct file_backend_t* const file_backend = (struct file_backend_t*)handle;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
if(strcmp("", output_name))
file_backend->fd = open(output_name, (direction == AUDIO_OUT) ? (O_CREAT|O_WRONLY|O_TRUNC) : O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
else
file_backend->fd = STDOUT_FILENO;
if (file_backend->fd == -1)
{
logger_log(LOG_FATAL, "%s: open error", __func__); //
perror("open");
return -errno;
}
return 0;
}
int file_close(audio_backend_handle_t handle)
{
int ret = 0;
struct file_backend_t* const file_backend = (struct file_backend_t*)handle;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
if (file_backend== 0)
{
/** nothing to do */
return 0;
}
if (file_backend->fd != STDOUT_FILENO)
ret = close(file_backend->fd);
return ret;
}
int file_write(audio_backend_handle_t handle, char const* data, size_t size)
{
int ret = 0;
struct file_backend_t* const file_backend = (struct file_backend_t*)handle;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
ret = write(file_backend->fd, (const void *)data, size);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s:", __func__);
perror("write");
}
return ret;
}
int file_read(audio_backend_handle_t handle, char* data, size_t size)
{
int ret = 0;
struct file_backend_t* const file_backend = (struct file_backend_t*)handle;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
ret = read(file_backend->fd, (void *)data, size);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s:", __func__);
perror("write");
}
return ret;
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of vban.
* Copyright (c) 2015 by Benoît Quiniou <quiniouben@yahoo.fr>
* Copyright (c) 2017 by Markus Buschhoff <markus.buschhoff@tu-dortmund.de>
*
* vban 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.
*
* vban 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 vban. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FILE_BACKEND_H__
#define __FILE_BACKEND_H__
#include "audio_backend.h"
#define FILE_BACKEND_NAME "file"
int file_backend_init(audio_backend_handle_t* handle);
#endif /*__FILE_BACKEND_H__*/

View File

@ -0,0 +1,697 @@
#include "jack_backend.h"
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include <errno.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
//#include <pthread.h>
#include "../logger.h"
#define NB_BUFFERS 2
struct jack_backend_t
{
struct audio_backend_t parent;
jack_client_t* jack_client;
jack_port_t* ports[VBAN_CHANNELS_MAX_NB];
jack_ringbuffer_t* ring_buffer;
size_t buffer_size;
enum VBanBitResolution bit_fmt;
unsigned int nb_channels;
unsigned char map[VBAN_CHANNELS_MAX_NB];
enum audio_direction direction;
uint32_t autoconnect;
int active;
};
long overruns = 0;
static int jack_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config);
static int jack_close(audio_backend_handle_t handle);
static int jack_write(audio_backend_handle_t handle, char const* data, size_t nb_sample);
static int jack_read(audio_backend_handle_t handle, char* data, size_t nb_sample);
static int jack_process_cb_tx(jack_nframes_t nframes, void* arg);
static int jack_process_cb_rx(jack_nframes_t nframes, void* arg);
static void jack_shutdown_cb(void* arg);
static jack_default_audio_sample_t jack_convert_sample_rx(char const* ptr, enum VBanBitResolution bit_fmt);
static void jack_convert_sample_tx(char* ptr, jack_default_audio_sample_t sample, enum VBanBitResolution bit_fmt);
pthread_mutex_t tx_read_thread_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
static int jack_start(struct jack_backend_t* jack_backend)
{
int ret = 0;
size_t port;
char port_name[32];
char port_name_template[9];
char const** ports;
volatile size_t port_id;
volatile size_t pports = 0;
uint8_t JackFlags;
memset(port_name_template, 0, 9);
switch (jack_backend->direction)
{
case AUDIO_IN:
if (jack_backend->autoconnect == CARD)
{
strcpy(port_name_template, "playback_%u");
JackFlags = JackPortIsInput + JackPortIsPhysical;
}
else
{
strcpy(port_name_template, "input_%u");
JackFlags = JackPortIsInput;
}
for (port = 0; port != jack_backend->nb_channels; ++port)
{
snprintf(port_name, sizeof(port_name)-1, port_name_template, (unsigned int)(port+1));
jack_backend->ports[port] = jack_port_register(jack_backend->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackFlags, 0);
if (jack_backend->ports[port] == 0)
{
logger_log(LOG_ERROR, "%s: impossible to set jack port for channel %d", __func__, port);
jack_close((audio_backend_handle_t)jack_backend);
return -ENODEV;
}
}
break;
default: // AUDIO_OUT:
if (jack_backend->autoconnect == CARD)
{
strcpy(port_name_template, "capture_%u");
JackFlags = JackPortIsOutput + JackPortIsPhysical;
}
else
{
strcpy(port_name_template, "output_%u");
JackFlags = JackPortIsOutput;
}
for (port = 0; port != jack_backend->nb_channels; ++port)
{
snprintf(port_name, sizeof(port_name)-1, port_name_template, (unsigned int)(port+1));
jack_backend->ports[port] = jack_port_register(jack_backend->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackFlags, 0);
if (jack_backend->ports[port] == 0)
{
logger_log(LOG_ERROR, "%s: impossible to set jack port for channel %d", __func__, port);
jack_close((audio_backend_handle_t)jack_backend);
return -ENODEV;
}
}
break;
}
ret = jack_activate(jack_backend->jack_client);
if (ret)
{
logger_log(LOG_ERROR, "%s: can't activate client", __func__);
return ret;
}
if (jack_backend->autoconnect == YES)
{
switch (jack_backend->direction)
{
case AUDIO_IN:
ports = jack_get_ports(jack_backend->jack_client, 0, 0, JackPortIsPhysical|JackPortIsOutput);
if (ports != 0)
{
for (port_id=0; port_id<1024; port_id++)
{
if (ports[port_id]==0) break;
if (strstr(ports[port_id], "midi")==NULL) pports++;
}
for (port_id=0; port_id<jack_backend->nb_channels; port_id++)
{
if (jack_backend->map[port_id]<=pports)
{
if (ports[jack_backend->map[port_id]]!=NULL) if (strstr(ports[jack_backend->map[port_id]], "midi")==NULL)
{
ret = jack_connect(jack_backend->jack_client, ports[jack_backend->map[port_id]], jack_port_name(jack_backend->ports[port_id]));
if (ret)
{
logger_log(LOG_WARNING, "%s: could not autoconnect channel %d", __func__, port_id);
}
else
{
logger_log(LOG_DEBUG, "%s: channel %d autoconnected", __func__, port_id);
}
}
}
}
jack_free(ports);
}
else
{
logger_log(LOG_WARNING, "%s: could not autoconnect channels", __func__);
}
break;
default: // AUDIO_OUT
ports = jack_get_ports(jack_backend->jack_client, 0, 0, JackPortIsPhysical|JackPortIsInput);
if (ports != 0)
{
for (port_id=0; port_id<1024; port_id++)
{
if (ports[port_id]==0) break;
if (strstr(ports[port_id], "midi")==NULL) pports++;
}
for (port_id=0; port_id<jack_backend->nb_channels; port_id++)
{
if (jack_backend->map[port_id]<=pports)
{
if (strstr(ports[jack_backend->map[port_id]], "midi")==NULL)
{
ret = jack_connect(jack_backend->jack_client, jack_port_name(jack_backend->ports[port_id]), ports[jack_backend->map[port_id]]);
if (ret)
{
logger_log(LOG_WARNING, "%s: could not autoconnect channel %d", __func__, port_id);
}
else
{
logger_log(LOG_DEBUG, "%s: channel %d autoconnected", __func__, port_id);
}
}
}
}
jack_free(ports);
}
else
{
logger_log(LOG_WARNING, "%s: could not autoconnect channels", __func__);
}
break;
}//*/
}
logger_log(LOG_DEBUG, "%s: jack activated", __func__);
return ret;
}
int jack_backend_init(audio_backend_handle_t* handle)
{
struct jack_backend_t* jack_backend = 0;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: null handle pointer", __func__);
return -EINVAL;
}
jack_backend = calloc(1, sizeof(struct jack_backend_t));
if (jack_backend == 0)
{
logger_log(LOG_FATAL, "%s: could not allocate memory", __func__);
return -ENOMEM;
}
jack_backend->parent.open = jack_open;
jack_backend->parent.close = jack_close;
jack_backend->parent.write = jack_write;
jack_backend->parent.read = jack_read;
*handle = (audio_backend_handle_t)jack_backend;
return 0;
}
int jack_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config)
{
int ret;
uint32_t jbs, res;
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
volatile jack_nframes_t jack_buffer_size;
jack_backend->direction = direction;
jack_backend->autoconnect = config->autoconnect;
jack_backend->nb_channels = config->nb_channels;
jack_backend->bit_fmt = config->bit_fmt;
memcpy(jack_backend->map, config->map, VBAN_CHANNELS_MAX_NB);
char name[24];
memset(name, 0, 16);
switch (jack_backend->direction)
{
case AUDIO_IN:
strncpy(name, "VBAN TX ", 8);
break;
default: // AUDIO_OUT
strncpy(name, "VBAN RX ", 8);
break;
}
strcat(name, config->streamname);
logger_log(LOG_DEBUG, "%s", __func__);
if (handle == 0)
{
logger_log(LOG_ERROR, "%s: handle is null", __func__);
return -EINVAL;
}
if (jack_backend->jack_client == 0)
{
//jack_backend->jack_client = jack_client_open((output_name[0] == '\0') ? name : output_name, 0, 0);
jack_backend->jack_client = jack_client_open(name, JackNullOption, NULL);
if (jack_backend->jack_client == 0)
{
logger_log(LOG_ERROR, "%s: could not open jack client", __func__);
return -ENODEV;
}
}
jack_buffer_size = jack_get_buffer_size(jack_backend->jack_client) * jack_backend->nb_channels * VBanBitResolutionSize[config->bit_fmt];
buffer_size = ((buffer_size > jack_buffer_size) ? buffer_size : jack_buffer_size) * NB_BUFFERS;
jack_backend->ring_buffer = jack_ringbuffer_create(buffer_size);
/*char* const zeros = calloc(1, buffer_size / NB_BUFFERS);
jack_ringbuffer_write(jack_backend->ring_buffer, zeros, buffer_size / NB_BUFFERS);
free(zeros);//*/
switch (jack_backend->direction)
{
case AUDIO_IN:
ret = jack_set_process_callback(jack_backend->jack_client, jack_process_cb_tx, jack_backend);
break;
default: // AUDIO_OUT
ret = jack_set_process_callback(jack_backend->jack_client, jack_process_cb_rx, jack_backend);
break;
}
if (ret)
{
logger_log(LOG_ERROR, "%s: impossible to set jack process callback", __func__);
jack_close(handle);
return ret;
}
jack_on_shutdown(jack_backend->jack_client, jack_shutdown_cb, jack_backend);
ret = jack_start(jack_backend);
return ret;
}
int jack_close(audio_backend_handle_t handle)
{
int ret = 0;
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
if (jack_backend->jack_client == 0)
{
/** nothing to do */
return 0;
}
jack_backend->active = 0;
ret = jack_deactivate(jack_backend->jack_client);
if (ret)
{
logger_log(LOG_FATAL, "%s: jack_deactivate failed with error %d", __func__);
}
ret = jack_client_close(jack_backend->jack_client);
if (ret)
{
logger_log(LOG_FATAL, "%s: jack_client_close failed with error %d", __func__);
}
if (jack_backend->ring_buffer != 0)
{
jack_ringbuffer_free(jack_backend->ring_buffer);
}
jack_backend->jack_client = 0;
memset(jack_backend->ports, 0, VBAN_CHANNELS_MAX_NB * sizeof(jack_port_t*));
return ret;
}
int jack_write(audio_backend_handle_t handle, char const* data, size_t size)
{
int ret = 0;
size_t available;
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
logger_log(LOG_DEBUG, "%s", __func__);
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
if (jack_backend->jack_client == 0)
{
logger_log(LOG_ERROR, "%s: device not open", __func__);
return -ENODEV;
}
if (jack_backend->active == 0)
{
logger_log(LOG_DEBUG, "%s: server not active yet", __func__);
return size;
}
if (jack_ringbuffer_write_space(jack_backend->ring_buffer) < size)
{
logger_log(LOG_WARNING, "%s: short write", __func__);
return 0;
}
//available = jack_ringbuffer_write_space(jack_backend->ring_buffer);
//if (available>size) available = size;
//else available = (available/(VBanBitResolutionSize[jack_backend->bit_fmt]*jack_backend->nb_channels))*VBanBitResolutionSize[jack_backend->bit_fmt]*jack_backend->nb_channels;
//jack_ringbuffer_write(jack_backend->ring_buffer, data, available);
while (jack_ringbuffer_write_space(jack_backend->ring_buffer)<size);
jack_ringbuffer_write(jack_backend->ring_buffer, data, size);
return (ret < 0) ? ret : size;
}
static int jack_read(audio_backend_handle_t handle, char* data, size_t size)
{
int ret = 0;
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
volatile size_t jsize;
//size_t bufSize = jack_backend->nb_channels * ;
logger_log(LOG_DEBUG, "%s", __func__);
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
if (jack_backend->jack_client == 0)
{
logger_log(LOG_ERROR, "%s: device not open", __func__);
return -ENODEV;
}
if (jack_backend->active == 0)
{
logger_log(LOG_DEBUG, "%s: server not active yet", __func__);
return size;
}
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
pthread_mutex_lock (&tx_read_thread_lock);
while (jack_ringbuffer_read_space(jack_backend->ring_buffer)<size) pthread_cond_wait(&data_ready, &tx_read_thread_lock);
pthread_mutex_unlock(&tx_read_thread_lock);
jack_ringbuffer_read(jack_backend->ring_buffer, data, size);
return (ret < 0) ? ret : size;
}
int jack_process_cb_tx(jack_nframes_t nframes, void* arg)
{
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)arg;
size_t channel;
size_t sample;
jack_ringbuffer_data_t rb_data[2];
static jack_default_audio_sample_t* buffers[VBAN_CHANNELS_MAX_NB];
/* char* ptr;
size_t len;
size_t index = 0;
size_t part;*/
size_t sampleSize;
size_t bufSize; // size of buffer for ALL channels
char sampleParts[8];
if (arg == 0)
{
logger_log(LOG_ERROR, "%s: handle pointer is null", __func__);
return -EINVAL;
}
logger_log(LOG_DEBUG, "%s", __func__);
sampleSize = VBanBitResolutionSize[jack_backend->bit_fmt];
bufSize = (nframes * jack_backend->nb_channels * sampleSize);
jack_backend->active = 1;
for (channel = 0; channel < jack_backend->nb_channels; channel++)
{
buffers[channel] = (jack_default_audio_sample_t*)jack_port_get_buffer(jack_backend->ports[channel], nframes);
}
jack_ringbuffer_get_write_vector(jack_backend->ring_buffer, rb_data);
if ((rb_data[0].len + rb_data[1].len) < bufSize)
{
logger_log(LOG_WARNING, "%s: short read", __func__);
//return 0;
}
// Attempt to mirror the callback process from the receiver's one.
// Unfinished. Here's segfaults.
/*ptr = rb_data[0].buf;
len = 0;
for (sample = 0; sample != nframes; ++sample)
{
for (channel = 0; channel != jack_backend->nb_channels; ++channel)
{
if (index == 0)
{
if ((len + sampleSize) > rb_data[0].len)
{
jack_convert_sample_tx((char*)sampleParts, ((jack_default_audio_sample_t*)buffers[channel])[sample], jack_backend->bit_fmt);
part = 0;
while (ptr != (rb_data[0].buf + rb_data[0].len))
{
*(ptr) = sampleParts[part];
ptr++;
part++;
}
ptr = rb_data[1].buf;
for (; part<VBanBitResolutionSize[jack_backend->bit_fmt]; part++, ptr++)
{
*ptr = sampleParts[part];
}
len += sampleSize;
index = 1;
continue;
}
if (len == rb_data[0].len)
{
ptr = rb_data[1].buf;
index = 1;
}
}
jack_convert_sample_tx((char*)sampleParts, ((jack_default_audio_sample_t*)buffers[channel])[sample], jack_backend->bit_fmt);
ptr += sampleSize;
len += sampleSize;
}
}
jack_ringbuffer_read_advance(jack_backend->ring_buffer, len);//*/
// Realisation of TX callback with the official example
for (sample = 0; sample < nframes; sample++)
{
if (!(jack_ringbuffer_write_space(jack_backend->ring_buffer)<(sampleSize*jack_backend->nb_channels)))
{
for (channel = 0; channel < jack_backend->nb_channels; channel++)
{
jack_convert_sample_tx(sampleParts, (((jack_default_audio_sample_t*)buffers[channel])[sample]), jack_backend->bit_fmt);
if (jack_ringbuffer_write(jack_backend->ring_buffer, sampleParts, sampleSize) < sampleSize) overruns++;
}
}
}
if (pthread_mutex_trylock(&tx_read_thread_lock)==0)
{
pthread_cond_signal(&data_ready);
pthread_mutex_unlock(&tx_read_thread_lock);
}
return 0;
}
int jack_process_cb_rx(jack_nframes_t nframes, void* arg)
{
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)arg;
size_t channel;
size_t sample;
jack_ringbuffer_data_t rb_data[2];
static jack_default_audio_sample_t* buffers[VBAN_CHANNELS_MAX_NB];
char const* ptr;
size_t len;
size_t sampleSize;
size_t index = 0;
size_t part;
char sampleParts[8];
if (arg == 0)
{
logger_log(LOG_ERROR, "%s: handle pointer is null", __func__);
return -EINVAL;
}
logger_log(LOG_DEBUG, "%s", __func__);
sampleSize = VBanBitResolutionSize[jack_backend->bit_fmt];
jack_backend->active = 1;
for (channel = 0; channel != jack_backend->nb_channels; ++channel)
{
buffers[channel] = (jack_default_audio_sample_t*)jack_port_get_buffer(jack_backend->ports[channel], nframes);
}
jack_ringbuffer_get_read_vector(jack_backend->ring_buffer, rb_data);
if ((rb_data[0].len + rb_data[1].len) < (nframes * jack_backend->nb_channels * sampleSize))
{
logger_log(LOG_WARNING, "%s: short read", __func__);
return 0;
}
ptr = rb_data[0].buf;
len = 0;
for (sample = 0; sample != nframes; ++sample)
{
for (channel = 0; channel != jack_backend->nb_channels; ++channel)
{
if (index == 0)
{
if ((len + sampleSize) > rb_data[0].len)
{
part = 0;
while (ptr != (rb_data[0].buf + rb_data[0].len))
{
sampleParts[part++] = *(ptr++);
}
ptr = rb_data[1].buf;
for (; part < VBanBitResolutionSize[jack_backend->bit_fmt]; ++part, ++ptr)
{
sampleParts[part] = *ptr;
}
buffers[channel][sample] = jack_convert_sample_rx((char const*)sampleParts, jack_backend->bit_fmt);
len += sampleSize;
index = 1;
continue;
}
if (len == rb_data[0].len)
{
ptr = rb_data[1].buf;
index = 1;
}
}
buffers[channel][sample] = jack_convert_sample_rx(ptr, jack_backend->bit_fmt);
ptr += sampleSize;
len += sampleSize;
}
}
jack_ringbuffer_read_advance(jack_backend->ring_buffer, len);
return 0;
}
void jack_shutdown_cb(void* arg)
{
audio_backend_handle_t const jack_backend = (audio_backend_handle_t)arg;
if (arg == 0)
{
logger_log(LOG_ERROR, "%s: handle pointer is null");
return ;
}
jack_close(jack_backend);
/*XXX how to notify upper layer that we are done ?*/
}
jack_default_audio_sample_t jack_convert_sample_rx(char const* ptr, enum VBanBitResolution bit_fmt)
{
int value;
switch (bit_fmt)
{
case VBAN_BITFMT_8_INT:
return (float)(*((int8_t const*)ptr)) / (float)(1 << 7);
case VBAN_BITFMT_16_INT:
return (float)(*((int16_t const*)ptr)) / (float)(1 << 15);
case VBAN_BITFMT_24_INT:
value = (ptr[2] << 16) | ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
return (float)value / (float)(1 << 23);
case VBAN_BITFMT_32_INT:
return (float)*((int32_t const*)ptr) / (float)(1 << 31);
case VBAN_BITFMT_32_FLOAT:
return *(float const*)ptr;
case VBAN_BITFMT_64_FLOAT:
default:
return 0.0;
}
}
static void jack_convert_sample_tx(char* ptr, jack_default_audio_sample_t sample, enum VBanBitResolution bit_fmt)
{
int32_t tmp = 0;
uint32_t* tp;
switch (bit_fmt)
{
case VBAN_BITFMT_8_INT:
tmp = (int8_t)((float)(1<<7)*sample);//roundf(127.0f*sample);
ptr[0] = tmp&0xFF;
break;
case VBAN_BITFMT_16_INT:
tmp = (int16_t)((float)(1<<15)*sample);//roundf(127.0f*sample);
ptr[0] = tmp&0xFF;
ptr[1] = (tmp>>8)&0xFF;
break;
case VBAN_BITFMT_24_INT:
tmp = (int32_t)((float)(1<<23)*sample);//roundf(127.0f*sample);
ptr[0] = tmp&0xFF;
ptr[1] = (tmp>>8)&0xFF;
ptr[2] = (tmp>>16)&0xFF;
break;
case VBAN_BITFMT_32_INT:
ptr[0] = tmp&0xFF;
ptr[1] = (tmp>>8)&0xFF;
ptr[2] = (tmp>>16)&0xFF;
ptr[3] = (tmp>>24)&0xFF;
break;
case VBAN_BITFMT_32_FLOAT:
tp = (uint32_t*)(&sample);
tmp = *tp;
ptr[0] = tmp&0xFF;
ptr[1] = (tmp>>8)&0xFF;
ptr[2] = (tmp>>16)&0xFF;
ptr[3] = (tmp>>24)&0xFF;
break;
case VBAN_BITFMT_64_FLOAT:
default:
break;
}
}

View File

@ -0,0 +1,11 @@
#ifndef __JACK_BACKEND_H__
#define __JACK_BACKEND_H__
#include "audio_backend.h"
#define JACK_BACKEND_NAME "jack"
int jack_backend_init(audio_backend_handle_t* handle);
#endif /*__JACK_BACKEND_H__*/

View File

@ -0,0 +1,173 @@
#include "pipe_backend.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "../../common/logger.h"
#ifndef _WIN32
#define FIFO_FILENAME "/tmp/vban_0"
#else
// name of named pipe is \\.\pipe\vban_0
#include <windef.h> // for DWORD and others
#include <winbase.h>
#define FIFO_FILENAME "\\\\.\\pipe\\vban_0"
#endif
struct pipe_backend_t
{
struct audio_backend_t parent;
int fd;
};
static int pipe_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config);
static int pipe_close(audio_backend_handle_t handle);
static int pipe_write(audio_backend_handle_t handle, char const* data, size_t size);
static int pipe_read(audio_backend_handle_t handle, char* data, size_t size);
int pipe_backend_init(audio_backend_handle_t* handle)
{
struct pipe_backend_t* pipe_backend = 0;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: null handle pointer", __func__);
return -EINVAL;
}
pipe_backend = calloc(1, sizeof(struct pipe_backend_t));
if (pipe_backend == 0)
{
logger_log(LOG_FATAL, "%s: could not allocate memory", __func__);
return -ENOMEM;
}
pipe_backend->parent.open = pipe_open;
pipe_backend->parent.close = pipe_close;
pipe_backend->parent.write = pipe_write;
pipe_backend->parent.read = pipe_read;
*handle = (audio_backend_handle_t)pipe_backend;
return 0;
}
int pipe_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config)
{
int ret;
struct pipe_backend_t* const pipe_backend = (struct pipe_backend_t*)handle;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
#ifndef _WIN32
ret = mkfifo(FIFO_FILENAME, 0666);
if (ret < 0)
{
logger_log(LOG_FATAL, "%s: ???", __func__);
//todo:strerror
perror("mknod");
pipe_backend->fd = 0;
return ret;
}
#else
// Windows has no mkfifo function. Use named pipes instead
HANDLE named_pipe = CreateNamedPipeA(
/* lpName */ FIFO_FILENAME,
/* dwOpenMode */ PIPE_ACCESS_DUPLEX,
/* dwPipeMode */ PIPE_TYPE_MESSAGE, // or maybe PIPE_TYPE_BYTE, let's see
/* nMaxInstances */ 1,
/* nOutBufferSize */ 0,
/* nInBufferSize */ 0,
/* nDefaultTimeOut */ 0,
/* lpSecurityAttributes */ 0);
if (named_pipe == INVALID_HANDLE_VALUE)
{
logger_log(LOG_FATAL, "%s: ???", __func__);
//todo:strerror
perror("mknod");
pipe_backend->fd = 0;
ret = GetLastError();
return ret;
}
#endif // _WIN32
pipe_backend->fd = open((output_name[0] == 0) ? FIFO_FILENAME : output_name, (direction == AUDIO_OUT) ? O_WRONLY : O_RDONLY);
if (pipe_backend->fd == -1)
{
logger_log(LOG_FATAL, "%s: open error", __func__); //
perror("open");
return ret;
}
return 0;
}
int pipe_close(audio_backend_handle_t handle)
{
int ret = 0;
struct pipe_backend_t* const pipe_backend = (struct pipe_backend_t*)handle;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
if (pipe_backend== 0)
{
/** nothing to do */
return 0;
}
ret = close(pipe_backend->fd);
unlink(FIFO_FILENAME);
return ret;
}
int pipe_write(audio_backend_handle_t handle, char const* data, size_t size)
{
int ret = 0;
struct pipe_backend_t* const pipe_backend = (struct pipe_backend_t*)handle;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
ret = write(pipe_backend->fd, (const void *)data, size);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s:", __func__);
perror("write");
}
return ret;
}
int pipe_read(audio_backend_handle_t handle, char* data, size_t size)
{
int ret = 0;
struct pipe_backend_t* const pipe_backend = (struct pipe_backend_t*)handle;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
ret = read(pipe_backend->fd, (void *)data, size);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s:", __func__);
perror("read");
}
return ret;
}

View File

@ -0,0 +1,10 @@
#ifndef __PIPE_BACKEND_H__
#define __PIPE_BACKEND_H__
#include "audio_backend.h"
#define PIPE_BACKEND_NAME "pipe"
int pipe_backend_init(audio_backend_handle_t* handle);
#endif /*__PIPE_BACKEND_H__*/

View File

@ -0,0 +1,186 @@
#include "pulseaudio_backend.h"
#include <pulse/simple.h>
#include <pulse/error.h>
#include <errno.h>
#include "../../common/logger.h"
struct pulseaudio_backend_t
{
struct audio_backend_t parent;
pa_simple* pulseaudio_handle;
};
static int pulseaudio_open(audio_backend_handle_t handle, char const* device_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config);
static int pulseaudio_close(audio_backend_handle_t handle);
static int pulseaudio_write(audio_backend_handle_t handle, char const* data, size_t size);
static int pulseaudio_read(audio_backend_handle_t handle, char* data, size_t size);
static enum pa_sample_format vban_to_pulseaudio_format(enum VBanBitResolution bit_resolution)
{
switch (bit_resolution)
{
case VBAN_BITFMT_8_INT:
return PA_SAMPLE_U8;
case VBAN_BITFMT_16_INT:
return PA_SAMPLE_S16LE;
case VBAN_BITFMT_24_INT:
return PA_SAMPLE_S24LE;
case VBAN_BITFMT_32_INT:
return PA_SAMPLE_S32LE;
case VBAN_BITFMT_32_FLOAT:
return PA_SAMPLE_FLOAT32LE;
case VBAN_BITFMT_64_FLOAT:
default:
return PA_SAMPLE_INVALID;
}
}
int pulseaudio_backend_init(audio_backend_handle_t* handle)
{
struct pulseaudio_backend_t* pulseaudio_backend = 0;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: null handle pointer", __func__);
return -EINVAL;
}
pulseaudio_backend = calloc(1, sizeof(struct pulseaudio_backend_t));
if (pulseaudio_backend == 0)
{
logger_log(LOG_FATAL, "%s: could not allocate memory", __func__);
return -ENOMEM;
}
pulseaudio_backend->parent.open = pulseaudio_open;
pulseaudio_backend->parent.close = pulseaudio_close;
pulseaudio_backend->parent.write = pulseaudio_write;
pulseaudio_backend->parent.read = pulseaudio_read;
*handle = (audio_backend_handle_t)pulseaudio_backend;
return 0;
}
int pulseaudio_open(audio_backend_handle_t handle, char const* device_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config)
{
int ret;
struct pulseaudio_backend_t* const pulseaudio_backend = (struct pulseaudio_backend_t*)handle;
pa_sample_spec const ss =
{
.format = vban_to_pulseaudio_format(config->bit_fmt),
.rate = config->sample_rate,
.channels = config->nb_channels,
};
pa_buffer_attr const ba =
{
.maxlength = (unsigned int)(-1),
.tlength = buffer_size,
.prebuf = buffer_size / 2,
.minreq = (unsigned int)(-1),
.fragsize = (unsigned int)(-1)
};
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
/* pulseaudio_backend->pulseaudio_handle = pa_simple_new(0, "vban", (direction == AUDIO_OUT) ? PA_STREAM_PLAYBACK : PA_STREAM_RECORD, (device_name[0] == '\0') ? 0 : device_name,
(direction == AUDIO_OUT) ? "playback": "record", &ss, 0, (direction == AUDIO_OUT) ? &ba : 0, &ret);//*/
pulseaudio_backend->pulseaudio_handle = pa_simple_new(0, "vban", (direction == AUDIO_OUT) ? PA_STREAM_PLAYBACK : PA_STREAM_RECORD, (device_name[0] == '\0') ? 0 : device_name,
(direction == AUDIO_OUT) ? "playback": "record", &ss, 0, (direction == AUDIO_OUT) ? &ba : 0, &ret);
if (pulseaudio_backend->pulseaudio_handle == 0)
{
logger_log(LOG_FATAL, "pulseaudio_open: open error: %s", pa_strerror(ret));
return ret;
}
return 0;
}
int pulseaudio_close(audio_backend_handle_t handle)
{
int ret = 0;
struct pulseaudio_backend_t* const pulseaudio_backend = (struct pulseaudio_backend_t*)handle;
if (handle == 0)
{
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
return -EINVAL;
}
if (pulseaudio_backend->pulseaudio_handle == 0)
{
/** nothing to do */
return 0;
}
pa_simple_free(pulseaudio_backend->pulseaudio_handle);
pulseaudio_backend->pulseaudio_handle = 0;
return ret;
}
int pulseaudio_write(audio_backend_handle_t handle, char const* data, size_t size)
{
int ret = 0;
int error;
struct pulseaudio_backend_t* const pulseaudio_backend = (struct pulseaudio_backend_t*)handle;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
if (pulseaudio_backend->pulseaudio_handle == 0)
{
logger_log(LOG_ERROR, "%s: device not open", __func__);
return -ENODEV;
}
ret = pa_simple_write(pulseaudio_backend->pulseaudio_handle, data, size, &error);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: pa_simple_write failed: %s", __func__, pa_strerror(error));
}
return (ret < 0) ? ret : size;
}
int pulseaudio_read(audio_backend_handle_t handle, char* data, size_t size)
{
int ret = 0;
int error;
struct pulseaudio_backend_t* const pulseaudio_backend = (struct pulseaudio_backend_t*)handle;
if ((handle == 0) || (data == 0))
{
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
return -EINVAL;
}
if (pulseaudio_backend->pulseaudio_handle == 0)
{
logger_log(LOG_ERROR, "%s: device not open", __func__);
return -ENODEV;
}
ret = pa_simple_read(pulseaudio_backend->pulseaudio_handle, data, size, &error);
if (ret < 0)
{
logger_log(LOG_ERROR, "%s: pa_simple_read failed: %s", __func__, pa_strerror(error));
}
return (ret < 0) ? ret : size;
}

View File

@ -0,0 +1,10 @@
#ifndef __PULSEAUDIO_BACKEND_H__
#define __PULSEAUDIO_BACKEND_H__
#include "audio_backend.h"
#define PULSEAUDIO_BACKEND_NAME "pulseaudio"
int pulseaudio_backend_init(audio_backend_handle_t* handle);
#endif /*__PULSEAUDIO_BACKEND_H__*/