/*
 * FILE:    transmit.c
 * PROGRAM: RAT
 * AUTHOR:  Isidor Kouvelas + Colin Perkins
 * 
 * $Revision: 1.1.1.1 $
 * $Date: 1998/08/15 19:16:46 $
 *
 * Copyright (c) 1995,1996,1997 University College London
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, is permitted, for non-commercial use only, provided
 * that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Computer Science
 *      Department at University College London
 * 4. Neither the name of the University nor of the Department may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 * Use of this software for commercial purposes is explicitly forbidden
 * unless prior written permission is obtained from the authors.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "bat_include.h"

#define FIX_PT(pt)	((pt) == L16? sp->l16_pt: (pt))

#define POST_HANG_LENGTH 8	/* 160ms */
#define FILTER_LENGTH 	 2

tx_unit *
get_unit(session_struct *sp, int pos)
{
	assert(((pos) / SAMPLES_PER_UNIT) < sp->rb.unit_buf_size); /* ...else we've overrun the memory allocated! */
	return sp->rb.unit_buf + ((pos) / SAMPLES_PER_UNIT);
}

/*
 * Reset buffer and use len for the new size of the buffer. The length is
 * effectively the amount of coded history we want to maintain.
 * The length has to be greater than the DEVICE_REC_BUF.
 */
#define READ_BUF_LEN	DEVICE_REC_BUF	/* Two seconds should do */

void
read_device_init(session_struct *sp)
{
	int l, units, len;

	len = READ_BUF_LEN;

	if (len < DEVICE_REC_BUF)
		len = DEVICE_REC_BUF;

	if (sp->rb.read_buf != NULL) {
		xfree(sp->rb.read_buf);
		xfree(sp->rb.unit_buf);
	}

	/* Round it up to the nearest multiple of packets */
	if ((l = len % (sp->units_per_pckt * SAMPLES_PER_UNIT)) != 0) {
		len += (sp->units_per_pckt * SAMPLES_PER_UNIT) - l;
	}

	/* Allocate twice as much space so that we do not split reads */
	sp->rb.read_buf      = (sample *)xmalloc(2 * len * BYTES_PER_SAMPLE);
	sp->rb.read_buf_len  = len;
	sp->rb.read_buf_dist = 0;
	sp->rb.read_buf_head = 0;
	sp->rb.read_buf_tail = 0;

	units = len / SAMPLES_PER_UNIT;
	sp->rb.unit_buf_size = units;
	sp->rb.unit_buf      = (tx_unit*)xmalloc(units * sizeof(tx_unit));
	memset(sp->rb.unit_buf, 0, units * sizeof(tx_unit));
	/* Create doubly linked list */
	for (l = 0; l < units; l++) {
		(sp->rb.unit_buf + l)->next = sp->rb.unit_buf + l + 1;
		(sp->rb.unit_buf + l)->prev = sp->rb.unit_buf + l - 1;
	}
	sp->rb.unit_buf->prev = sp->rb.unit_buf + units - 1;
	(sp->rb.unit_buf + units - 1)->next = sp->rb.unit_buf;
	sp->rb.silence_ptr = sp->rb.tx_ptr = sp->rb.unit_buf;

	if (sp->sd_info == NULL) {
		sp->sd_info = sd_init();
	}

	if (sp->mode != TRANSCODER) {
		audio_drain(sp->audio_fd);
	}
}

static void
free_coded_unit(coded_unit *u)
{
	assert(u->coding_type >= 0);
#ifdef NDEF	/* This is invalid now Isidor's changed the L16 codec type? */
	assert(u->coding_type <= 128);
#endif
	if (u->state) {
		block_free(u->state, u->state_len + u->data_len);
		u->state     = NULL;
		u->state_len = 0;
		u->data      = NULL;
		u->data_len  = 0;
	} 
	if (u->data) {
		block_free(u->data, u->data_len);
		u->data     = NULL;
		u->data_len = 0;
	}
	u->coding_type = -1;
}

static void
clear_tx_unit(tx_unit *u)
{
	int l;

	for (l = 0; l < MAX_ENCODINGS; l++) {
		if (u->coded[l].data != NULL) {
			free_coded_unit(u->coded + l);
		}
	}

	u->talkspurt_start = FALSE;
	u->send = FALSE;
	u->data = NULL;			/* Mark as clear */
}

void
transmit_audit(session_struct *sp)
{
	int	units, l;
	tx_unit	*u;

	units = sp->rb.read_buf_len / SAMPLES_PER_UNIT;
	for (l = 0, u = sp->rb.unit_buf; l < units; l++) {
		if (u->data) {
			clear_tx_unit(u);
		}
	}
	sp->transmit_audit_required = FALSE;
}

/* These routines are called when the button on the interface is toggled */
void
start_sending(session_struct *sp)
{
	if (sp->sending_audio)
		return;

	if (sp->transmit_audit_required)
		transmit_audit(sp);

	sp->sending_audio = TRUE;
	sp->rb.read_buf_dist = 0;
	sp->rb.read_buf_head = 0;
	sp->rb.read_buf_tail = 0;
	sp->rb.silence_ptr = sp->rb.tx_ptr = sp->rb.unit_buf;
}

void
stop_sending(session_struct *sp)
{
	if (sp->sending_audio == FALSE)
		return;
	sp->sending_audio = FALSE;
	sp->transmit_audit_required = TRUE;
	sp->rb.talkspurt = FALSE;
	ui_input_level(0);
	ui_info_deactivate_us();
}

int
read_device(session_struct *sp, u_int32 *cur_time)
{
	/* Read from the audio device into the circular buffer
	 * pointed to by read_buf
	 * Update head, tail, dist whilst we're at it...
	 */

	int read_len, extra = 0, units = 0;
	tx_unit *u;

	read_len = audio_device_read(sp, sp->rb.read_buf + sp->rb.read_buf_head, sp->rb.read_buf_len);
        if ((read_len>0) && (sp->bc.active)) {
                audio_unbias(&sp->bc, sp->rb.read_buf + sp->rb.read_buf_head, read_len);
        }
	assert(read_len >= 0);
	assert(read_len <= sp->rb.read_buf_len);

        if ((sp->mode == FLAKEAWAY) && (sp->flake_go>0)) {
                sp->flake_go -= read_len;
                if (sp->flake_go < 0) {
                        sp->flake_go = 0;
                }
        }
        
	if ((sp->in_file) && sp->flake_go == 0) {
		if (fread(sp->rb.read_buf + sp->rb.read_buf_head, BYTES_PER_SAMPLE, read_len, sp->in_file) < read_len) {
			fclose(sp->in_file);
			sp->in_file = NULL;
		}
                sp->flake_os += read_len;
	}

	/* Wrap around in the circular buffer when we run out of space. Note that this is not */
	/* a problem since the buffer was allocated to be twice the maximum read size!  [csp] */
	if (read_len > sp->rb.read_buf_len - sp->rb.read_buf_head) {
		memcpy(sp->rb.read_buf, sp->rb.read_buf + sp->rb.read_buf_len, (read_len + sp->rb.read_buf_head - sp->rb.read_buf_len) * BYTES_PER_SAMPLE);
	}

	/* If the following condition is true, we've overrun the space available in the read buffer.    [csp] */
	/* We calculate "units" to be the number of 20ms units by which we've overrun the buffer, rounded up. */
	if (read_len + sp->rb.read_buf_dist > sp->rb.read_buf_len) {
		units = ((sp->rb.read_buf_dist + read_len) - sp->rb.read_buf_len) / SAMPLES_PER_UNIT;
		if ((extra = sp->rb.read_buf_head % SAMPLES_PER_UNIT) != 0) {
			units++;
		}
	}

	sp->rb.read_buf_head += read_len;
	if (sp->rb.read_buf_head >= sp->rb.read_buf_len) {
		sp->rb.read_buf_head -= sp->rb.read_buf_len;
	}

	/* If we overran the read buffer, we clear the units which were overwritten. [csp] */
	if (units > 0) {
		assert(sp->rb.read_buf_tail >= 0);
		assert(sp->rb.read_buf_tail < sp->rb.read_buf_len);
		u = get_unit(sp, sp->rb.read_buf_tail);
		/* Sanity check the unit we've just been given... [csp] */
		assert((u->next == (u + 1)) || (u->next == sp->rb.unit_buf));
		assert((u->prev == (u - 1)) || (u->prev == sp->rb.unit_buf + sp->rb.unit_buf_size - 1));
		assert((u->data == NULL) || ((u->data >= sp->rb.read_buf) && (u->data <= (sp->rb.read_buf + (sp->rb.read_buf_len * 2)))));
		/* Now, process the units... */
		for (; units > 0; units--) {
			clear_tx_unit(u);
			u = u->next;
		}
		if (extra > 0) {
			sp->rb.read_buf_tail = sp->rb.read_buf_head + SAMPLES_PER_UNIT - extra;
			sp->rb.read_buf_dist = sp->rb.read_buf_len  - SAMPLES_PER_UNIT + extra;
		} else {
			sp->rb.read_buf_tail = sp->rb.read_buf_head;
			sp->rb.read_buf_dist = sp->rb.read_buf_len;
		}
		if (sp->rb.read_buf_tail >= sp->rb.read_buf_len) {
			sp->rb.read_buf_tail -= sp->rb.read_buf_len;
		}
		assert(sp->rb.read_buf_tail >= 0);
		assert(sp->rb.read_buf_tail < sp->rb.read_buf_len);
	} else {
		sp->rb.read_buf_dist += read_len;
	}

	(*cur_time) += read_len;
	return read_len;
}

int
process_read_audio(session_struct *sp, speaker_table *st)
{
	sample		*bp;
	tx_unit		*u;
	speaker_table	*current_speaker;
	int		 i;

	if (sp->rb.read_buf_dist < SAMPLES_PER_UNIT)
		return (FALSE);

	/* If there is a complete unit... */
	while (sp->rb.read_buf_dist >= SAMPLES_PER_UNIT) {
		bp = sp->rb.read_buf + sp->rb.read_buf_tail;
		if (sp->rb.read_buf_tail + SAMPLES_PER_UNIT > sp->rb.read_buf_len) {
			memcpy(sp->rb.read_buf + sp->rb.read_buf_len, sp->rb.read_buf, (SAMPLES_PER_UNIT + sp->rb.read_buf_tail - sp->rb.read_buf_len)*BYTES_PER_SAMPLE);
		}

		u = get_unit(sp, sp->rb.read_buf_tail);
		if (u->next == sp->rb.tx_ptr) {
			sp->rb.tx_ptr = sp->rb.tx_ptr->next;
			if (u->next == sp->rb.silence_ptr) {
				sp->rb.silence_ptr = sp->rb.silence_ptr->next;
			}
		}
		if (u->data) {
			clear_tx_unit(u);
		}
		u->data = bp;
		u->time = sp->cur_time - sp->rb.read_buf_dist;

		/* Set active speakers for this unit... */
		for (i=1; i<16; i++) {
			u->dbe_source[i] = NULL;
		}
		u->dbe_source[0]    = sp->db.my_dbe; assert(sp->db.my_dbe != NULL);
		u->dbe_source_count = 1;
		if (st != NULL) {
			for (current_speaker = st; current_speaker != NULL; current_speaker = current_speaker->next) {
				/* 2 is a magic number, WHITE in speaker_table.c */
				if (current_speaker->state == 2) {
					u->dbe_source[u->dbe_source_count++] = current_speaker->dbe;
				}
				if (u->dbe_source_count > 16) break;
			}
		}
		if (sp->mode != TRANSCODER) assert(u->dbe_source_count == 1);

		/* Do first pass of silence supression */
		u->energy = audio_energy(bp, SAMPLES_PER_UNIT);
		if (sp->detect_silence) {
			u->silence = sd(sp->sd_info, u->energy, FALSE);
		} else {
			u->silence = FALSE;
		}
		/* Automatic Gain Control... */
#ifdef NDEF
		agc_table_update(u->energy);
#endif

		sp->rb.read_buf_dist -= SAMPLES_PER_UNIT;
		sp->rb.read_buf_tail += SAMPLES_PER_UNIT;
		if (sp->rb.read_buf_tail >= sp->rb.read_buf_len) {
			sp->rb.read_buf_tail -= sp->rb.read_buf_len;
		}
		assert(sp->rb.read_buf_tail >= 0);
		assert(sp->rb.read_buf_tail < sp->rb.read_buf_len);
	}
	return TRUE;
}

#define PREHANG		3
#define POSTHANG	8
#define SIGNIFICANT	3

/*
 * For the time being this only does small changes and not posthang...
 */
void
rules_based_silence(session_struct *sp)
{
	tx_unit	*tail, *u;
	int	change, i;

	tail = get_unit(sp, sp->rb.read_buf_tail);
	while (sp->rb.silence_ptr != tail) {
		if (sp->rb.silence_ptr->silence == sp->rb.talkspurt) {
			if (sp->rb.talkspurt) {
				if (++sp->rb.posthang > POSTHANG) {
					sp->rb.talkspurt = FALSE;
					sp->rb.posthang = 0;
				}
			} else {
				u = sp->rb.silence_ptr;
				for (change = 0; u != tail
				     && u->silence == FALSE
				     && change < SIGNIFICANT;) {
					change++;
					u = u->next;
				}
				if (change == SIGNIFICANT) {
					sp->rb.talkspurt = TRUE;
					u = sp->rb.silence_ptr;
					for (i = 0; i < PREHANG && u != sp->rb.tx_ptr; i++) {
						u = u->prev;
						u->send = TRUE;
					}
					u->talkspurt_start = TRUE;
				} else if (u == tail)
					break;
			}
		} else {
			if (sp->rb.posthang > 0)
				sp->rb.posthang--;
		}
		sp->rb.silence_ptr->send = sp->rb.talkspurt;
		sp->rb.silence_ptr = sp->rb.silence_ptr->next;
	}
}

static coded_unit *
get_coded_unit(session_struct *sp, tx_unit *u, int coding)
{
	coded_unit *c;
	int l;

	/* Find the first free coded_unit, and code into it! */
	for (l = 0, c = u->coded; l < MAX_ENCODINGS && c->data != NULL; l++, c++) {
		if (c->coding_type == coding) {
			break;
		}
	}
	assert(l != MAX_ENCODINGS);	/* ...else we've run out of space to add units into! */
	if (c->data == NULL) {
		encoder(sp, u->data, coding, c);
	}
	return c;
}

static int
fill_one_level(session_struct *sp, tx_unit *u, int coding, int max_units, struct iovec *iovp, int *len)
{
	int i, iovc = 0;
	coded_unit *c;

	*len = 0;
	for (i = 0; i < max_units; i++, u = u->next) {
		if (sp->rb.tx_ptr->send == FALSE)
			break;
		c = get_coded_unit(sp, u, coding);
		if (i == 0 && c->state != NULL) {
			iovp[iovc].iov_base = (caddr_t)c->state;
			*len += iovp[iovc].iov_len = c->state_len;
			iovc++;
		}
		iovp[iovc].iov_base = (caddr_t)c->data;
		*len += iovp[iovc].iov_len = c->data_len;
		iovc++;
	}

	return (iovc);
}

void
compress_transmit_audio(session_struct *sp)
{
	int		units, red, i, blocks, iovc, *pt, len, l, j, k, found;
	tx_unit		*u, *v;
	rtp_hdr_t	rtp_header;
	u_int32		red_hdr[MAX_ENCODINGS], tmph;
	struct iovec	send_ptrs[MAX_ENCODINGS * 8 + 2];

	rtp_header.type = 2;
	rtp_header.x    = 0;
	rtp_header.p    = 0;
	rtp_header.cc   = 0;
	rtp_header.ssrc = htonl(rtcp_myssrc(sp));

	units = 0;
	u = sp->rb.tx_ptr;
	while (u != sp->rb.silence_ptr) {
		units++;
		u = u->next;
	}

	while (units > sp->units_per_pckt) {
		if (sp->rb.tx_ptr->send == FALSE) {
			sp->rb.tx_ptr = sp->rb.tx_ptr->next;
			units--;
			continue;
		}

		u = sp->rb.tx_ptr;
		i = sp->units_per_pckt * (sp->num_encodings - 1);
		red = 0;
		if (i > 0) {
			for (; red < i && u->prev->send == TRUE; red++)
				u = u->prev;

			/* Round to the nearest whole packet */
			i = red % sp->units_per_pckt;
			red -= i;
			for (; i > 0; i--)
				u = u->next;
		}

		send_ptrs->iov_base = (caddr_t)&rtp_header;
		send_ptrs->iov_len = 12;
		iovc = 1;

		pt = sp->encodings + red / sp->units_per_pckt;
		if (red > 0) {
			rtp_header.pt = sp->redundancy_pt;
			send_ptrs[1].iov_base = (caddr_t)red_hdr;
			iovc++;
			blocks = 0;
			for (i = red; i > 0; i -= sp->units_per_pckt) {
				iovc += fill_one_level(sp, u, *pt, sp->units_per_pckt, send_ptrs + iovc, &len);
				tmph = len;
				tmph |= (i * SAMPLES_PER_UNIT) << 10;
				tmph |= (0x80 | FIX_PT(*pt)) << 24;
				red_hdr[blocks++] = htonl(tmph);
				pt--;
				for (l = 0; l < sp->units_per_pckt; l++)
					u = u->next;
			}
			(*(char *)(red_hdr + blocks)) = FIX_PT(*pt);
			send_ptrs[1].iov_len = blocks * 4 + 1;
		} else {
			rtp_header.pt = FIX_PT(*pt);
		}
		rtp_header.m = u->talkspurt_start;
		rtp_header.seq = htons(sp->rtp_seq);
		sp->rtp_seq++;
		rtp_header.ts = htonl(u->time);

		/* Fill in CSRC information... */
		v = u;
		for (i=0; i<sp->units_per_pckt; v=v->next, i++) {
			for (j=1; j<v->dbe_source_count; j++) {
				/* Add CSRC, unless it's already been added, or is our SSRC */
				if (v->dbe_source[j]->ssrc == rtp_header.ssrc) {
					break;
				}
				found = FALSE;
				for (k=0; k<rtp_header.cc; k++) {
					if (rtp_header.csrc[k] == v->dbe_source[j]->ssrc) found = TRUE;
				}
				if (!found) {
					rtp_header.csrc[rtp_header.cc++] = htonl(v->dbe_source[j]->ssrc);
				}
			}
		}
		send_ptrs->iov_len = 12 + (rtp_header.cc * 4);

		iovc += fill_one_level(sp, u, *pt, sp->units_per_pckt, send_ptrs + iovc, &len);
                if (sp->drop != 0.0) {
                        if (drand48() >= sp->drop) {
                                net_write_iov(sp->rtp_fd, sp->net_maddress, sp->rtp_port, send_ptrs, iovc, PACKET_RTP);
                        }
                } else {
                        net_write_iov(sp->rtp_fd, sp->net_maddress, sp->rtp_port, send_ptrs, iovc, PACKET_RTP);
                }
		/* Update RTP/RTCP statistics... [csp] */
		sp->db.pkt_count  += 1;
		sp->db.byte_count += len;
		sp->db.sending     = TRUE;

		units -= sp->units_per_pckt;
		for (i = 0; i < sp->units_per_pckt; i++)
			sp->rb.tx_ptr = sp->rb.tx_ptr->next;
	}
}

void
service_transmitter(session_struct *sp)
{
	/*
	 * For load adaption etc we can modify these functions to do only
	 * one unit at a time and look at the rb pointers to figure out if
	 * any need servicing... [ik]
	 */
	rules_based_silence(sp);
	compress_transmit_audio(sp);
}
