Binaural synthesis (confbridge): Adds binaural synthesis to bridge_softmix.
Adds binaural synthesis to bridge_softmix (via convolution using libfftw3). Binaural synthesis is conducted at 48kHz. For a conference, only one spatial representation is rendered. The default rendering is applied for mono-capable channels. ASTERISK-26292 Change-Id: Iecdb381b6adc17c961049658678f6219adae1ddf
This commit is contained in:
parent
9ad1df71b3
commit
094c26aa68
|
@ -1475,6 +1475,7 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
|
|||
ast_bridge_set_internal_sample_rate(conference->bridge, conference->b_profile.internal_sample_rate);
|
||||
/* Set the internal mixing interval on the bridge from the bridge profile */
|
||||
ast_bridge_set_mixing_interval(conference->bridge, conference->b_profile.mix_interval);
|
||||
ast_bridge_set_binaural_active(conference->bridge, ast_test_flag(&conference->b_profile, BRIDGE_OPT_BINAURAL_ACTIVE));
|
||||
|
||||
if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
|
||||
ast_bridge_set_talker_src_video_mode(conference->bridge);
|
||||
|
|
|
@ -14,7 +14,15 @@
|
|||
MODULE_PREFIX=bridge
|
||||
MENUSELECT_CATEGORY=BRIDGES
|
||||
MENUSELECT_DESCRIPTION=Bridging Modules
|
||||
MODULE_EXCLUDE=binaural_rendering_in_bridge_softmix
|
||||
|
||||
all: _all
|
||||
|
||||
ifeq ($(findstring binaural_rendering_in_bridge_softmix,$(MENUSELECT_BRIDGES)),)
|
||||
bridge_softmix.o bridge_softmix/bridge_softmix_binaural.o: _ASTCFLAGS+=-DBINAURAL_RENDERING
|
||||
bridge_softmix.so: LIBS+=$(FFTW3_LIB)
|
||||
endif
|
||||
|
||||
include $(ASTTOPDIR)/Makefile.moddir_rules
|
||||
|
||||
$(call MOD_ADD_C,bridge_softmix,$(wildcard bridge_softmix/*.c))
|
||||
|
|
|
@ -31,29 +31,8 @@
|
|||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/bridge.h"
|
||||
#include "asterisk/bridge_technology.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/slinfactory.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/timing.h"
|
||||
#include "asterisk/translate.h"
|
||||
|
||||
#define MAX_DATALEN 8096
|
||||
#include "bridge_softmix/include/bridge_softmix_internal.h"
|
||||
|
||||
/*! The minimum sample rate of the bridge. */
|
||||
#define SOFTMIX_MIN_SAMPLE_RATE 8000 /* 8 kHz sample rate */
|
||||
|
@ -75,71 +54,6 @@
|
|||
#define DEFAULT_SOFTMIX_SILENCE_THRESHOLD 2500
|
||||
#define DEFAULT_SOFTMIX_TALKING_THRESHOLD 160
|
||||
|
||||
#define DEFAULT_ENERGY_HISTORY_LEN 150
|
||||
|
||||
struct video_follow_talker_data {
|
||||
/*! audio energy history */
|
||||
int energy_history[DEFAULT_ENERGY_HISTORY_LEN];
|
||||
/*! The current slot being used in the history buffer, this
|
||||
* increments and wraps around */
|
||||
int energy_history_cur_slot;
|
||||
/*! The current energy sum used for averages. */
|
||||
int energy_accum;
|
||||
/*! The current energy average */
|
||||
int energy_average;
|
||||
};
|
||||
|
||||
/*! \brief Structure which contains per-channel mixing information */
|
||||
struct softmix_channel {
|
||||
/*! Lock to protect this structure */
|
||||
ast_mutex_t lock;
|
||||
/*! Factory which contains audio read in from the channel */
|
||||
struct ast_slinfactory factory;
|
||||
/*! Frame that contains mixed audio to be written out to the channel */
|
||||
struct ast_frame write_frame;
|
||||
/*! Current expected read slinear format. */
|
||||
struct ast_format *read_slin_format;
|
||||
/*! DSP for detecting silence */
|
||||
struct ast_dsp *dsp;
|
||||
/*!
|
||||
* \brief TRUE if a channel is talking.
|
||||
*
|
||||
* \note This affects how the channel's audio is mixed back to
|
||||
* it.
|
||||
*/
|
||||
unsigned int talking:1;
|
||||
/*! TRUE if the channel provided audio for this mixing interval */
|
||||
unsigned int have_audio:1;
|
||||
/*! Buffer containing final mixed audio from all sources */
|
||||
short final_buf[MAX_DATALEN];
|
||||
/*! Buffer containing only the audio from the channel */
|
||||
short our_buf[MAX_DATALEN];
|
||||
/*! Data pertaining to talker mode for video conferencing */
|
||||
struct video_follow_talker_data video_talker;
|
||||
};
|
||||
|
||||
struct softmix_bridge_data {
|
||||
struct ast_timer *timer;
|
||||
/*!
|
||||
* \brief Bridge pointer passed to the softmix mixing thread.
|
||||
*
|
||||
* \note Does not need a reference because the bridge will
|
||||
* always exist while the mixing thread exists even if the
|
||||
* bridge is no longer actively using the softmix technology.
|
||||
*/
|
||||
struct ast_bridge *bridge;
|
||||
/*! Lock for signaling the mixing thread. */
|
||||
ast_mutex_t lock;
|
||||
/*! Condition, used if we need to wake up the mixing thread. */
|
||||
ast_cond_t cond;
|
||||
/*! Thread handling the mixing */
|
||||
pthread_t thread;
|
||||
unsigned int internal_rate;
|
||||
unsigned int internal_mixing_interval;
|
||||
/*! TRUE if the mixing thread should stop */
|
||||
unsigned int stop:1;
|
||||
};
|
||||
|
||||
struct softmix_stats {
|
||||
/*! Each index represents a sample rate used above the internal rate. */
|
||||
unsigned int sample_rates[16];
|
||||
|
@ -155,12 +69,6 @@ struct softmix_stats {
|
|||
unsigned int locked_rate;
|
||||
};
|
||||
|
||||
struct softmix_mixing_array {
|
||||
unsigned int max_num_entries;
|
||||
unsigned int used_entries;
|
||||
int16_t **buffers;
|
||||
};
|
||||
|
||||
struct softmix_translate_helper_entry {
|
||||
int num_times_requested; /*!< Once this entry is no longer requested, free the trans_pvt
|
||||
and re-init if it was usable. */
|
||||
|
@ -263,14 +171,14 @@ static int16_t *softmix_process_read_audio(struct softmix_channel *sc, unsigned
|
|||
*/
|
||||
static void softmix_process_write_audio(struct softmix_translate_helper *trans_helper,
|
||||
struct ast_format *raw_write_fmt,
|
||||
struct softmix_channel *sc)
|
||||
struct softmix_channel *sc, unsigned int default_sample_size)
|
||||
{
|
||||
struct softmix_translate_helper_entry *entry = NULL;
|
||||
int i;
|
||||
|
||||
/* If we provided audio that was not determined to be silence,
|
||||
* then take it out while in slinear format. */
|
||||
if (sc->have_audio && sc->talking) {
|
||||
if (sc->have_audio && sc->talking && !sc->binaural) {
|
||||
for (i = 0; i < sc->write_frame.samples; i++) {
|
||||
ast_slinear_saturated_subtract(&sc->final_buf[i], &sc->our_buf[i]);
|
||||
}
|
||||
|
@ -285,6 +193,13 @@ static void softmix_process_write_audio(struct softmix_translate_helper *trans_h
|
|||
/* do not do any special write translate optimization if we had to make
|
||||
* a special mix for them to remove their own audio. */
|
||||
return;
|
||||
} else if (sc->have_audio && sc->talking && sc->binaural > 0) {
|
||||
/*
|
||||
* Binaural audio requires special saturated substract since we have two
|
||||
* audio signals per channel now.
|
||||
*/
|
||||
softmix_process_write_binaural_audio(sc, default_sample_size);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attempt to optimize channels using the same translation path/codec. Build a list of entries
|
||||
|
@ -293,6 +208,9 @@ static void softmix_process_write_audio(struct softmix_translate_helper *trans_h
|
|||
multiple channels (>=2) using the same codec make sure resources are allocated only when
|
||||
needed and released when not (see also softmix_translate_helper_cleanup */
|
||||
AST_LIST_TRAVERSE(&trans_helper->entries, entry, entry) {
|
||||
if (sc->binaural != 0) {
|
||||
continue;
|
||||
}
|
||||
if (ast_format_cmp(entry->dst_format, raw_write_fmt) == AST_FORMAT_CMP_EQUAL) {
|
||||
entry->num_times_requested++;
|
||||
} else {
|
||||
|
@ -351,12 +269,18 @@ static void softmix_translate_helper_cleanup(struct softmix_translate_helper *tr
|
|||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
}
|
||||
|
||||
static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_channel *bridge_channel, int reset)
|
||||
static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_channel *bridge_channel, int reset, int set_binaural, int binaural_pos_id, int is_announcement)
|
||||
{
|
||||
struct softmix_channel *sc = bridge_channel->tech_pvt;
|
||||
struct ast_format *slin_format;
|
||||
int setup_fail;
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
if (interval != BINAURAL_MIXING_INTERVAL) {
|
||||
interval = BINAURAL_MIXING_INTERVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The callers have already ensured that sc is never NULL. */
|
||||
ast_assert(sc != NULL);
|
||||
|
||||
|
@ -381,6 +305,26 @@ static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_ch
|
|||
sc->write_frame.datalen = SOFTMIX_DATALEN(rate, interval);
|
||||
sc->write_frame.samples = SOFTMIX_SAMPLES(rate, interval);
|
||||
|
||||
/* We will store the rate here cause we need to set the data again when a channel is unsuspended */
|
||||
sc->rate = rate;
|
||||
|
||||
/* If the channel will contain binaural data we will set a identifier in the channel
|
||||
* if set_binaural == -1 this is just a sample rate update, will ignore it. */
|
||||
if (set_binaural == 1) {
|
||||
sc->binaural = 1;
|
||||
} else if (set_binaural == 0) {
|
||||
sc->binaural = 0;
|
||||
}
|
||||
|
||||
/* Setting the binaural position. This doesn't require a change of the overlaying channel infos
|
||||
* and doesn't have to be done if we just updating sample rates. */
|
||||
if (binaural_pos_id != -1) {
|
||||
sc->binaural_pos = binaural_pos_id;
|
||||
}
|
||||
if (is_announcement != -1) {
|
||||
sc->is_announcement = is_announcement;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: The read_slin_format does not hold a reference because it
|
||||
* will always be a signed linear format.
|
||||
|
@ -395,7 +339,13 @@ static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_ch
|
|||
setup_fail |= ast_set_read_format_path(bridge_channel->chan,
|
||||
ast_channel_rawreadformat(bridge_channel->chan), slin_format);
|
||||
ast_channel_unlock(bridge_channel->chan);
|
||||
setup_fail |= ast_set_write_format(bridge_channel->chan, slin_format);
|
||||
|
||||
/* If channel contains binaural data we will set it here for the trans_pvt. */
|
||||
if (set_binaural == 1 || (set_binaural == -1 && sc->binaural == 1)) {
|
||||
setup_fail |= ast_set_write_format_interleaved_stereo(bridge_channel->chan, slin_format);
|
||||
} else if (set_binaural == 0) {
|
||||
setup_fail |= ast_set_write_format(bridge_channel->chan, slin_format);
|
||||
}
|
||||
|
||||
/* set up new DSP. This is on the read side only right before the read frame enters the smoother. */
|
||||
sc->dsp = ast_dsp_new_with_rate(rate);
|
||||
|
@ -435,6 +385,16 @@ static void softmix_poke_thread(struct softmix_bridge_data *softmix_data)
|
|||
/*! \brief Function called when a channel is unsuspended from the bridge */
|
||||
static void softmix_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
#ifdef BINAURAL_RENDERING
|
||||
struct softmix_channel *sc = bridge_channel->tech_pvt;
|
||||
if (sc->binaural) {
|
||||
/* Restore some usefull data if it was a binaural channel */
|
||||
struct ast_format *slin_format;
|
||||
|
||||
slin_format = ast_format_cache_get_slin_by_rate(sc->rate);
|
||||
ast_set_write_format_interleaved_stereo(bridge_channel->chan, slin_format);
|
||||
}
|
||||
#endif
|
||||
if (bridge->tech_pvt) {
|
||||
softmix_poke_thread(bridge->tech_pvt);
|
||||
}
|
||||
|
@ -445,6 +405,15 @@ static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chan
|
|||
{
|
||||
struct softmix_channel *sc;
|
||||
struct softmix_bridge_data *softmix_data;
|
||||
int set_binaural = 0;
|
||||
/*
|
||||
* If false, the channel will be convolved, but since it is a non stereo channel, output
|
||||
* will be mono.
|
||||
*/
|
||||
int skip_binaural_output = 1;
|
||||
int pos_id;
|
||||
int is_announcement = 0;
|
||||
int samplerate_change;
|
||||
|
||||
softmix_data = bridge->tech_pvt;
|
||||
if (!softmix_data) {
|
||||
|
@ -456,6 +425,32 @@ static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chan
|
|||
return -1;
|
||||
}
|
||||
|
||||
samplerate_change = softmix_data->internal_rate;
|
||||
pos_id = -1;
|
||||
if (bridge->softmix.binaural_active) {
|
||||
if (strncmp(ast_channel_name(bridge_channel->chan), "CBAnn", 5) != 0) {
|
||||
set_binaural = ast_format_get_channel_count(bridge_channel->write_format) > 1 ? 1 : 0;
|
||||
if (set_binaural) {
|
||||
softmix_data->internal_rate = samplerate_change;
|
||||
}
|
||||
skip_binaural_output = 0;
|
||||
} else {
|
||||
is_announcement = 1;
|
||||
}
|
||||
if (set_binaural) {
|
||||
softmix_data->convolve.binaural_active = 1;
|
||||
}
|
||||
if (!skip_binaural_output) {
|
||||
pos_id = set_binaural_data_join(&softmix_data->convolve, softmix_data->default_sample_size);
|
||||
if (pos_id == -1) {
|
||||
ast_log(LOG_ERROR, "Bridge %s: Failed to join channel %s. "
|
||||
"Could not allocate enough memory.\n", bridge->uniqueid,
|
||||
ast_channel_name(bridge_channel->chan));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Can't forget the lock */
|
||||
ast_mutex_init(&sc->lock);
|
||||
|
||||
|
@ -466,7 +461,7 @@ static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chan
|
|||
softmix_data->internal_mixing_interval
|
||||
? softmix_data->internal_mixing_interval
|
||||
: DEFAULT_SOFTMIX_INTERVAL,
|
||||
bridge_channel, 0);
|
||||
bridge_channel, 0, set_binaural, pos_id, is_announcement);
|
||||
|
||||
softmix_poke_thread(softmix_data);
|
||||
return 0;
|
||||
|
@ -475,11 +470,23 @@ static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chan
|
|||
/*! \brief Function called when a channel leaves the bridge */
|
||||
static void softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
struct softmix_channel *sc = bridge_channel->tech_pvt;
|
||||
|
||||
struct softmix_channel *sc;
|
||||
struct softmix_bridge_data *softmix_data;
|
||||
softmix_data = bridge->tech_pvt;
|
||||
sc = bridge_channel->tech_pvt;
|
||||
|
||||
if (!sc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bridge->softmix.binaural_active) {
|
||||
if (sc->binaural) {
|
||||
set_binaural_data_leave(&softmix_data->convolve, sc->binaural_pos,
|
||||
softmix_data->default_sample_size);
|
||||
}
|
||||
}
|
||||
|
||||
bridge_channel->tech_pvt = NULL;
|
||||
|
||||
/* Drop mutex lock */
|
||||
|
@ -793,10 +800,15 @@ static void gather_softmix_stats(struct softmix_stats *stats,
|
|||
* \retval 0, no changes to internal rate
|
||||
* \retval 1, internal rate was changed, update all the channels on the next mixing iteration.
|
||||
*/
|
||||
static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct softmix_bridge_data *softmix_data)
|
||||
static unsigned int analyse_softmix_stats(struct softmix_stats *stats,
|
||||
struct softmix_bridge_data *softmix_data, int binaural_active)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (binaural_active) {
|
||||
stats->locked_rate = SOFTMIX_BINAURAL_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-adjust the internal bridge sample rate if
|
||||
* 1. The bridge's internal sample rate is locked in at a sample
|
||||
|
@ -868,7 +880,8 @@ static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct so
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int softmix_mixing_array_init(struct softmix_mixing_array *mixing_array, unsigned int starting_num_entries)
|
||||
static int softmix_mixing_array_init(struct softmix_mixing_array *mixing_array,
|
||||
unsigned int starting_num_entries, unsigned int binaural_active)
|
||||
{
|
||||
memset(mixing_array, 0, sizeof(*mixing_array));
|
||||
mixing_array->max_num_entries = starting_num_entries;
|
||||
|
@ -876,23 +889,45 @@ static int softmix_mixing_array_init(struct softmix_mixing_array *mixing_array,
|
|||
ast_log(LOG_NOTICE, "Failed to allocate softmix mixing structure.\n");
|
||||
return -1;
|
||||
}
|
||||
if (binaural_active) {
|
||||
if (!(mixing_array->chan_pairs = ast_calloc(mixing_array->max_num_entries,
|
||||
sizeof(struct convolve_channel_pair *)))) {
|
||||
ast_log(LOG_NOTICE, "Failed to allocate softmix mixing structure.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void softmix_mixing_array_destroy(struct softmix_mixing_array *mixing_array)
|
||||
static void softmix_mixing_array_destroy(struct softmix_mixing_array *mixing_array,
|
||||
unsigned int binaural_active)
|
||||
{
|
||||
ast_free(mixing_array->buffers);
|
||||
if (binaural_active) {
|
||||
ast_free(mixing_array->chan_pairs);
|
||||
}
|
||||
}
|
||||
|
||||
static int softmix_mixing_array_grow(struct softmix_mixing_array *mixing_array, unsigned int num_entries)
|
||||
static int softmix_mixing_array_grow(struct softmix_mixing_array *mixing_array,
|
||||
unsigned int num_entries, unsigned int binaural_active)
|
||||
{
|
||||
int16_t **tmp;
|
||||
|
||||
/* give it some room to grow since memory is cheap but allocations can be expensive */
|
||||
mixing_array->max_num_entries = num_entries;
|
||||
if (!(tmp = ast_realloc(mixing_array->buffers, (mixing_array->max_num_entries * sizeof(int16_t *))))) {
|
||||
ast_log(LOG_NOTICE, "Failed to re-allocate softmix mixing structure.\n");
|
||||
return -1;
|
||||
}
|
||||
if (binaural_active) {
|
||||
struct convolve_channel_pair **tmp2;
|
||||
if (!(tmp2 = ast_realloc(mixing_array->chan_pairs,
|
||||
(mixing_array->max_num_entries * sizeof(struct convolve_channel_pair *))))) {
|
||||
ast_log(LOG_NOTICE, "Failed to re-allocate softmix mixing structure.\n");
|
||||
return -1;
|
||||
}
|
||||
mixing_array->chan_pairs = tmp2;
|
||||
}
|
||||
mixing_array->buffers = tmp;
|
||||
return 0;
|
||||
}
|
||||
|
@ -911,6 +946,10 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
struct ast_timer *timer;
|
||||
struct softmix_translate_helper trans_helper;
|
||||
int16_t buf[MAX_DATALEN];
|
||||
#ifdef BINAURAL_RENDERING
|
||||
int16_t bin_buf[MAX_DATALEN];
|
||||
int16_t ann_buf[MAX_DATALEN];
|
||||
#endif
|
||||
unsigned int stat_iteration_counter = 0; /* counts down, gather stats at zero and reset. */
|
||||
int timingfd;
|
||||
int update_all_rates = 0; /* set this when the internal sample rate has changed */
|
||||
|
@ -924,7 +963,8 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
ast_timer_set_rate(timer, (1000 / softmix_data->internal_mixing_interval));
|
||||
|
||||
/* Give the mixing array room to grow, memory is cheap but allocations are expensive. */
|
||||
if (softmix_mixing_array_init(&mixing_array, bridge->num_channels + 10)) {
|
||||
if (softmix_mixing_array_init(&mixing_array, bridge->num_channels + 10,
|
||||
bridge->softmix.binaural_active)) {
|
||||
goto softmix_cleanup;
|
||||
}
|
||||
|
||||
|
@ -952,7 +992,8 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
|
||||
/* Grow the mixing array buffer as participants are added. */
|
||||
if (mixing_array.max_num_entries < bridge->num_channels
|
||||
&& softmix_mixing_array_grow(&mixing_array, bridge->num_channels + 5)) {
|
||||
&& softmix_mixing_array_grow(&mixing_array, bridge->num_channels + 5,
|
||||
bridge->softmix.binaural_active)) {
|
||||
goto softmix_cleanup;
|
||||
}
|
||||
|
||||
|
@ -971,6 +1012,10 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
softmix_translate_helper_change_rate(&trans_helper, softmix_data->internal_rate);
|
||||
}
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
check_binaural_position_change(bridge, softmix_data, bridge_channel);
|
||||
#endif
|
||||
|
||||
/* Go through pulling audio from each factory that has it available */
|
||||
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
|
||||
struct softmix_channel *sc = bridge_channel->tech_pvt;
|
||||
|
@ -982,7 +1027,8 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
|
||||
/* Update the sample rate to match the bridge's native sample rate if necessary. */
|
||||
if (update_all_rates) {
|
||||
set_softmix_bridge_data(softmix_data->internal_rate, softmix_data->internal_mixing_interval, bridge_channel, 1);
|
||||
set_softmix_bridge_data(softmix_data->internal_rate,
|
||||
softmix_data->internal_mixing_interval, bridge_channel, 1, -1, -1, -1);
|
||||
}
|
||||
|
||||
/* If stat_iteration_counter is 0, then collect statistics during this mixing interation */
|
||||
|
@ -998,12 +1044,16 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
/* Try to get audio from the factory if available */
|
||||
ast_mutex_lock(&sc->lock);
|
||||
if ((mixing_array.buffers[mixing_array.used_entries] = softmix_process_read_audio(sc, softmix_samples))) {
|
||||
#ifdef BINAURAL_RENDERING
|
||||
add_binaural_mixing(bridge, softmix_data, softmix_samples, &mixing_array, sc,
|
||||
ast_channel_name(bridge_channel->chan));
|
||||
#endif
|
||||
mixing_array.used_entries++;
|
||||
}
|
||||
ast_mutex_unlock(&sc->lock);
|
||||
}
|
||||
|
||||
/* mix it like crazy */
|
||||
/* mix it like crazy (non binaural channels)*/
|
||||
memset(buf, 0, softmix_datalen);
|
||||
for (idx = 0; idx < mixing_array.used_entries; ++idx) {
|
||||
for (x = 0; x < softmix_samples; ++x) {
|
||||
|
@ -1011,6 +1061,10 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
binaural_mixing(bridge, softmix_data, &mixing_array, bin_buf, ann_buf);
|
||||
#endif
|
||||
|
||||
/* Next step go through removing the channel's own audio and creating a good frame... */
|
||||
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
|
||||
struct softmix_channel *sc = bridge_channel->tech_pvt;
|
||||
|
@ -1025,12 +1079,22 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
/* Make SLINEAR write frame from local buffer */
|
||||
ao2_t_replace(sc->write_frame.subclass.format, cur_slin,
|
||||
"Replace softmix channel slin format");
|
||||
sc->write_frame.datalen = softmix_datalen;
|
||||
sc->write_frame.samples = softmix_samples;
|
||||
memcpy(sc->final_buf, buf, softmix_datalen);
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
if (bridge->softmix.binaural_active && softmix_data->convolve.binaural_active
|
||||
&& sc->binaural) {
|
||||
create_binaural_frame(bridge_channel, sc, bin_buf, ann_buf, softmix_datalen,
|
||||
softmix_samples, buf);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
sc->write_frame.datalen = softmix_datalen;
|
||||
sc->write_frame.samples = softmix_samples;
|
||||
memcpy(sc->final_buf, buf, softmix_datalen);
|
||||
}
|
||||
/* process the softmix channel's new write audio */
|
||||
softmix_process_write_audio(&trans_helper, ast_channel_rawwriteformat(bridge_channel->chan), sc);
|
||||
softmix_process_write_audio(&trans_helper,
|
||||
ast_channel_rawwriteformat(bridge_channel->chan), sc,
|
||||
softmix_data->default_sample_size);
|
||||
|
||||
ast_mutex_unlock(&sc->lock);
|
||||
|
||||
|
@ -1040,7 +1104,8 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
|
||||
update_all_rates = 0;
|
||||
if (!stat_iteration_counter) {
|
||||
update_all_rates = analyse_softmix_stats(&stats, softmix_data);
|
||||
update_all_rates = analyse_softmix_stats(&stats, softmix_data,
|
||||
bridge->softmix.binaural_active);
|
||||
stat_iteration_counter = SOFTMIX_STAT_INTERVAL;
|
||||
}
|
||||
stat_iteration_counter--;
|
||||
|
@ -1071,7 +1136,7 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
|
|||
|
||||
softmix_cleanup:
|
||||
softmix_translate_helper_destroy(&trans_helper);
|
||||
softmix_mixing_array_destroy(&mixing_array);
|
||||
softmix_mixing_array_destroy(&mixing_array, bridge->softmix.binaural_active);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1109,6 +1174,32 @@ static void *softmix_mixing_thread(void *data)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (bridge->softmix.binaural_active && !softmix_data->binaural_init) {
|
||||
#ifndef BINAURAL_RENDERING
|
||||
ast_bridge_lock(bridge);
|
||||
bridge->softmix.binaural_active = 0;
|
||||
ast_bridge_unlock(bridge);
|
||||
ast_log(LOG_WARNING, "Bridge: %s: Binaural rendering active by config but not "
|
||||
"compiled.\n", bridge->uniqueid);
|
||||
#else
|
||||
/* Set and init binaural data if binaural is activated in the configuration. */
|
||||
softmix_data->internal_rate = SOFTMIX_BINAURAL_SAMPLE_RATE;
|
||||
softmix_data->default_sample_size = SOFTMIX_SAMPLES(softmix_data->internal_rate,
|
||||
softmix_data->internal_mixing_interval);
|
||||
/* If init for binaural processing fails we will fall back to mono audio processing. */
|
||||
if (init_convolve_data(&softmix_data->convolve, softmix_data->default_sample_size)
|
||||
== -1) {
|
||||
ast_bridge_lock(bridge);
|
||||
bridge->softmix.binaural_active = 0;
|
||||
ast_bridge_unlock(bridge);
|
||||
ast_log(LOG_ERROR, "Bridge: %s: Unable to allocate memory for "
|
||||
"binaural processing, Will only process mono audio.\n",
|
||||
bridge->uniqueid);
|
||||
}
|
||||
softmix_data->binaural_init = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (softmix_mixing_loop(bridge)) {
|
||||
/*
|
||||
* A mixing error occurred. Sleep and try again later so we
|
||||
|
@ -1160,6 +1251,11 @@ static int softmix_bridge_create(struct ast_bridge *bridge)
|
|||
softmix_data->internal_rate = SOFTMIX_MIN_SAMPLE_RATE;
|
||||
softmix_data->internal_mixing_interval = DEFAULT_SOFTMIX_INTERVAL;
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
softmix_data->default_sample_size = SOFTMIX_SAMPLES(softmix_data->internal_rate,
|
||||
softmix_data->internal_mixing_interval);
|
||||
#endif
|
||||
|
||||
bridge->tech_pvt = softmix_data;
|
||||
|
||||
/* Start the mixing thread. */
|
||||
|
@ -1219,7 +1315,9 @@ static void softmix_bridge_destroy(struct ast_bridge *bridge)
|
|||
ast_debug(1, "Bridge %s: Waiting for mixing thread to die.\n", bridge->uniqueid);
|
||||
pthread_join(thread, NULL);
|
||||
}
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
free_convolve_data(&softmix_data->convolve);
|
||||
#endif
|
||||
softmix_bridge_data_destroy(softmix_data);
|
||||
bridge->tech_pvt = NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,579 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2016, Frank Haase, Dennis Guse
|
||||
*
|
||||
* Frank Haase <fra.haase@gmail.com>
|
||||
* Dennis Guse <dennis.guse@alumni.tu-berlin.de>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Multi-party software based binaural mixing
|
||||
*
|
||||
* \author Frank Haase <fra.haase@googlemail.com>
|
||||
* \author Dennis Guse <dennis.guse@alumni.tu-berlin.de>
|
||||
*
|
||||
* \ingroup bridges
|
||||
*/
|
||||
|
||||
#include "include/bridge_softmix_internal.h"
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
#include "include/hrirs_configuration.h"
|
||||
#endif
|
||||
|
||||
/*! The number of prealloced channels when a bridge will be created. */
|
||||
#define CONVOLVE_CHANNEL_PREALLOC 3
|
||||
/*! Max size of the convolve buffer. */
|
||||
#define CONVOLVE_MAX_BUFFER 4096
|
||||
/*! The default sample size in an binaural environment with a two-channel
|
||||
* codec at 48kHz is 960 samples.
|
||||
*/
|
||||
#define CONVOLUTION_SAMPLE_SIZE 960
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
#if SOFTMIX_BINAURAL_SAMPLE_RATE != HRIRS_SAMPLE_RATE
|
||||
#error HRIRs are required to be SOFTMIX_BINAURAL_SAMPLE_RATE Hz. Please adjust hrirs.h accordingly.
|
||||
#endif
|
||||
#if CONVOLUTION_SAMPLE_SIZE < HRIRS_IMPULSE_LEN
|
||||
#error HRIRS_IMPULSE_LEN cannot be longer than CONVOLUTION_SAMPLE_SIZE. Please adjust hrirs.h accordingly.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void reset_channel_pair(struct convolve_channel_pair *channel_pair,
|
||||
unsigned int default_sample_size)
|
||||
{
|
||||
memset(channel_pair->chan_left.overlap_add, 0, sizeof(float) * default_sample_size);
|
||||
memset(channel_pair->chan_right.overlap_add, 0, sizeof(float) * default_sample_size);
|
||||
}
|
||||
|
||||
void random_binaural_pos_change(struct softmix_bridge_data *softmix_data)
|
||||
{
|
||||
/*
|
||||
* We perform a shuffle of all channels, even the ones that aren't used at the
|
||||
* moment of shuffling now. This has the efect that new members will be placed
|
||||
* randomly too.
|
||||
*/
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
struct convolve_channel_pair *tmp;
|
||||
|
||||
if (softmix_data->convolve.chan_size < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
for (i = softmix_data->convolve.chan_size - 1; i > 0; i--) {
|
||||
j = rand() % (i + 1);
|
||||
tmp = softmix_data->convolve.cchan_pair[i];
|
||||
reset_channel_pair(tmp, softmix_data->default_sample_size);
|
||||
softmix_data->convolve.cchan_pair[i] = softmix_data->convolve.cchan_pair[j];
|
||||
softmix_data->convolve.cchan_pair[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
int do_convolve(struct convolve_channel *chan, int16_t *in_samples,
|
||||
unsigned int in_sample_size, unsigned int hrtf_length)
|
||||
{
|
||||
#ifdef BINAURAL_RENDERING
|
||||
unsigned int i;
|
||||
|
||||
if (in_sample_size != CONVOLUTION_SAMPLE_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FFT setting real part */
|
||||
for (i = 0; i < CONVOLUTION_SAMPLE_SIZE; i++) {
|
||||
chan->fftw_in[i] = in_samples[i] * (FLT_MAX / SHRT_MAX);
|
||||
}
|
||||
|
||||
for (i = CONVOLUTION_SAMPLE_SIZE; i < hrtf_length; i++) {
|
||||
chan->fftw_in[i] = 0;
|
||||
}
|
||||
fftw_execute(chan->fftw_plan);
|
||||
|
||||
/* Imaginary mulitplication (frequency space). */
|
||||
/* First FFTW result has never an imaginary part. */
|
||||
chan->fftw_in[0] = chan->fftw_out[0] * chan->hrtf[0];
|
||||
for (i = 1; i < (hrtf_length / 2); i++) {
|
||||
/* Real part */
|
||||
chan->fftw_in[i] = (chan->fftw_out[i] * chan->hrtf[i]) -
|
||||
(chan->fftw_out[hrtf_length - i] * chan->hrtf[hrtf_length - i]);
|
||||
/* Imaginary part */
|
||||
chan->fftw_in[hrtf_length - i] = (chan->fftw_out[i] * chan->hrtf[hrtf_length - i]) +
|
||||
(chan->fftw_out[hrtf_length - i] * chan->hrtf[i]);
|
||||
}
|
||||
|
||||
/* The last (if even) FFTW result has never an imaginary part. */
|
||||
if (hrtf_length % 2 == 0) {
|
||||
chan->fftw_in[hrtf_length / 2] = chan->fftw_out[hrtf_length / 2] *
|
||||
chan->hrtf[hrtf_length / 2];
|
||||
}
|
||||
|
||||
/* iFFT */
|
||||
fftw_execute(chan->fftw_plan_inverse);
|
||||
/* Remove signal increase due to iFFT. */
|
||||
for (i = 0; i < hrtf_length; i++) {
|
||||
chan->fftw_out[i] = chan->fftw_out[i] / (hrtf_length / 2);
|
||||
}
|
||||
|
||||
/* Save the block for overlapp add in the next itteration. */
|
||||
for (i = 0; i < in_sample_size; i++) {
|
||||
chan->overlap_add[i] += chan->fftw_out[i];
|
||||
}
|
||||
|
||||
/* Copy real part to the output, ignore the complex part. */
|
||||
for (i = 0; i < in_sample_size; i++) {
|
||||
chan->out_data[i] = chan->overlap_add[i] * (SHRT_MAX / FLT_MAX);
|
||||
chan->overlap_add[i] = chan->fftw_out[i + in_sample_size];
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct convolve_channel_pair *do_convolve_pair(struct convolve_data *data,
|
||||
unsigned int pos_id, int16_t *in_samples, unsigned int in_sample_size,
|
||||
const char *channel_name)
|
||||
{
|
||||
struct convolve_channel_pair *chan_pair;
|
||||
|
||||
/* If a position has no active member we will not convolve. */
|
||||
if (data->pos_ids[pos_id] != 1) {
|
||||
ast_log(LOG_ERROR, "Channel %s: Channel pair has no active member! (pos id = %d)\n",
|
||||
channel_name, pos_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chan_pair = data->cchan_pair[pos_id];
|
||||
if (do_convolve(&chan_pair->chan_left, in_samples, in_sample_size, data->hrtf_length)) {
|
||||
ast_log(LOG_ERROR, "Channel %s: Binaural processing failed.", channel_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (do_convolve(&chan_pair->chan_right, in_samples, in_sample_size, data->hrtf_length)) {
|
||||
ast_log(LOG_ERROR, "Channel %s: Binaural processing failed.", channel_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chan_pair;
|
||||
}
|
||||
|
||||
float *get_hrir(unsigned int chan_pos, unsigned int chan_side)
|
||||
{
|
||||
#ifdef BINAURAL_RENDERING
|
||||
if (chan_side == HRIRS_CHANNEL_LEFT) {
|
||||
return hrirs_left[ast_binaural_positions[chan_pos]];
|
||||
} else if (chan_side == HRIRS_CHANNEL_RIGHT) {
|
||||
return hrirs_right[ast_binaural_positions[chan_pos]];
|
||||
}
|
||||
#else
|
||||
ast_log(LOG_ERROR, "Requesting data for the binaural conference feature without "
|
||||
"it beeing active.\n");
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int init_convolve_channel(struct convolve_channel *channel, unsigned int hrtf_len,
|
||||
unsigned int chan_pos, unsigned int chan_side, unsigned int default_sample_size)
|
||||
{
|
||||
#ifdef BINAURAL_RENDERING
|
||||
unsigned int j;
|
||||
float *hrir;
|
||||
|
||||
/* Prepare FFTW. */
|
||||
channel->fftw_in = fftw_alloc_real(hrtf_len + 1);
|
||||
if (channel->fftw_in == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel->fftw_out = fftw_alloc_real(hrtf_len + 1);
|
||||
if (channel->fftw_out == NULL) {
|
||||
fftw_free(channel->fftw_in);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(channel->fftw_in, 0, sizeof(double) * (hrtf_len + 1));
|
||||
memset(channel->fftw_out, 0, sizeof(double) * (hrtf_len + 1));
|
||||
|
||||
channel->fftw_plan = fftw_plan_r2r_1d(hrtf_len, channel->fftw_in, channel->fftw_out,
|
||||
FFTW_R2HC, FFTW_PATIENT);
|
||||
channel->fftw_plan_inverse = fftw_plan_r2r_1d(hrtf_len, channel->fftw_in, channel->fftw_out,
|
||||
FFTW_HC2R, FFTW_PATIENT);
|
||||
channel->out_data = ast_calloc(CONVOLVE_MAX_BUFFER, sizeof(int16_t));
|
||||
if (channel->out_data == NULL) {
|
||||
fftw_free(channel->fftw_in);
|
||||
fftw_free(channel->fftw_out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reuse positions if all positions are already used. */
|
||||
chan_pos = chan_pos % HRIRS_IMPULSE_SIZE;
|
||||
|
||||
/* Get HRTF for the channels spatial position. */
|
||||
hrir = get_hrir(chan_pos, chan_side);
|
||||
if (hrir == NULL) {
|
||||
fftw_free(channel->fftw_in);
|
||||
fftw_free(channel->fftw_out);
|
||||
ast_free(channel->out_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (j = 0; j < HRIRS_IMPULSE_LEN; j++) {
|
||||
channel->fftw_in[j] = hrir[j];
|
||||
}
|
||||
|
||||
for (j = HRIRS_IMPULSE_LEN; j < hrtf_len; j++) {
|
||||
channel->fftw_in[j] = 0;
|
||||
}
|
||||
|
||||
fftw_execute(channel->fftw_plan);
|
||||
channel->hrtf = fftw_alloc_real(hrtf_len);
|
||||
if (channel->hrtf == NULL) {
|
||||
fftw_free(channel->fftw_in);
|
||||
fftw_free(channel->fftw_out);
|
||||
ast_free(channel->out_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (j = 0; j < hrtf_len; j++) {
|
||||
channel->hrtf[j] = channel->fftw_out[j];
|
||||
}
|
||||
channel->overlap_add = ast_calloc(default_sample_size, sizeof(float));
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int init_convolve_channel_pair(struct convolve_channel_pair *cchan_pair,
|
||||
unsigned int hrtf_len, unsigned int chan_pos, unsigned int default_sample_size)
|
||||
{
|
||||
#ifdef BINAURAL_RENDERING
|
||||
unsigned int hrirs_pos = chan_pos * 2;
|
||||
int success = 0;
|
||||
|
||||
ast_debug(3, "Binaural pos for the new channel pair will be L: %d R: %d (pos id = %d)\n",
|
||||
hrirs_pos, hrirs_pos + 1, chan_pos);
|
||||
success = init_convolve_channel(&cchan_pair->chan_left, hrtf_len, chan_pos, HRIRS_CHANNEL_LEFT,
|
||||
default_sample_size);
|
||||
if (success == -1) {
|
||||
return success;
|
||||
}
|
||||
|
||||
success = init_convolve_channel(&cchan_pair->chan_right, hrtf_len, chan_pos,
|
||||
HRIRS_CHANNEL_RIGHT, default_sample_size);
|
||||
if (success == -1) {
|
||||
free_convolve_channel(&cchan_pair->chan_left);
|
||||
}
|
||||
|
||||
return success;
|
||||
#else
|
||||
ast_log(LOG_ERROR, "Requesting data for the binaural conference feature "
|
||||
"without it beeing active.\n");
|
||||
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int init_convolve_data(struct convolve_data *data, unsigned int default_sample_size)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
int success;
|
||||
success = 0;
|
||||
|
||||
data->pos_ids = ast_calloc(sizeof(int), sizeof(int) * CONVOLVE_CHANNEL_PREALLOC);
|
||||
if (data->pos_ids == NULL) {
|
||||
return -1;
|
||||
}
|
||||
data->chan_size = CONVOLVE_CHANNEL_PREALLOC;
|
||||
data->number_channels = 0;
|
||||
data->cchan_pair = ast_malloc(sizeof(struct convolve_channel_pair *) *
|
||||
CONVOLVE_CHANNEL_PREALLOC);
|
||||
if (data->cchan_pair == NULL) {
|
||||
ast_free(data->pos_ids);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONVOLVE_CHANNEL_PREALLOC; i++) {
|
||||
data->cchan_pair[i] = ast_malloc(sizeof(struct convolve_channel_pair));
|
||||
if (data->cchan_pair[i] == NULL) {
|
||||
ast_free(data->pos_ids);
|
||||
for (j = 0; j < i; j++) {
|
||||
ast_free(data->cchan_pair[j]);
|
||||
}
|
||||
ast_free(data->cchan_pair);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
data->hrtf_length = (default_sample_size * 2) - 1;
|
||||
for (i = 0; i < CONVOLVE_CHANNEL_PREALLOC; i++) {
|
||||
success = init_convolve_channel_pair(data->cchan_pair[i], data->hrtf_length, i,
|
||||
default_sample_size);
|
||||
if (success == -1) {
|
||||
ast_free(data->pos_ids);
|
||||
for (j = 0; j < i; j++) {
|
||||
free_convolve_channel_pair(data->cchan_pair[j]);
|
||||
}
|
||||
for (j = 0; j < CONVOLVE_CHANNEL_PREALLOC; j++) {
|
||||
ast_free(data->cchan_pair[j]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void free_convolve_channel(struct convolve_channel *cchan)
|
||||
{
|
||||
#ifdef BINAURAL_RENDERING
|
||||
fftw_free(cchan->fftw_out);
|
||||
fftw_free(cchan->fftw_in);
|
||||
fftw_free(cchan->hrtf);
|
||||
ast_free(cchan->overlap_add);
|
||||
ast_free(cchan->out_data);
|
||||
fftw_destroy_plan(cchan->fftw_plan);
|
||||
fftw_destroy_plan(cchan->fftw_plan_inverse);
|
||||
#endif
|
||||
}
|
||||
|
||||
void free_convolve_channel_pair(struct convolve_channel_pair *cchan_pair)
|
||||
{
|
||||
free_convolve_channel(&cchan_pair->chan_left);
|
||||
free_convolve_channel(&cchan_pair->chan_right);
|
||||
}
|
||||
|
||||
void free_convolve_data(struct convolve_data *data)
|
||||
{
|
||||
unsigned int i;
|
||||
ast_free(data->pos_ids);
|
||||
for (i = 0; i < data->chan_size; i++) {
|
||||
free_convolve_channel_pair(data->cchan_pair[i]);
|
||||
ast_free(data->cchan_pair[i]);
|
||||
}
|
||||
ast_free(data->cchan_pair);
|
||||
}
|
||||
|
||||
int set_binaural_data_join(struct convolve_data *data, unsigned int default_sample_size)
|
||||
{
|
||||
struct convolve_channel_pair **cchan_pair_tmp;
|
||||
unsigned int i;
|
||||
int *pos_ids_tmp;
|
||||
|
||||
/* Raise the number of input channels. */
|
||||
data->number_channels++;
|
||||
/* We realloc another channel pair if we are out of prealloced ones. */
|
||||
/* We have prealloced one at the beginning of a conference and if a member leaves. */
|
||||
if (data->chan_size < data->number_channels) {
|
||||
data->chan_size += 1;
|
||||
|
||||
pos_ids_tmp = ast_realloc(data->pos_ids, data->chan_size * sizeof(int));
|
||||
if (pos_ids_tmp) {
|
||||
data->pos_ids = pos_ids_tmp;
|
||||
} else {
|
||||
goto binaural_join_fails;
|
||||
}
|
||||
|
||||
data->pos_ids[data->chan_size - 1] = 0;
|
||||
cchan_pair_tmp = ast_realloc(data->cchan_pair,
|
||||
data->chan_size * sizeof(struct convolve_channel_pair *));
|
||||
if (cchan_pair_tmp) {
|
||||
data->cchan_pair = cchan_pair_tmp;
|
||||
} else {
|
||||
goto binaural_join_fails;
|
||||
}
|
||||
|
||||
data->cchan_pair[data->chan_size - 1] = ast_malloc(sizeof(struct convolve_channel_pair));
|
||||
if (data->cchan_pair[data->chan_size - 1] == NULL) {
|
||||
goto binaural_join_fails;
|
||||
}
|
||||
|
||||
i = init_convolve_channel_pair(data->cchan_pair[data->chan_size - 1], data->hrtf_length,
|
||||
data->chan_size - 1, default_sample_size);
|
||||
if (i == -1) {
|
||||
goto binaural_join_fails;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < data->chan_size; i++) {
|
||||
if (data->pos_ids[i] == 0) {
|
||||
data->pos_ids[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
|
||||
binaural_join_fails:
|
||||
data->number_channels--;
|
||||
data->chan_size -= 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void set_binaural_data_leave(struct convolve_data *data, unsigned int pos,
|
||||
unsigned int default_sample_size)
|
||||
{
|
||||
if (pos >= data->chan_size || data->pos_ids[pos] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
reset_channel_pair(data->cchan_pair[pos], default_sample_size);
|
||||
data->number_channels--;
|
||||
data->pos_ids[pos] = 0;
|
||||
}
|
||||
|
||||
void softmix_process_write_binaural_audio(struct softmix_channel *sc,
|
||||
unsigned int default_sample_size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (sc->write_frame.samples % default_sample_size != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If binaural is suspended, the source audio (mono) will be removed. */
|
||||
if (sc->binaural_suspended) {
|
||||
for (i = 0; i < default_sample_size; i++) {
|
||||
ast_slinear_saturated_subtract(&sc->final_buf[i * 2], &sc->our_buf[i]);
|
||||
ast_slinear_saturated_subtract(&sc->final_buf[(i * 2) + 1], &sc->our_buf[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If binaural is NOT suspended, the source audio (binaural) will be removed. */
|
||||
for (i = 0; i < default_sample_size; i++) {
|
||||
ast_slinear_saturated_subtract(&sc->final_buf[i * 2],
|
||||
&sc->our_chan_pair->chan_left.out_data[i]);
|
||||
ast_slinear_saturated_subtract(&sc->final_buf[(i * 2) + 1],
|
||||
&sc->our_chan_pair->chan_right.out_data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void check_binaural_position_change(struct ast_bridge *bridge,
|
||||
struct softmix_bridge_data *softmix_data, struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
unsigned int pos_change;
|
||||
|
||||
/*
|
||||
* We only check binaural things if binaural is activated by the config
|
||||
* and at least one binaural channel joined.
|
||||
*/
|
||||
if (!(bridge->softmix.binaural_active && softmix_data->convolve.binaural_active)) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Before we pull any audio, we must check if any channel requests a
|
||||
* change of binaural positions.
|
||||
*/
|
||||
pos_change = 0;
|
||||
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
|
||||
if (!bridge_channel->binaural_pos_change) {
|
||||
continue;
|
||||
}
|
||||
ast_bridge_channel_lock_bridge(bridge_channel);
|
||||
bridge_channel->binaural_pos_change = 0;
|
||||
ast_bridge_unlock(bridge_channel->bridge);
|
||||
pos_change = 1;
|
||||
}
|
||||
|
||||
if (pos_change) {
|
||||
random_binaural_pos_change(softmix_data);
|
||||
}
|
||||
}
|
||||
|
||||
void add_binaural_mixing(struct ast_bridge *bridge, struct softmix_bridge_data *softmix_data,
|
||||
unsigned int softmix_samples, struct softmix_mixing_array *mixing_array,
|
||||
struct softmix_channel *sc, const char *channel_name)
|
||||
{
|
||||
struct convolve_channel_pair *pair;
|
||||
|
||||
pair = NULL;
|
||||
/* We only check binaural things if at least one binaural channel joined. */
|
||||
if (!(bridge->softmix.binaural_active && softmix_data->convolve.binaural_active
|
||||
&& (softmix_samples % CONVOLUTION_SAMPLE_SIZE) == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sc->is_announcement) {
|
||||
pair = do_convolve_pair(&softmix_data->convolve, sc->binaural_pos,
|
||||
mixing_array->buffers[mixing_array->used_entries], softmix_samples, channel_name);
|
||||
}
|
||||
sc->our_chan_pair = pair;
|
||||
mixing_array->chan_pairs[mixing_array->used_entries] = pair;
|
||||
}
|
||||
|
||||
void binaural_mixing(struct ast_bridge *bridge, struct softmix_bridge_data *softmix_data,
|
||||
struct softmix_mixing_array *mixing_array, int16_t *bin_buf, int16_t *ann_buf)
|
||||
{
|
||||
unsigned int idx;
|
||||
unsigned int x;
|
||||
|
||||
if (!(bridge->softmix.binaural_active && softmix_data->convolve.binaural_active)) {
|
||||
return;
|
||||
}
|
||||
/* mix it like crazy (binaural channels) */
|
||||
memset(bin_buf, 0, MAX_DATALEN);
|
||||
memset(ann_buf, 0, MAX_DATALEN);
|
||||
|
||||
for (idx = 0; idx < mixing_array->used_entries; idx++) {
|
||||
if (mixing_array->chan_pairs[idx] == NULL) {
|
||||
for (x = 0; x < softmix_data->default_sample_size; x++) {
|
||||
ast_slinear_saturated_add(bin_buf + (x * 2), mixing_array->buffers[idx] + x);
|
||||
ast_slinear_saturated_add(bin_buf + (x * 2) + 1, mixing_array->buffers[idx] + x);
|
||||
ann_buf[x * 2] = mixing_array->buffers[idx][x];
|
||||
ann_buf[(x * 2) + 1] = mixing_array->buffers[idx][x];
|
||||
}
|
||||
} else {
|
||||
for (x = 0; x < softmix_data->default_sample_size; x++) {
|
||||
ast_slinear_saturated_add(bin_buf + (x * 2),
|
||||
mixing_array->chan_pairs[idx]->chan_left.out_data + x);
|
||||
ast_slinear_saturated_add(bin_buf + (x * 2) + 1,
|
||||
mixing_array->chan_pairs[idx]->chan_right.out_data + x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create_binaural_frame(struct ast_bridge_channel *bridge_channel,
|
||||
struct softmix_channel *sc, int16_t *bin_buf, int16_t *ann_buf,
|
||||
unsigned int softmix_datalen, unsigned int softmix_samples, int16_t *buf)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
sc->write_frame.datalen = softmix_datalen * 2;
|
||||
sc->write_frame.samples = softmix_samples * 2;
|
||||
if (!bridge_channel->binaural_suspended) {
|
||||
sc->binaural_suspended = 0;
|
||||
if (sc->is_announcement) {
|
||||
memcpy(sc->final_buf, ann_buf, softmix_datalen * 2);
|
||||
} else {
|
||||
memcpy(sc->final_buf, bin_buf, softmix_datalen * 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark that binaural output is suspended, since we use two channel audio
|
||||
* we copy the same signals into both channels.
|
||||
*/
|
||||
sc->binaural_suspended = 1;
|
||||
for (i = 0; i < softmix_samples; i++) {
|
||||
sc->final_buf[i * 2] = buf[i];
|
||||
sc->final_buf[(i * 2) + 1] = buf[i];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2011, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
* David Vossel <dvossel@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Multi-party software based channel mixing (header)
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
* \author David Vossel <dvossel@digium.com>
|
||||
*
|
||||
* \ingroup bridges
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_BRIDGE_SOFTMIX_INTERNAL_H
|
||||
#define _ASTERISK_BRIDGE_SOFTMIX_INTERNAL_H
|
||||
|
||||
#include "asterisk.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/bridge.h"
|
||||
#include "asterisk/bridge_technology.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/slinfactory.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/timing.h"
|
||||
#include "asterisk/translate.h"
|
||||
|
||||
#ifdef BINAURAL_RENDERING
|
||||
#include <fftw3.h>
|
||||
#endif
|
||||
|
||||
#if defined(__Darwin__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
|
||||
#include <float.h>
|
||||
#else
|
||||
#include <values.h>
|
||||
#endif
|
||||
|
||||
#define MAX_DATALEN 8096
|
||||
#define DEFAULT_ENERGY_HISTORY_LEN 150
|
||||
|
||||
/*! Setting the sample rate to 48000 by default if binaural is activated. */
|
||||
#define SOFTMIX_BINAURAL_SAMPLE_RATE 48000
|
||||
/*! We only support 20 ms interval length with binaural data at the moment. */
|
||||
#define BINAURAL_MIXING_INTERVAL 20
|
||||
|
||||
struct convolve_channel {
|
||||
/*! The head related transfer function used for convolving */
|
||||
double *hrtf;
|
||||
/*! Input signals for fftw */
|
||||
double *fftw_in;
|
||||
/*! Output signals from the fftw */
|
||||
double *fftw_out;
|
||||
/*! Signals for overlap add */
|
||||
float *overlap_add;
|
||||
/*! The resulting data after the convolution */
|
||||
int16_t *out_data;
|
||||
#ifdef BINAURAL_RENDERING
|
||||
/*! The fftw plan for binaural signaling */
|
||||
fftw_plan fftw_plan;
|
||||
/*! The inverse fftw plan for binaural signaling */
|
||||
fftw_plan fftw_plan_inverse;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct convolve_channel_pair {
|
||||
/*! The left channel of a stereo channel pair */
|
||||
struct convolve_channel chan_left;
|
||||
/*! The right channel of a stereo channel pair */
|
||||
struct convolve_channel chan_right;
|
||||
};
|
||||
|
||||
struct convolve_data {
|
||||
/*! A count of all channels potentialy having input data for the conference. */
|
||||
int number_channels;
|
||||
/*! Will set to true if there is at least one binaural output.
|
||||
* Only if set to true data will be convolved. */
|
||||
int binaural_active;
|
||||
/*! The length of the head related transfer function */
|
||||
unsigned int hrtf_length;
|
||||
/*! Number of channels available for convolving.
|
||||
* We do not delete a channel when a member leaves, cause we can reuse it for the next one. */
|
||||
int chan_size;
|
||||
/*! The positions of the single channels in the virtual room */
|
||||
int *pos_ids;
|
||||
/*! Each channel has a stereo pair of channels for the convolution */
|
||||
struct convolve_channel_pair **cchan_pair;
|
||||
};
|
||||
|
||||
struct video_follow_talker_data {
|
||||
/*! audio energy history */
|
||||
int energy_history[DEFAULT_ENERGY_HISTORY_LEN];
|
||||
/*! The current slot being used in the history buffer, this
|
||||
* increments and wraps around */
|
||||
int energy_history_cur_slot;
|
||||
/*! The current energy sum used for averages. */
|
||||
int energy_accum;
|
||||
/*! The current energy average */
|
||||
int energy_average;
|
||||
};
|
||||
|
||||
/*! \brief Structure which contains per-channel mixing information */
|
||||
struct softmix_channel {
|
||||
/*! Lock to protect this structure */
|
||||
ast_mutex_t lock;
|
||||
/*! Factory which contains audio read in from the channel */
|
||||
struct ast_slinfactory factory;
|
||||
/*! Frame that contains mixed audio to be written out to the channel */
|
||||
struct ast_frame write_frame;
|
||||
/*! Current expected read slinear format. */
|
||||
struct ast_format *read_slin_format;
|
||||
/*! DSP for detecting silence */
|
||||
struct ast_dsp *dsp;
|
||||
/*!
|
||||
* \brief TRUE if a channel is talking.
|
||||
*
|
||||
* \note This affects how the channel's audio is mixed back to
|
||||
* it.
|
||||
*/
|
||||
unsigned int talking:1;
|
||||
/*! TRUE if the channel provided audio for this mixing interval */
|
||||
unsigned int have_audio:1;
|
||||
/*! We set binaural also as channel data, to have better tracking.
|
||||
* It is also present in transpvt.
|
||||
*/
|
||||
unsigned int binaural:1;
|
||||
/*! TRUE if this is an announcement channel (data will not be convolved) */
|
||||
unsigned int is_announcement:1;
|
||||
/*! The position of the channel in the virtual room represented by an id
|
||||
* This ID has to be set even if the channel has no binaural output!
|
||||
*/
|
||||
unsigned int binaural_pos;
|
||||
/*! The channel pair for this channel */
|
||||
struct convolve_channel_pair *our_chan_pair;
|
||||
/*! Marks the channel for suspending all binaural activity on the output */
|
||||
unsigned int binaural_suspended:1;
|
||||
/*! Channel sample rate, stored to retrieve it after unsuspending the channel */
|
||||
int rate;
|
||||
/*! Buffer containing final mixed audio from all sources */
|
||||
short final_buf[MAX_DATALEN];
|
||||
/*! Buffer containing only the audio from the channel */
|
||||
short our_buf[MAX_DATALEN];
|
||||
/*! Data pertaining to talker mode for video conferencing */
|
||||
struct video_follow_talker_data video_talker;
|
||||
};
|
||||
|
||||
struct softmix_bridge_data {
|
||||
struct ast_timer *timer;
|
||||
/*!
|
||||
* \brief Bridge pointer passed to the softmix mixing thread.
|
||||
*
|
||||
* \note Does not need a reference because the bridge will
|
||||
* always exist while the mixing thread exists even if the
|
||||
* bridge is no longer actively using the softmix technology.
|
||||
*/
|
||||
struct ast_bridge *bridge;
|
||||
/*! Lock for signaling the mixing thread. */
|
||||
ast_mutex_t lock;
|
||||
/*! Condition, used if we need to wake up the mixing thread. */
|
||||
ast_cond_t cond;
|
||||
/*! Thread handling the mixing */
|
||||
pthread_t thread;
|
||||
unsigned int internal_rate;
|
||||
unsigned int internal_mixing_interval;
|
||||
/*! TRUE if the mixing thread should stop */
|
||||
unsigned int stop:1;
|
||||
/*! The default sample size (e.g. using Opus at 48khz and 20 ms mixing
|
||||
* interval, sample size is 960) */
|
||||
unsigned int default_sample_size;
|
||||
/*! All data needed for binaural signaling */
|
||||
struct convolve_data convolve;
|
||||
/*! TRUE if the first attempt to init binaural rendering data was done
|
||||
* (does not guarantee success)
|
||||
*/
|
||||
unsigned int binaural_init;
|
||||
};
|
||||
|
||||
struct softmix_mixing_array {
|
||||
unsigned int max_num_entries;
|
||||
unsigned int used_entries;
|
||||
int16_t **buffers;
|
||||
/*! Stereo channel pairs used to store convolved binaural signals */
|
||||
struct convolve_channel_pair **chan_pairs;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Deletes left over signals on a channel that it can be reused.
|
||||
*
|
||||
* \param channel_pair The channel pair which contains the left and right audio channel.
|
||||
* \param default_sample_size The sample size which the channel pair uses.
|
||||
*/
|
||||
void reset_channel_pair(struct convolve_channel_pair *channel_pair,
|
||||
unsigned int default_sample_size);
|
||||
|
||||
/*!
|
||||
* \brief Randomly changes the virtual positions of conference participants.
|
||||
*
|
||||
* \param softmix_data The structure containing all position informations.
|
||||
*/
|
||||
void random_binaural_pos_change(struct softmix_bridge_data *softmix_data);
|
||||
|
||||
/*!
|
||||
* \brief Binaural convolving of audio data for a channel.
|
||||
*
|
||||
* \param chan The channel that will contain the binaural audio data as result.
|
||||
* \param in_samples The audio data which will be convolved.
|
||||
* \param in_sample_size The size of the audio data.
|
||||
* \param hrtf_length The length of the head related transfer function used to convolve the audio.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int do_convolve(struct convolve_channel *chan, int16_t *in_samples,
|
||||
unsigned int in_sample_size, unsigned int hrtf_length);
|
||||
|
||||
/*!
|
||||
* \brief Binaural convolving of audio data for a channel pair (left and right channel).
|
||||
*
|
||||
* \param data Contains the left and right audio channel.
|
||||
* \param pos_id The position the channel has in the virtual enviroment.
|
||||
* \param in_samples The audio data which will be convolved for both channels.
|
||||
* \param in_sample_size The size of the audio data.
|
||||
* \param channel_name The name of the channel
|
||||
*
|
||||
* \retval The channel pair with convolved audio on success.
|
||||
* \retval NULL failure
|
||||
*/
|
||||
struct convolve_channel_pair *do_convolve_pair(struct convolve_data *data,
|
||||
unsigned int pos_id, int16_t *in_samples, unsigned int in_sample_size,
|
||||
const char *channel_name);
|
||||
|
||||
/*!
|
||||
* \brief Provides a head related impulse response for the given position in the virtual
|
||||
* enviroment.
|
||||
*
|
||||
* \param chan_pos The position of the channel in the virtual enviroment.
|
||||
* \param chan_side 0 for the left audio channel, 1 for the right.
|
||||
*
|
||||
* \retval The hrir for the given position in the virtual room for either the left or right
|
||||
* channels.
|
||||
* \retval NULL on failure.
|
||||
*
|
||||
*/
|
||||
float *get_hrir(unsigned int chan_pos, unsigned int chan_side);
|
||||
|
||||
/*!
|
||||
* \brief Initializes all data needed for binaural audio processing.
|
||||
*
|
||||
* \param channel The channel used for binaural audio processing.
|
||||
* \param hrtf_len The length of the head related impulse response used for binaural processing.
|
||||
* \param chan_pos The position of the channel in the virtual enviroment.
|
||||
* \param chan_side 0 for the left audio channel, 1 for the right.
|
||||
* \param default_sample_size The default size of audio samples.
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int init_convolve_channel(struct convolve_channel *channel, unsigned int hrtf_len,
|
||||
unsigned int chan_pos, unsigned int chan_side, unsigned int default_sample_size);
|
||||
|
||||
/*!
|
||||
* \brief Initializies all data needed for binaural audio processing of a channel pair
|
||||
* (left and right).
|
||||
*
|
||||
* \param cchan_pair The channel pair used for binaural audio processing.
|
||||
* \param hrtf_len The length of the head related impulse response used for binaural processing.
|
||||
* \param chan_pos The position of the channel in the virtual enviroment.
|
||||
* \param default_sample_size The default size of audio samples.
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int init_convolve_channel_pair(struct convolve_channel_pair *cchan_pair,
|
||||
unsigned int hrtf_len, unsigned int chan_pos, unsigned int default_sample_size);
|
||||
|
||||
/*!
|
||||
* \brief Preinits a specific number of channels (CONVOVLE_CHANNEL_PREALLOC)
|
||||
* at the beginning of a conference.
|
||||
*
|
||||
* \param data Contains all channels and data needed for binaural processing
|
||||
* (e.g. head related transfer functions).
|
||||
* \param default_sample_size The default size of audio samples.
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int init_convolve_data(struct convolve_data *data, unsigned int default_sample_size);
|
||||
|
||||
/*!
|
||||
* \brief Frees all data needed for binaural processing by an audio channel.
|
||||
*
|
||||
* \param cchan The channel to clean up.
|
||||
*/
|
||||
void free_convolve_channel(struct convolve_channel *cchan);
|
||||
|
||||
/*!
|
||||
* \brief Frees all data needed for binaural processing by a pair of audio channels
|
||||
* (left and right).
|
||||
*
|
||||
* \param cchan_pair The channel pair to clean up.
|
||||
*/
|
||||
void free_convolve_channel_pair(struct convolve_channel_pair *cchan_pair);
|
||||
|
||||
/*!
|
||||
* \brief Frees all channels and data needed for binaural audio processing.
|
||||
*
|
||||
* \param data Contains all channels and data for the cleanup process.
|
||||
*/
|
||||
void free_convolve_data(struct convolve_data *data);
|
||||
|
||||
/*!
|
||||
* \brief Joins a channel into a virtual enviroment build with the help of binaural sythesis.
|
||||
*
|
||||
* \param data Contains all channels and data needed for binaural processing
|
||||
* (e.g. head related transfer functions).
|
||||
* \param default_sample_size The default size of audio samples.
|
||||
*
|
||||
* \retval The position of the channel in the virtual enviroment.
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int set_binaural_data_join(struct convolve_data *data, unsigned int default_sample_size);
|
||||
|
||||
/*!
|
||||
* \brief Removes a channel from the binaural conference bridge. Marks the position in
|
||||
* the virtual room as unused that it can be reused by the next channel which enters the
|
||||
* conference.
|
||||
*
|
||||
* \param data Contains all channels and data needed for binaural processing
|
||||
* (e.g. head related transfer functions).
|
||||
* \param pos The position of the channel in the virtual enviroment.
|
||||
* \param default_sample_size The default size of audio samples.
|
||||
*/
|
||||
void set_binaural_data_leave(struct convolve_data *data, unsigned int pos,
|
||||
unsigned int default_sample_size);
|
||||
|
||||
/*!
|
||||
* \brief Writes the binaural audio to a channel.
|
||||
*
|
||||
* \param sc The softmix channel.
|
||||
* \param default_sample_size The default size of audio samples.
|
||||
*/
|
||||
void softmix_process_write_binaural_audio(struct softmix_channel *sc,
|
||||
unsigned int default_sample_size);
|
||||
|
||||
/*!
|
||||
* \brief Checks if a position change in the virual enviroment is requested by one of
|
||||
* the participants.
|
||||
*
|
||||
* \param bridge The conference bridge.
|
||||
* \param softmix_data The data used by the softmix bridge.
|
||||
* \param bridge_channel The bridge channel.
|
||||
*/
|
||||
void check_binaural_position_change(struct ast_bridge *bridge,
|
||||
struct softmix_bridge_data *softmix_data, struct ast_bridge_channel *bridge_channel);
|
||||
|
||||
/*!
|
||||
* \brief Processes audio data with the binaural synthesis and adds the result to the mixing array.
|
||||
*
|
||||
* \param bridge The conference bridge needed to check if binaural processing is active or not.
|
||||
* \param softmix_data Contains all data for the softmix bridge and for the binaural processing.
|
||||
* \param softmix_samples The sample size.
|
||||
* \param mixing_array The array which holds all audio data for mixing.
|
||||
* \param sc The channel which contains the audio data to process.
|
||||
* \param channel_name The name of the channel
|
||||
*/
|
||||
void add_binaural_mixing(struct ast_bridge *bridge, struct softmix_bridge_data *softmix_data,
|
||||
unsigned int softmix_samples, struct softmix_mixing_array *mixing_array,
|
||||
struct softmix_channel *sc, const char *channel_name);
|
||||
|
||||
/*!
|
||||
* \brief Mixes all binaural audio data contained in the mixing array.
|
||||
*
|
||||
* \param bridge The conference bridge needed to check if binaural processing is active or not.
|
||||
* \param softmix_data Contains all data for the softmix bridge and for the binaural processing.
|
||||
* \param mixing_array The array which holds all audio data for mixing.
|
||||
* \param bin_buf The buffer that will contain the mixing results.
|
||||
* \param ann_buf The buffer that will contain mixed announcements in an interleaved format.
|
||||
*/
|
||||
void binaural_mixing(struct ast_bridge *bridge, struct softmix_bridge_data *softmix_data,
|
||||
struct softmix_mixing_array *mixing_array, int16_t *bin_buf, int16_t *ann_buf);
|
||||
|
||||
/*!
|
||||
* \brief Creates a frame out of binaural audio data.
|
||||
*
|
||||
* \param bridge_channel Contains the information if binaural processing is active or not.
|
||||
* If active binaural audio data will be copied, if not mono data will be provided in an
|
||||
* interleaved format.
|
||||
* \param sc The softmix channel holding all informations for the process.
|
||||
* \param bin_buf The buffer that contains all mixing results.
|
||||
* \param ann_buf The buffer that contains mixed announcements in an interleaved format.
|
||||
* \param softmix_datalen The size of the audio data.
|
||||
* \param softmix_samples The number of audio samples.
|
||||
* \param buf The buffer that contains all mono mixing results, used if binaural processing is
|
||||
* inactive.
|
||||
*/
|
||||
void create_binaural_frame(struct ast_bridge_channel *bridge_channel,
|
||||
struct softmix_channel *sc, int16_t *bin_buf, int16_t *ann_buf,
|
||||
unsigned int softmix_datalen, unsigned int softmix_samples, int16_t *buf);
|
||||
|
||||
#endif /* _ASTERISK_BRIDGE_SOFTMIX_INTERNAL_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2016, Frank Haase, Dennis Guse
|
||||
*
|
||||
* Frank Haase <fra.haase@gmail.com>
|
||||
* Dennis Guse <dennis.guse@alumni.tu-berlin.de>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Multi-party software binaural channel mixing (header)
|
||||
*
|
||||
* \author Frank Haase <fra.haase@googlemail.com>
|
||||
* \author Dennis Guse <dennis.guse@alumni.tu-berlin.de>
|
||||
*
|
||||
* \ingroup bridges
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_HRIRS_CONFIGURATION_H
|
||||
#define _ASTERISK_HRIRS_CONFIGURATION_H
|
||||
|
||||
#include "hrirs.h"
|
||||
|
||||
/*! The size of possible positions in the virtual enviroment build with the help
|
||||
* of binaural audio processing.
|
||||
*/
|
||||
#define POSITION_SIZE 181
|
||||
|
||||
#if POSITION_SIZE != HRIRS_IMPULSE_SIZE
|
||||
#error "The conference is designed for 181 individual places at the moment. If you want to change this please alter the positions array first."
|
||||
#endif
|
||||
|
||||
/*! The offset for the left channel audio channel. */
|
||||
#define HRIRS_CHANNEL_LEFT 0
|
||||
/*! The offset for the right channel audio channel. */
|
||||
#define HRIRS_CHANNEL_RIGHT 1
|
||||
|
||||
/*! The ast_binaural_positions array contains a specific plan to order conference
|
||||
* participants in the virtual enviroment.
|
||||
*/
|
||||
static unsigned int ast_binaural_positions[POSITION_SIZE] = {
|
||||
90, 80, 100, 70, 110, 60, 120, 50, 130, 40, 140, 20, 160, 0, 180, 85, 95, 75, 105, 65, 115,
|
||||
55, 125, 45, 135, 30, 150, 10, 170, 87, 93, 82, 98, 77, 103, 72, 108, 67, 113, 62, 118, 57,
|
||||
123, 52, 128, 47, 133, 42, 138, 35, 145, 25, 155, 15, 165, 5, 175, 88, 92, 86, 83, 97, 81,
|
||||
99, 78, 102, 76, 104, 73, 94, 107, 71, 109, 68, 112, 66, 114, 63, 117, 61, 119, 58, 122, 56,
|
||||
124, 53, 127, 51, 129, 48, 132, 46, 134, 43, 137, 41, 139, 37, 143, 32, 148, 27, 153, 22,
|
||||
158, 17, 163, 12, 168, 7, 96, 173, 2, 178, 89, 91, 84, 79, 101, 74, 106, 69, 111, 64, 116,
|
||||
59, 121, 54, 126, 49, 131, 44, 136, 38, 142, 36, 144, 33, 147, 31, 149, 28, 152, 26, 154,
|
||||
23, 157, 21, 159, 162, 16, 164, 13, 167, 11, 169, 8, 172, 6, 174, 3, 177, 1, 179, 39, 141,
|
||||
34, 146, 29, 151, 24, 156, 19, 161, 14, 166, 9, 171, 4, 18, 176 };
|
||||
|
||||
#endif /* _ASTERISK_HRIRS_CONFIGURATION_H */
|
|
@ -0,0 +1,8 @@
|
|||
<member name="binaural_rendering_in_bridge_softmix"
|
||||
displayname="Enable binaural rendering in bridge_softmix"
|
||||
remove_on_change="bridges/bridge_softmix.o bridges/bridge_softmix.so bridges/bridge_softmix/bridge_softmix_binaural.o">
|
||||
<support_level>option</support_level>
|
||||
<depend>bridge_softmix</depend>
|
||||
<depend>fftw3</depend>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
</member>
|
|
@ -29419,7 +29419,7 @@ if eval \${$as_ac_Lib+:} false; then :
|
|||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lfftw3 ${pbxlibdir} -lfftw3 $LIBS"
|
||||
LIBS="-lfftw3 ${pbxlibdir} $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
|
@ -29461,7 +29461,7 @@ fi
|
|||
|
||||
# now check for the header.
|
||||
if test "${AST_FFTW3_FOUND}" = "yes"; then
|
||||
FFTW3_LIB="${pbxlibdir} -lfftw3 -lfftw3"
|
||||
FFTW3_LIB="${pbxlibdir} -lfftw3 "
|
||||
# if --with-FFTW3=DIR has been specified, use it.
|
||||
if test "x${FFTW3_DIR}" != "x"; then
|
||||
FFTW3_INCLUDE="-I${FFTW3_DIR}/include"
|
||||
|
@ -29499,11 +29499,6 @@ _ACEOF
|
|||
fi
|
||||
|
||||
|
||||
if test "$PBX_FFTW3" = "1"; then
|
||||
|
||||
$as_echo "#define HAVE_FFTW 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
if test "x${PBX_SNDFILE}" != "x1" -a "${USE_SNDFILE}" != "no"; then
|
||||
pbxlibdir=""
|
||||
|
@ -29528,7 +29523,7 @@ if eval \${$as_ac_Lib+:} false; then :
|
|||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lsndfile ${pbxlibdir} -lsndfile $LIBS"
|
||||
LIBS="-lsndfile ${pbxlibdir} $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
|
@ -29570,7 +29565,7 @@ fi
|
|||
|
||||
# now check for the header.
|
||||
if test "${AST_SNDFILE_FOUND}" = "yes"; then
|
||||
SNDFILE_LIB="${pbxlibdir} -lsndfile -lsndfile"
|
||||
SNDFILE_LIB="${pbxlibdir} -lsndfile "
|
||||
# if --with-SNDFILE=DIR has been specified, use it.
|
||||
if test "x${SNDFILE_DIR}" != "x"; then
|
||||
SNDFILE_INCLUDE="-I${SNDFILE_DIR}/include"
|
||||
|
@ -29610,8 +29605,6 @@ fi
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
if test "x${PBX_SPANDSP}" != "x1" -a "${USE_SPANDSP}" != "no"; then
|
||||
if test "xminimum version of SpanDSP" != "x"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for minimum version of SpanDSP" >&5
|
||||
|
@ -36253,6 +36246,31 @@ else
|
|||
touch makeopts.acbak
|
||||
fi
|
||||
|
||||
# conf_bridge (binaural rendering): check if HRIRs are available
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for bridges/bridge_softmix/include/hrirs.h" >&5
|
||||
$as_echo_n "checking for bridges/bridge_softmix/include/hrirs.h... " >&6; }
|
||||
if ${ac_cv_file_bridges_bridge_softmix_include_hrirs_h+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
test "$cross_compiling" = yes &&
|
||||
as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
|
||||
if test -r "bridges/bridge_softmix/include/hrirs.h"; then
|
||||
ac_cv_file_bridges_bridge_softmix_include_hrirs_h=yes
|
||||
else
|
||||
ac_cv_file_bridges_bridge_softmix_include_hrirs_h=no
|
||||
fi
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file_bridges_bridge_softmix_include_hrirs_h" >&5
|
||||
$as_echo "$ac_cv_file_bridges_bridge_softmix_include_hrirs_h" >&6; }
|
||||
if test "x$ac_cv_file_bridges_bridge_softmix_include_hrirs_h" = xyes; then :
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_BRIDGES_BRIDGE_SOFTMIX_INCLUDE_HRIRS_H 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files build_tools/menuselect-deps makeopts"
|
||||
|
||||
|
||||
|
|
12
configure.ac
12
configure.ac
|
@ -2283,13 +2283,8 @@ AST_EXT_LIB_CHECK([PRI_REVERSE_CHARGE], [pri], [pri_sr_set_reversecharge], [libp
|
|||
# ------------------------------------^
|
||||
|
||||
AST_EXT_LIB_CHECK([RESAMPLE], [resample], [resample_open], [libresample.h], [-lm])
|
||||
AST_EXT_LIB_CHECK([FFTW3], [fftw3], [fftw_alloc_real], [fftw3.h], [-lfftw3])
|
||||
if test "$PBX_FFTW3" = "1"; then
|
||||
AC_DEFINE([HAVE_FFTW], 1, [Define 1 if your system has fftw.])
|
||||
fi
|
||||
AST_EXT_LIB_CHECK([SNDFILE], [sndfile], [sf_open], [sndfile.h], [-lsndfile])
|
||||
|
||||
|
||||
AST_EXT_LIB_CHECK([FFTW3], [fftw3], [fftw_alloc_real], [fftw3.h])
|
||||
AST_EXT_LIB_CHECK([SNDFILE], [sndfile], [sf_open], [sndfile.h])
|
||||
|
||||
AST_C_COMPILE_CHECK([SPANDSP], [
|
||||
#if SPANDSP_RELEASE_DATE < 20080516
|
||||
|
@ -2692,6 +2687,9 @@ else
|
|||
touch makeopts.acbak
|
||||
fi
|
||||
|
||||
# conf_bridge (binaural rendering): check if HRIRs are available
|
||||
AC_CHECK_FILES([bridges/bridge_softmix/include/hrirs.h])
|
||||
|
||||
AC_CONFIG_FILES([build_tools/menuselect-deps makeopts])
|
||||
AST_CHECK_MANDATORY
|
||||
|
||||
|
|
|
@ -124,6 +124,10 @@
|
|||
/* Define to 1 if you have the Bluetooth library. */
|
||||
#undef HAVE_BLUETOOTH
|
||||
|
||||
/* Define to 1 if you have the file `bridges/bridge_softmix/include/hrirs.h'.
|
||||
*/
|
||||
#undef HAVE_BRIDGES_BRIDGE_SOFTMIX_INCLUDE_HRIRS_H
|
||||
|
||||
/* Define to 1 if byteswap.h macros are available. */
|
||||
#undef HAVE_BYTESWAP_H
|
||||
|
||||
|
@ -236,9 +240,6 @@
|
|||
/* Define to 1 if you have the `ffsll' function. */
|
||||
#undef HAVE_FFSLL
|
||||
|
||||
/* Define 1 if your system has fftw. */
|
||||
#undef HAVE_FFTW
|
||||
|
||||
/* Define to 1 if you have the LIBFFTW3 library. */
|
||||
#undef HAVE_FFTW3
|
||||
|
||||
|
|
|
@ -263,6 +263,8 @@ struct ast_bridge_softmix {
|
|||
* for itself.
|
||||
*/
|
||||
unsigned int internal_mixing_interval;
|
||||
/*! TRUE if binaural convolve is activated in configuration. */
|
||||
unsigned int binaural_active;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -867,6 +869,14 @@ void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int
|
|||
*/
|
||||
void ast_bridge_set_mixing_interval(struct ast_bridge *bridge, unsigned int mixing_interval);
|
||||
|
||||
/*!
|
||||
* \brief Activates the use of binaural signals in a conference bridge.
|
||||
*
|
||||
* \param bridge Channel to activate the binaural signals.
|
||||
* \param binaural_active If true binaural signal processing will be active for the bridge.
|
||||
*/
|
||||
void ast_bridge_set_binaural_active(struct ast_bridge *bridge, unsigned int binaural_active);
|
||||
|
||||
/*!
|
||||
* \brief Set a bridge to feed a single video source to all participants.
|
||||
*/
|
||||
|
|
|
@ -172,6 +172,17 @@ struct ast_bridge_channel {
|
|||
/*! Collected DTMF digits for DTMF hooks. */
|
||||
char collected[MAXIMUM_DTMF_FEATURE_STRING];
|
||||
} dtmf_hook_state;
|
||||
union {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
/*! TRUE if binaural is suspended. */
|
||||
unsigned int binaural_suspended:1;
|
||||
/*! TRUE if a change of binaural positions has to be performed. */
|
||||
unsigned int binaural_pos_change:1;
|
||||
/*! Padding */
|
||||
unsigned int padding:30;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -3736,6 +3736,13 @@ void ast_bridge_set_mixing_interval(struct ast_bridge *bridge, unsigned int mixi
|
|||
ast_bridge_unlock(bridge);
|
||||
}
|
||||
|
||||
void ast_bridge_set_binaural_active(struct ast_bridge *bridge, unsigned int binaural_active)
|
||||
{
|
||||
ast_bridge_lock(bridge);
|
||||
bridge->softmix.binaural_active = binaural_active;
|
||||
ast_bridge_unlock(bridge);
|
||||
}
|
||||
|
||||
void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate)
|
||||
{
|
||||
ast_bridge_lock(bridge);
|
||||
|
|
|
@ -238,6 +238,10 @@ static enum support_level_values string_to_support_level(const char *support_lev
|
|||
return SUPPORT_EXTERNAL;
|
||||
}
|
||||
|
||||
if (!strcasecmp(support_level, "option")) {
|
||||
return SUPPORT_OPTION;
|
||||
}
|
||||
|
||||
return SUPPORT_UNSPECIFIED;
|
||||
}
|
||||
|
||||
|
@ -253,6 +257,8 @@ static const char *support_level_to_string(enum support_level_values support_lev
|
|||
return "Deprecated";
|
||||
case SUPPORT_EXTERNAL:
|
||||
return "External";
|
||||
case SUPPORT_OPTION:
|
||||
return "Module Options";
|
||||
default:
|
||||
return "Unspecified";
|
||||
}
|
||||
|
@ -461,7 +467,7 @@ static int process_xml_member_node(xmlNode *node, struct category *cat)
|
|||
process_process_xml_category_child_node(cur, mem);
|
||||
}
|
||||
|
||||
if (!cat->positive_output) {
|
||||
if (!cat->positive_output && strcasecmp(mem->support_level, "option")) {
|
||||
mem->enabled = 1;
|
||||
if (!mem->defaultenabled || strcasecmp(mem->defaultenabled, "no")) {
|
||||
mem->was_enabled = 1;
|
||||
|
|
|
@ -108,7 +108,8 @@ enum support_level_values {
|
|||
SUPPORT_DEPRECATED = 2,
|
||||
SUPPORT_UNSPECIFIED = 3,
|
||||
SUPPORT_EXTERNAL = 4,
|
||||
SUPPORT_COUNT = 5, /* Keep this item at the end of the list. Tracks total number of support levels. */
|
||||
SUPPORT_OPTION = 5,
|
||||
SUPPORT_COUNT = 6, /* Keep this item at the end of the list. Tracks total number of support levels. */
|
||||
};
|
||||
|
||||
AST_LIST_HEAD_NOLOCK(support_level_bucket, member);
|
||||
|
|
|
@ -23,3 +23,4 @@ stereorize
|
|||
strcompat.c
|
||||
streamplayer
|
||||
threadstorage.c
|
||||
conf_bridge_binaural_hrir_importer
|
||||
|
|
|
@ -70,10 +70,6 @@ ifneq ($(filter pbx_ael,$(MENUSELECT_PBX)),)
|
|||
UTILS:=$(filter-out conf2ael,$(UTILS))
|
||||
endif
|
||||
|
||||
ifeq ($(SNDFILE_LIB),)
|
||||
UTILS:=$(filter-out conf_bridge_binaural_hrir_importer,$(UTILS))
|
||||
endif
|
||||
|
||||
all: $(UTILS)
|
||||
|
||||
install:
|
||||
|
@ -193,10 +189,6 @@ smsq: LIBS+=$(POPT_LIB)
|
|||
|
||||
streamplayer: streamplayer.o
|
||||
|
||||
conf_bridge_binaural_hrir_importer: LIBS+=$(SNDFILE_LIB)
|
||||
conf_bridge_binaural_hrir_importer: _ASTCFLAGS+=$(SNDFILE_INCLUDE)
|
||||
conf_bridge_binaural_hrir_importer: conf_bridge_binaural_hrir_importer.o
|
||||
|
||||
muted: muted.o
|
||||
muted: LIBS+=$(AUDIO_LIBS)
|
||||
muted: _ASTCFLAGS:=$(filter-out -Werror,$(_ASTCFLAGS))
|
||||
|
@ -213,6 +205,9 @@ astdb2bdb: LIBS+=$(SQLITE3_LIB)
|
|||
astdb2bdb: _ASTCFLAGS+=$(SQLITE3_INCLUDE)
|
||||
astdb2bdb: db1-ast/libdb1.a
|
||||
|
||||
conf_bridge_binaural_hrir_importer: LIBS+=$(SNDFILE_LIB)
|
||||
conf_bridge_binaural_hrir_importer.o: _ASTCFLAGS+=$(SNDFILE_INCLUDE)
|
||||
|
||||
ifneq ($(wildcard .*.d),)
|
||||
include .*.d
|
||||
endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2016, Digium, Inc.
|
||||
* Copyright (C) 2016, Frank Haase, Dennis Guse
|
||||
*
|
||||
* Frank Haase <fra.haase@gmail.com>
|
||||
* Dennis Guse <dennis.guse@alumni.tu-berlin.de>
|
||||
|
@ -30,6 +30,7 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sndfile.h>
|
||||
#include "conf_bridge_binaural_hrir_importer.h"
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
|
@ -99,46 +100,44 @@ int main (int argc, char **argv)
|
|||
impulse_response_index_end = (binaural_index_end + 1) * 2;
|
||||
|
||||
/* Write header */
|
||||
printf("//Used hrirs database: %s\n", hrir_filename);
|
||||
printf("//Start index in database: %d\n", impulse_response_index_start);
|
||||
printf("//End index in database: %d\n", impulse_response_index_end);
|
||||
printf(FILE_HEADER, hrir_filename, binaural_index_start, binaural_index_end);
|
||||
|
||||
printf("#define HRIRS_IMPULSE_LEN %ld\n", hrir_info.frames);
|
||||
printf("#define HRIRS_IMPULSE_SIZE %d\n", binaural_index_end - binaural_index_start + 1);
|
||||
printf("#define HRIRS_SAMPLE_RATE %d\n", hrir_info.samplerate);
|
||||
printf("#define HRIRS_SAMPLE_RATE %d\n\n", hrir_info.samplerate);
|
||||
|
||||
printf("float hrirs_left[HRIRS_IMPULSE_SIZE][HRIRS_IMPULSE_LEN] = {\n");
|
||||
for (ir_current = impulse_response_index_start; ir_current < impulse_response_index_end; ir_current += 2) {
|
||||
printf("{");
|
||||
printf("{");
|
||||
|
||||
for (j = 0; j < hrir_info.frames - 1; j++) {
|
||||
printf("%.16f,", hrir_data[ir_current * hrir_info.frames + j]);
|
||||
}
|
||||
/* Write last without trailing "," */
|
||||
printf("%.16f", hrir_data[ir_current * hrir_info.frames + hrir_info.frames - 1]);
|
||||
for (j = 0; j < hrir_info.frames - 1; j++) {
|
||||
printf("%.16f,%s", hrir_data[ir_current * hrir_info.frames + j], ((j + 1) % 4 ? " " : "\n"));
|
||||
}
|
||||
/* Write last without trailing "," */
|
||||
printf("%.16f", hrir_data[ir_current * hrir_info.frames + hrir_info.frames - 1]);
|
||||
|
||||
if (ir_current + 2 < impulse_response_index_end) {
|
||||
printf("},\n");
|
||||
} else {
|
||||
printf("}};");
|
||||
}
|
||||
if (ir_current + 2 < impulse_response_index_end) {
|
||||
printf("},\n");
|
||||
} else {
|
||||
printf("}};");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\nfloat hrirs_right[HRIRS_IMPULSE_SIZE][HRIRS_IMPULSE_LEN] = {\n");
|
||||
for (ir_current = impulse_response_index_start + 1; ir_current < impulse_response_index_end + 1; ir_current += 2) {
|
||||
printf("{");
|
||||
printf("{");
|
||||
|
||||
for (j = 0; j < hrir_info.frames - 1; j++) {
|
||||
printf("%.16f,", hrir_data[ir_current * hrir_info.frames + j]);
|
||||
}
|
||||
/* Write last without trailing "," */
|
||||
printf("%.16f", hrir_data[ir_current * hrir_info.frames + hrir_info.frames - 1]);
|
||||
for (j = 0; j < hrir_info.frames - 1; j++) {
|
||||
printf("%.16f,%s", hrir_data[ir_current * hrir_info.frames + j], ((j + 1) % 4 ? " " : "\n"));
|
||||
}
|
||||
/* Write last without trailing "," */
|
||||
printf("%.16f", hrir_data[ir_current * hrir_info.frames + hrir_info.frames - 1]);
|
||||
|
||||
if (ir_current + 2 < impulse_response_index_end) {
|
||||
printf("},\n");
|
||||
} else {
|
||||
printf("}};");
|
||||
}
|
||||
if (ir_current + 2 < impulse_response_index_end) {
|
||||
printf("},\n");
|
||||
} else {
|
||||
printf("}};");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "INFO: Successfully converted: imported %d impulse responses.\n", impulse_response_index_end - impulse_response_index_start);
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#define FILE_HEADER "\
|
||||
* Asterisk -- An open source telephony toolkit.\n\
|
||||
*\n\
|
||||
* Copyright (C) 2016, Frank Haase, Dennis Guse\n\
|
||||
*\n\
|
||||
* Frank Haase <fra.haase@gmail.com>\n\
|
||||
* Dennis Guse <dennis.guse@alumni.tu-berlin.de>\n\
|
||||
*\n\
|
||||
* See http://www.asterisk.org for more information about\n\
|
||||
* the Asterisk project. Please do not directly contact\n\
|
||||
* any of the maintainers of this project for assistance;\n\
|
||||
* the project provides a web site, mailing lists and IRC\n\
|
||||
* channels for your use.\n\
|
||||
*\n\
|
||||
* Copyright (c) 2001 The Regents of the University of California. All Rights Reserved.\n\
|
||||
*\n\
|
||||
* The HRIRs used here are obtained from The CIPIC HRTF Database\n\
|
||||
* (http://interface.cipic.ucdavis.edu/CIL_html/CIL_HRTF_database.htm)\n\
|
||||
* Note that the above mentioned material is Copyright (c) 2001 The\n\
|
||||
* Regents of the University of California. All Rights Reserved.\n\
|
||||
*\n\
|
||||
* Download the file\n\
|
||||
* http://interface.cipic.ucdavis.edu/data/special_kemar_hrir.tar and\n\
|
||||
* uncompress it in the folder where this Matlab script resides. Finally,\n\
|
||||
* run the script.\n\
|
||||
*\n\
|
||||
* This program is free software, distributed under the terms of\n\
|
||||
* the GNU General Public License Version 2. See the LICENSE file\n\
|
||||
* at the top of the source tree.\n\
|
||||
*/\n\
|
||||
\n\
|
||||
/*! \\file\n\
|
||||
*\n\
|
||||
* \\brief Multi-party software binaural channel HRIRS\n\
|
||||
*\n\
|
||||
* \\author Frank Haase <fra.haase@googlemail.com>\n\
|
||||
* \\author Dennis Guse <dennis.guse@alumni.tu-berlin.de>\n\
|
||||
*\n\
|
||||
* \\ingroup bridges\n\
|
||||
*/\n\
|
||||
\n\
|
||||
/*\n\
|
||||
* This file was created with command:\n\
|
||||
* $ conf_bridge_binaural_hrir_importer %s %d %d\n\
|
||||
*/\n\
|
||||
"
|
|
@ -20,11 +20,6 @@
|
|||
<depend>newt</depend>
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="conf_bridge_binaural_hrir_importer">
|
||||
<defaultenabled>no</defaultenabled>
|
||||
<depend>sndfile</depend>
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="check_expr">
|
||||
<defaultenabled>no</defaultenabled>
|
||||
<support_level>extended</support_level>
|
||||
|
@ -54,4 +49,12 @@
|
|||
<defaultenabled>no</defaultenabled>
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="conf_bridge_binaural_hrir_importer"
|
||||
displayname="Impulse Noise wav to hrirs.h generator"
|
||||
remove_on_change="conf_bridge_binaural_hrir_importer">
|
||||
<defaultenabled>no</defaultenabled>
|
||||
<depend>sndfile</depend>
|
||||
<support_level>extended</support_level>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
</member>
|
||||
</category>
|
||||
|
|
Loading…
Reference in New Issue