Index: pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/conf_switch.c
===================================================================
--- pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/conf_switch.c	(revision 2444)
+++ pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/conf_switch.c	(revision 2445)
@@ -52,4 +52,6 @@
 #define INVALID_SLOT	    ((SLOT_TYPE)-1)
 #define BUFFER_SIZE	    PJMEDIA_MAX_MTU
+#define MAX_LEVEL	    (32767)
+#define MIN_LEVEL	    (-32768)
 
 /*
@@ -153,4 +155,8 @@
     conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
 
+    /* Default level adjustment is 128 (which means no adjustment) */
+    conf_port->tx_adj_level = NORMAL_LEVEL;
+    conf_port->rx_adj_level = NORMAL_LEVEL;
+
     /* Create transmit flag array */
     conf_port->listener_slots = (SLOT_TYPE*)
@@ -166,5 +172,4 @@
     f = (pjmedia_frame*)conf_port->tx_buf;
     f->buf = conf_port->tx_buf + sizeof(pjmedia_frame);
-    f->size = 0;
 
     /* Done */
@@ -673,4 +678,6 @@
 
     if (i != src_port->listener_cnt) {
+	pjmedia_frame_ext *f;
+
 	pj_assert(src_port->listener_cnt > 0 && 
 		  src_port->listener_cnt < conf->max_ports);
@@ -683,6 +690,10 @@
 	--dst_port->transmitter_cnt;
 	
-	/* Clean up sink TX buffer. */
-	pj_bzero(dst_port->tx_buf, sizeof(pjmedia_frame_ext));
+	/* Cleanup listener TX buffer. */
+	f = (pjmedia_frame_ext*)dst_port->tx_buf;
+	f->base.type = PJMEDIA_FRAME_TYPE_NONE;
+	f->base.size = 0;
+	f->samples_cnt = 0;
+	f->subframe_cnt = 0;
 
 	PJ_LOG(4,(THIS_FILE,
@@ -777,4 +788,5 @@
 	unsigned dst_slot;
 	struct conf_port *dst_port;
+	pjmedia_frame_ext *f;
 
 	dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1];
@@ -785,6 +797,10 @@
 	--conf->connect_cnt;
 
-	/* Clean up TX buffer. */
-	pj_bzero(dst_port->tx_buf, sizeof(pjmedia_frame_ext));
+	/* Cleanup & reinit listener TX buffer. */
+	f = (pjmedia_frame_ext*)dst_port->tx_buf;
+	f->base.type = PJMEDIA_FRAME_TYPE_NONE;
+	f->base.size = 0;
+	f->samples_cnt = 0;
+	f->subframe_cnt = 0;
     }
 
@@ -857,4 +873,6 @@
     info->bits_per_sample = conf_port->info->bits_per_sample;
     info->format = conf_port->port->info.format;
+    info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
+    info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
 
     return PJ_SUCCESS;
@@ -935,4 +953,9 @@
     conf_port = conf->ports[slot];
 
+    /* Level adjustment is applicable only for ports that work with raw PCM. */
+    PJ_ASSERT_RETURN(conf_port->info->format.u32 == 0 || 
+		     conf_port->info->format.u32 == PJMEDIA_FOURCC_L16,
+		     PJ_EIGNORED);
+
     /* Set normalized adjustment level. */
     conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
@@ -964,4 +987,12 @@
 
     conf_port = conf->ports[slot];
+
+    /* Level adjustment is applicable only for ports that work with raw PCM. */
+    PJ_ASSERT_RETURN(conf_port->info->format.u32 == 0 || 
+		     conf_port->info->format.u32 == PJMEDIA_FOURCC_L16,
+		     PJ_EIGNORED);
+
+    /* Set normalized adjustment level. */
+    conf_port->tx_adj_level = adj_level + NORMAL_LEVEL;
 
     return PJ_SUCCESS;
@@ -1032,4 +1063,26 @@
 	    if (nsamples_to_copy > nsamples_req)
 		nsamples_to_copy = nsamples_req;
+
+	    /* Adjust TX level. */
+	    if (cport_dst->tx_adj_level != NORMAL_LEVEL) {
+		pj_int16_t *p, *p_end;
+
+		p = f_start;
+		p_end = p + nsamples_to_copy;
+		while (p < p_end) {
+		    pj_int32_t itemp = *p;
+
+		    /* Adjust the level */
+		    itemp = (itemp * cport_dst->tx_adj_level) >> 7;
+
+		    /* Clip the signal if it's too loud */
+		    if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
+		    else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
+
+		    /* Put back in the buffer. */
+		    *p = (pj_int16_t)itemp;
+		    ++p;
+		}
+	    }
 
 	    pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
@@ -1132,6 +1185,4 @@
     unsigned ci, i;
     
-    PJ_TODO(ADJUST_RX_TX_LEVEL_FOR_PCM_FRAMES);
-
     /* Must lock mutex */
     pj_mutex_lock(conf->mutex);
@@ -1174,5 +1225,5 @@
 	    pj_status_t status;
 	    unsigned j;
-	    pj_int32_t level;
+	    pj_int32_t level = 0;
 
 	    pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
@@ -1186,8 +1237,32 @@
 		continue;
 
-	    /* Calculate RX level. */
+	    /* Calculate & adjust RX level. */
 	    if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
-		level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
-						f->size >>1 );
+		if (cport->rx_adj_level != NORMAL_LEVEL) {
+		    pj_int16_t *p = (pj_int16_t*)f->buf;
+		    pj_int16_t *end;
+
+		    end = p + (f->size >> 1);
+		    while (p < end) {
+			pj_int32_t itemp = *p;
+
+			/* Adjust the level */
+			itemp = (itemp * cport->rx_adj_level) >> 7;
+
+			/* Clip the signal if it's too loud */
+			if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
+			else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
+
+			level += PJ_ABS(itemp);
+
+			/* Put back in the buffer. */
+			*p = (pj_int16_t)itemp;
+			++p;
+		    }
+		    level /= (f->size >> 1);
+		} else {
+		    level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
+						    f->size >> 1);
+		}
 	    } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 		/* For extended frame, TX level is unknown, so we just set 
@@ -1195,7 +1270,6 @@
 		 */
 		level = NORMAL_LEVEL;
-	    } else { /* PJMEDIA_FRAME_TYPE_NONE */
-		level = 0;
 	    }
+
 	    cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
 
@@ -1221,6 +1295,9 @@
 		}
 
-		/* Set listener TX level equal to transmitter RX level. */
-		listener->tx_level = cport->rx_level;
+		/* Set listener TX level based on transmitter RX level & 
+		 * listener TX level.
+		 */
+		listener->tx_level = (cport->rx_level * listener->tx_adj_level)
+				     >> 8;
 	    }
 	}
@@ -1242,8 +1319,14 @@
 	if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0)
 	{
+	    pjmedia_frame_ext *f;
+	    
 	    /* Clear left-over samples in tx_buffer, if any, so that it won't
 	     * be transmitted next time we have audio signal.
 	     */
-	    pj_bzero(cport->tx_buf, sizeof(pjmedia_frame_ext));
+	    f = (pjmedia_frame_ext*)cport->tx_buf;
+	    f->base.type = PJMEDIA_FRAME_TYPE_NONE;
+	    f->base.size = 0;
+	    f->samples_cnt = 0;
+	    f->subframe_cnt = 0;
 	    
 	    cport->tx_level = 0;
@@ -1280,6 +1363,7 @@
 	    
 	    if (f_src_->samples_cnt < this_cport->info->samples_per_frame) {
-		pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
-		frame->type = PJMEDIA_FRAME_TYPE_NONE;
+		f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
+		f_dst->samples_cnt = 0;
+		f_dst->subframe_cnt = 0;
 		break;
 	    }
@@ -1303,6 +1387,6 @@
 	} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
 	    if ((f_src->size>>1) < this_cport->info->samples_per_frame) {
-		pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
 		frame->type = PJMEDIA_FRAME_TYPE_NONE;
+		frame->size = 0;
 		break;
 	    }
@@ -1321,7 +1405,10 @@
 				     f_src->size >> 1);
 	} else { /* PJMEDIA_FRAME_TYPE_NONE */
+	    pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
+
 	    /* Reset TX buffer */
-	    pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
-	    frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	    f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
+	    f_dst->samples_cnt = 0;
+	    f_dst->subframe_cnt = 0;
 	}
     } while (0);
@@ -1342,5 +1429,5 @@
     struct conf_port *cport = conf->ports[this_port->port_data.ldata];
     unsigned j;
-    pj_int32_t level;
+    pj_int32_t level = 0;
 
     pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
@@ -1358,8 +1445,32 @@
     }
 
-    /* Calculate RX level. */
+    /* Calculate & adjust RX level. */
     if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
-	level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
-					f->size >>1 );
+	if (cport->rx_adj_level != NORMAL_LEVEL) {
+	    pj_int16_t *p = (pj_int16_t*)f->buf;
+	    pj_int16_t *end;
+
+	    end = p + (f->size >> 1);
+	    while (p < end) {
+		pj_int32_t itemp = *p;
+
+		/* Adjust the level */
+		itemp = (itemp * cport->rx_adj_level) >> 7;
+
+		/* Clip the signal if it's too loud */
+		if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
+		else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
+
+		level += PJ_ABS(itemp);
+
+		/* Put back in the buffer. */
+		*p = (pj_int16_t)itemp;
+		++p;
+	    }
+	    level /= (f->size >> 1);
+	} else {
+	    level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
+					    f->size >> 1);
+	}
     } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
 	/* For extended frame, TX level is unknown, so we just set 
@@ -1367,7 +1478,6 @@
 	 */
 	level = NORMAL_LEVEL;
-    } else { /* PJMEDIA_FRAME_TYPE_NONE. */
-	level = 0;
-    }
+    }
+
     cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
 
@@ -1402,6 +1512,8 @@
 	}
 
-	/* Set listener TX level equal to transmitter RX level. */
-	listener->tx_level = cport->rx_level;
+	/* Set listener TX level based on transmitter RX level & listener
+	 * TX level.
+	 */
+	listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8;
     }
 
