+/*
+ * recording function
+ * Records all data from down and from up into one single stream.
+ * Both streams may have gaps or jitter.
+ * A Jitter buffer for both streams is used to compensate jitter.
+ *
+ * If one stream (dir) received packets, they are stored to a
+ * buffer to wait for the other stream (dir), so both streams can
+ * be combined. If the buffer is full, it's content is written
+ * without mixing stream. (assuming only one stram (dir) exists.)
+ * A flag is used to indicate what stream is currently in buffer.
+ *
+ * NOTE: First stereo sample (odd) is from down, second is from up.
+ */
+void Port::record(unsigned char *data, int length, int dir_fromup)
+{
+ unsigned char write_buffer[1024], *d;
+ signed short *s;
+ int free, i, ii;
+ signed int sample;
+ int ret;
+
+ /* no recording */
+ if (!p_record || !length)
+ return;
+
+ /* skip data from local caller (dtmf input) */
+ if (p_record_skip && !dir_fromup) {
+ /* more to skip than we have */
+ if (p_record_skip > length) {
+ p_record_skip -= length;
+ return;
+ }
+ /* less to skip */
+ data += p_record_skip;
+ length -= p_record_skip;
+ p_record_skip = 0;
+ }
+
+//printf("dir=%d len=%d\n", dir_fromup, length);
+
+ free = ((p_record_buffer_readp - p_record_buffer_writep - 1) & RECORD_BUFFER_MASK);
+
+//PDEBUG(DEBUG_PORT, "record(data,%d,%d): free=%d, p_record_buffer_dir=%d, p_record_buffer_readp=%d, p_record_buffer_writep=%d.\n", length, dir_fromup, free, p_record_buffer_dir, p_record_buffer_readp, p_record_buffer_writep);
+
+ /* the buffer stores the same data stream */
+ if (dir_fromup == p_record_buffer_dir) {
+same_again:
+
+//printf("same free=%d length=%d\n", free, length);
+ /* first write what we can to the buffer */
+ while(free && length) {
+ p_record_buffer[p_record_buffer_writep] = audio_law_to_s32[*data++];
+ p_record_buffer_writep = (p_record_buffer_writep + 1) & RECORD_BUFFER_MASK;
+ free--;
+ length--;
+ }
+ /* all written, so we return */
+ if (!length)
+ return;
+ /* still data left, buffer is full, so we need to write a chunk to file */
+ switch(p_record_type) {
+ case CODEC_MONO:
+ s = (signed short *)write_buffer;
+ i = 0;
+ while(i < 256) {
+ *s++ = p_record_buffer[p_record_buffer_readp];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ i++;
+ }
+ ret = fwrite(write_buffer, 512, 1, p_record);
+ p_record_length += 512;
+ break;
+
+ case CODEC_STEREO:
+ s = (signed short *)write_buffer;
+ if (p_record_buffer_dir) {
+ i = 0;
+ while(i < 256) {
+ *s++ = 0; /* nothing from down */
+ *s++ = p_record_buffer[p_record_buffer_readp];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ i++;
+ }
+ } else {
+ i = 0;
+ while(i < 256) {
+ *s++ = p_record_buffer[p_record_buffer_readp];
+ *s++ = 0; /* nothing from up */
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ i++;
+ }
+ }
+ ret = fwrite(write_buffer, 1024, 1, p_record);
+ p_record_length += 1024;
+ break;
+
+ case CODEC_8BIT:
+ d = write_buffer;
+ i = 0;
+ while(i < 256) {
+ *d++ = ((unsigned short)(p_record_buffer[p_record_buffer_readp]+0x8000)) >> 8;
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ i++;
+ }
+ ret = fwrite(write_buffer, 512, 1, p_record);
+ p_record_length += 512;
+ break;
+
+ case CODEC_LAW:
+ d = write_buffer;
+ i = 0;
+ while(i < 256) {
+ *d++ = audio_s16_to_law[p_record_buffer[p_record_buffer_readp] & 0xffff];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ i++;
+ }
+ ret = fwrite(write_buffer, 256, 1, p_record);
+ p_record_length += 256;
+ break;
+ }
+ /* because we still have data, we write again */
+ free += 256;
+ goto same_again;
+ }
+ /* the buffer stores the other stream */
+
+different_again:
+ /* if buffer empty, change it */
+ if (p_record_buffer_readp == p_record_buffer_writep) {
+ p_record_buffer_dir = dir_fromup;
+ goto same_again;
+ }
+ /* how much data can we mix ? */
+ ii = (p_record_buffer_writep - p_record_buffer_readp) & RECORD_BUFFER_MASK;
+ if (length < ii)
+ ii = length;
+
+ if (ii > 256)
+ ii = 256;
+//printf("same ii=%d length=%d\n", ii, length);
+//PDEBUG(DEBUG_PORT, "record(data,%d,%d): free=%d, p_record_buffer_dir=%d, p_record_buffer_readp=%d, p_record_buffer_writep=%d: mixing %d bytes.\n", length, dir_fromup, free, p_record_buffer_dir, p_record_buffer_readp, p_record_buffer_writep, ii);
+
+ /* write data mixed with the buffer */
+ switch(p_record_type) {
+ case CODEC_MONO:
+ s = (signed short *)write_buffer;
+ i = 0;
+ while(i < ii) {
+ sample = p_record_buffer[p_record_buffer_readp]
+ + audio_law_to_s32[*data++];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ if (sample < SHORT_MIN) sample = SHORT_MIN;
+ if (sample > SHORT_MAX) sample = SHORT_MAX;
+ *s++ = sample;
+ i++;
+ }
+ ret = fwrite(write_buffer, ii<<1, 1, p_record);
+ p_record_length += (ii<<1);
+ break;
+
+ case CODEC_STEREO:
+ s = (signed short *)write_buffer;
+ if (p_record_buffer_dir) {
+ i = 0;
+ while(i < ii) {
+ *s++ = audio_law_to_s32[*data++];
+ *s++ = p_record_buffer[p_record_buffer_readp];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ i++;
+ }
+ } else {
+ i = 0;
+ while(i < ii) {
+ *s++ = p_record_buffer[p_record_buffer_readp];
+ *s++ = audio_law_to_s32[*data++];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ i++;
+ }
+ }
+ ret = fwrite(write_buffer, ii<<2, 1, p_record);
+ p_record_length += (ii<<2);
+ break;
+
+ case CODEC_8BIT:
+ d = write_buffer;
+ i = 0;
+ while(i < ii) {
+ sample = p_record_buffer[p_record_buffer_readp]
+ + audio_law_to_s32[*data++];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ if (sample < SHORT_MIN) sample = SHORT_MIN;
+ if (sample > SHORT_MAX) sample = SHORT_MAX;
+ *d++ = (sample+0x8000) >> 8;
+ i++;
+ }
+ ret = fwrite(write_buffer, ii, 1, p_record);
+ p_record_length += ii;
+ break;
+
+ case CODEC_LAW:
+ d = write_buffer;
+ i = 0;
+ while(i < ii) {
+ sample = p_record_buffer[p_record_buffer_readp]
+ + audio_law_to_s32[*data++];
+ p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK;
+ if (sample < SHORT_MIN) sample = SHORT_MIN;
+ if (sample > SHORT_MAX) sample = SHORT_MAX;
+ *d++ = audio_s16_to_law[sample & 0xffff];
+ i++;
+ }
+ ret = fwrite(write_buffer, ii, 1, p_record);
+ p_record_length += ii;
+ break;
+ }
+ length -= ii;
+ /* still data */
+ if (length)
+ goto different_again;
+ /* no data (maybe buffer) */
+ return;
+
+}
+
+void Port::update_rxoff(void)
+{
+}
+
+void Port::update_load(void)
+{
+}
+
+
+/*
+ * bridge handling
+ */
+
+static void remove_bridge(struct port_bridge *bridge, class Port *port)
+{
+ struct port_bridge **temp = &p_bridge_first;
+ while (*temp) {
+ if (*temp == bridge) {
+ int remove = 0;
+
+ /* Remove us from bridge. If bridge is empty, remove it completely. */
+ if (bridge->sunrise == port) {
+ bridge->sunrise = NULL;
+ if (!bridge->sunset)
+ remove = 1;
+ }
+ if (bridge->sunset == port) {
+ bridge->sunset = NULL;
+ if (!bridge->sunrise)
+ remove = 1;
+ }
+ if (remove) {
+ PDEBUG(DEBUG_PORT, "Remove bridge %u\n", bridge->bridge_id);
+ *temp = bridge->next;
+ FREE(bridge, sizeof(struct port_bridge));
+ memuse--;
+ }
+ return;
+ }
+ temp = &((*temp)->next);
+ }
+ PERROR("Bridge %p not found in list\n", bridge);
+}
+
+void Port::bridge(unsigned int bridge_id)
+{
+ /* Remove bridge, if we leave bridge or if we join a different bridge. */
+ if (p_bridge && bridge_id != p_bridge->bridge_id) {
+ PDEBUG(DEBUG_PORT, "Remove port %u from bridge %u, because out new bridge is %u\n", p_serial, p_bridge->bridge_id, bridge_id);
+ remove_bridge(p_bridge, this);
+ p_bridge = NULL;
+ }
+
+ /* if we leave bridge */
+ if (!bridge_id)
+ return;
+
+ /* find bridge */
+ if (!p_bridge) {
+ struct port_bridge *temp = p_bridge_first;
+
+ while (temp) {
+ if (temp->bridge_id == bridge_id)
+ break;
+ temp = temp->next;
+ }
+ p_bridge = temp;
+ if (p_bridge)
+ PDEBUG(DEBUG_PORT, "Port %d found existing bridge %u.\n", p_serial, p_bridge->bridge_id);
+ }
+
+ /* create bridge */
+ if (!p_bridge) {
+ struct port_bridge **temp = &p_bridge_first;
+
+ p_bridge = (struct port_bridge *) MALLOC(sizeof(struct port_bridge));
+ memuse++;
+ p_bridge->bridge_id = bridge_id;
+ p_bridge->sunrise = this;
+
+ /* attach bridge instance to list */
+ while (*temp)
+ temp = &((*temp)->next);
+ *temp = p_bridge;
+ PDEBUG(DEBUG_PORT, "Port %d creating not existing bridge %u.\n", p_serial, p_bridge->bridge_id);
+ }
+
+ /* already joined */
+ if (p_bridge->sunrise == this || p_bridge->sunset == this)
+ return;
+
+ /* join bridge */
+ if (!p_bridge->sunrise) {
+ p_bridge->sunrise = this;
+ return;
+ }
+ if (!p_bridge->sunset) {
+ p_bridge->sunset = this;
+ return;
+ }
+
+ PERROR("Bridge ID %u cannot be joined by port %u, because it is already occupied by ports %u and %u.\n", p_bridge->bridge_id, p_serial, p_bridge->sunrise->p_serial, p_bridge->sunset->p_serial);
+ p_bridge = NULL;
+}
+
+class Port *Port::bridge_remote(void)
+{
+ class Port *remote = NULL;
+
+ /* get remote port from bridge */
+ if (!p_bridge)
+ return NULL;
+ if (p_bridge->sunrise == this)
+ remote = p_bridge->sunset;
+ if (p_bridge->sunset == this)
+ remote = p_bridge->sunrise;
+
+ return remote;
+}
+
+/* send data to remote Port */
+int Port::bridge_tx(unsigned char *data, int len)
+{
+ class Port *remote = bridge_remote();
+
+ if (!remote)
+ return -EINVAL;
+
+// printf("Traffic: %u -> %u (bridge %u)\n", p_serial, remote->p_serial, p_bridge->bridge_id);
+ return remote->bridge_rx(data, len);
+}
+
+/* receive data from remote Port (dummy, needs to be inherited) */
+int Port::bridge_rx(unsigned char *data, int len)
+{
+ return 0; /* datenklo */
+}