summaryrefslogtreecommitdiff
path: root/bridges/bridge_softmix/bridge_softmix_binaural.c
blob: 128e4640e6cd48d6ba501e94f849592451429e9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
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];
	}
}