Add files via upload
This commit is contained in:
parent
68ccaf20c1
commit
1f345af128
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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__*/
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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__*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__*/
|
||||
|
Loading…
Reference in New Issue