Index: /pjproject/trunk/pjmedia/include/pjmedia/alaw_ulaw.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/alaw_ulaw.h	(revision 2415)
+++ /pjproject/trunk/pjmedia/include/pjmedia/alaw_ulaw.h	(revision 2416)
@@ -140,4 +140,72 @@
 #endif
 
+/**
+ * Encode 16-bit linear PCM data to 8-bit U-Law data.
+ *
+ * @param dst	    Destination buffer for 8-bit U-Law data.
+ * @param src	    Source, 16-bit linear PCM data.
+ * @param len	    Number of samples.
+ */
+PJ_INLINE(void) pjmedia_ulaw_encode(pj_uint8_t *dst, const pj_int16_t *src, 
+				    pj_size_t len)
+{
+    const pj_int16_t *end = src + len;
+    
+    while (src < end) {
+	*dst++ = pjmedia_linear2ulaw(*src++);
+    }
+}
+
+/**
+ * Encode 16-bit linear PCM data to 8-bit A-Law data.
+ *
+ * @param dst	    Destination buffer for 8-bit A-Law data.
+ * @param src	    Source, 16-bit linear PCM data.
+ * @param len	    Number of samples.
+ */
+PJ_INLINE(void) pjmedia_alaw_encode(pj_uint8_t *dst, const pj_int16_t *src, 
+				    pj_size_t len)
+{
+    const pj_int16_t *end = src + len;
+    
+    while (src < end) {
+	*dst++ = pjmedia_linear2alaw(*src++);
+    }
+}
+
+/**
+ * Decode 8-bit U-Law data to 16-bit linear PCM data.
+ *
+ * @param dst	    Destination buffer for 16-bit PCM data.
+ * @param src	    Source, 8-bit U-Law data.
+ * @param len	    Number of samples.
+ */
+PJ_INLINE(void) pjmedia_ulaw_decode(pj_int16_t *dst, const pj_uint8_t *src, 
+				    pj_size_t len)
+{
+    const pj_uint8_t *end = src + len;
+    
+    while (src < end) {
+	*dst++ = pjmedia_ulaw2linear(*src++);
+    }
+}
+
+/**
+ * Decode 8-bit A-Law data to 16-bit linear PCM data.
+ *
+ * @param dst	    Destination buffer for 16-bit PCM data.
+ * @param src	    Source, 8-bit A-Law data.
+ * @param len	    Number of samples.
+ */
+PJ_INLINE(void) pjmedia_alaw_decode(pj_int16_t *dst, const pj_uint8_t *src, 
+				    pj_size_t len)
+{
+    const pj_uint8_t *end = src + len;
+    
+    while (src < end) {
+	*dst++ = pjmedia_alaw2linear(*src++);
+    }
+}
+
 PJ_END_DECL
 
Index: /pjproject/trunk/pjmedia/src/pjmedia/symbian_sound_aps.cpp
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/symbian_sound_aps.cpp	(revision 2415)
+++ /pjproject/trunk/pjmedia/src/pjmedia/symbian_sound_aps.cpp	(revision 2416)
@@ -23,4 +23,5 @@
 #include <pj/assert.h>
 #include <pj/log.h>
+#include <pj/math.h>
 #include <pj/os.h>
 
@@ -268,5 +269,4 @@
 
     static CPjAudioEngine *NewL(pjmedia_snd_stream *parent_strm,
-				pjmedia_dir dir,
 				pjmedia_snd_rec_cb rec_cb,
 				pjmedia_snd_play_cb play_cb,
@@ -280,5 +280,4 @@
 private:
     CPjAudioEngine(pjmedia_snd_stream *parent_strm,
-		   pjmedia_dir dir,
 		   pjmedia_snd_rec_cb rec_cb,
 		   pjmedia_snd_play_cb play_cb,
@@ -300,5 +299,4 @@
     State			 state_;
     pjmedia_snd_stream		*parentStrm_;
-    pjmedia_dir			 dir_;
     pjmedia_snd_rec_cb		 recCb_;
     pjmedia_snd_play_cb		 playCb_;
@@ -317,14 +315,24 @@
     CQueueHandler		*iRecCommHandler;
     CQueueHandler		*iRecHandler;
+    
+    static pj_uint8_t		 aps_samples_per_frame;
+    
+    pj_int16_t			*play_buf;
+    pj_uint16_t			 play_buf_len;
+    pj_uint16_t			 play_buf_start;
+    pj_int16_t			*rec_buf;
+    pj_uint16_t			 rec_buf_len;
 };
 
 
+pj_uint8_t CPjAudioEngine::aps_samples_per_frame = 0;
+
+
 CPjAudioEngine* CPjAudioEngine::NewL(pjmedia_snd_stream *parent_strm,
-				     pjmedia_dir dir,
 				     pjmedia_snd_rec_cb rec_cb,
 				     pjmedia_snd_play_cb play_cb,
 				     void *user_data)
 {
-    CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm, dir,
+    CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
 						       rec_cb, play_cb,
 						       user_data);
@@ -336,5 +344,4 @@
 
 CPjAudioEngine::CPjAudioEngine(pjmedia_snd_stream *parent_strm,
-			       pjmedia_dir dir,
 			       pjmedia_snd_rec_cb rec_cb,
 			       pjmedia_snd_play_cb play_cb,
@@ -342,5 +349,4 @@
       : state_(STATE_NULL),
 	parentStrm_(parent_strm),
-	dir_(dir),
 	recCb_(rec_cb),
 	playCb_(play_cb),
@@ -364,5 +370,5 @@
 
     if (state_ == STATE_READY) {
-	if (dir_ != PJMEDIA_DIR_PLAYBACK) {
+	if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
 	    iReadQ.Close();
 	    iReadCommQ.Close();
@@ -474,4 +480,15 @@
     iSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
     iSettings.iSettings.iVolume	    = 0;
+    
+    /* play_buf size is samples per frame of parent stream. */
+    play_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool, 
+				          parentStrm_->samples_per_frame << 1);
+    play_buf_len = 0;
+    play_buf_start = 0;
+    
+    /* rec_buf size is samples per frame of parent stream. */
+    rec_buf  = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool, 
+					  parentStrm_->samples_per_frame << 1);
+    rec_buf_len = 0;
 }
 
@@ -484,19 +501,21 @@
     iSession.SetVadMode(EFalse);
     iSession.SetPlc(EFalse);
-    iSession.SetEncoderMode(EALawOr20ms);
-    iSession.SetDecoderMode(EALawOr20ms);
+    iSession.SetEncoderMode(EULawOr30ms);
+    iSession.SetDecoderMode(EULawOr30ms);
     iSession.ActivateLoudspeaker(act_loudspeaker);
 
     // Not only playback
-    if (dir_ != PJMEDIA_DIR_PLAYBACK) {
+    if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
 	iRecHandler = CQueueHandler::NewL(this, &iReadQ, 
 					  CQueueHandler::ERecordQueue);
 	iRecHandler->Start();
 	iSession.Read();
+	TRACE_((THIS_FILE, "APS recorder started"));
     }
 
     // Not only capture
-    if (dir_ != PJMEDIA_DIR_CAPTURE) {
+    if (parentStrm_->dir != PJMEDIA_DIR_CAPTURE) {
 	iSession.Write();
+	TRACE_((THIS_FILE, "APS player started"));
     }
 
@@ -505,5 +524,8 @@
 }
 
+///////////////////////////////////////////////////////////
 // Inherited from MQueueHandlerObserver
+//
+
 void CPjAudioEngine::InputStreamInitialized(const TInt aStatus)
 {
@@ -521,5 +543,5 @@
 
     if (aStatus == KErrNone) {
-	if (dir_ == PJMEDIA_DIR_PLAYBACK) {
+	if (parentStrm_->dir == PJMEDIA_DIR_PLAYBACK) {
 	    state_ = STATE_READY;
 	    // Only playback, start directly
@@ -537,19 +559,43 @@
 void CPjAudioEngine::RecCb(TAPSCommBuffer &buffer)
 {
-    pj_int16_t buf[160];
     pj_assert(buffer.iBuffer[0] == 1 && buffer.iBuffer[1] == 0);
 
-    for (int i=0; i<160; ++i)
-	 buf[i] = pjmedia_alaw2linear(buffer.iBuffer[i+2]);
-
-    recCb_(userData_, 0, buf, sizeof(buf));
+    /* Detect the recorder G.711 frame size, player frame size will follow 
+     * this recorder frame size. 
+     */
+    if (CPjAudioEngine::aps_samples_per_frame == 0) {
+	CPjAudioEngine::aps_samples_per_frame = buffer.iBuffer.Length() < 160?
+						 80 : 160;
+	TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples", 
+		CPjAudioEngine::aps_samples_per_frame));
+    }
+
+    /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
+     * Whenever rec_buf is full, call parent stream callback.  
+     */ 
+    unsigned dec_len = 0;
+
+    while (dec_len < CPjAudioEngine::aps_samples_per_frame) {
+	unsigned tmp;
+
+	tmp = PJ_MIN(parentStrm_->samples_per_frame - rec_buf_len, 
+		     CPjAudioEngine::aps_samples_per_frame - dec_len);
+	pjmedia_ulaw_decode(&rec_buf[rec_buf_len], 
+			    buffer.iBuffer.Ptr() + 2 + dec_len, 
+			    tmp);
+	rec_buf_len += tmp;
+	dec_len += tmp;
+	
+	pj_assert(rec_buf_len <= parentStrm_->samples_per_frame);
+	
+	if (rec_buf_len == parentStrm_->samples_per_frame) {
+	    recCb_(userData_, 0, rec_buf, rec_buf_len << 1);
+	    rec_buf_len = 0;
+	}
+    }
 }
 
 void CPjAudioEngine::PlayCb(TAPSCommBuffer &buffer)
 {
-    pj_int16_t buf[160];
-
-    playCb_(userData_, 0, buf, sizeof(buf));
-
     buffer.iCommand = CQueueHandler::EAPSPlayData;
     buffer.iStatus = 0;
@@ -557,9 +603,46 @@
     buffer.iBuffer.Append(1);
     buffer.iBuffer.Append(0);
-    for (int i=0; i<160; ++i)
-	buffer.iBuffer.Append(pjmedia_linear2alaw(buf[i]));
+
+    /* Send 10ms silence frame if frame size hasn't been known. */
+    if (CPjAudioEngine::aps_samples_per_frame == 0) {
+	pjmedia_zero_samples(play_buf, 80);
+	pjmedia_ulaw_encode((pj_uint8_t*)play_buf, play_buf, 80);
+	buffer.iBuffer.Append((TUint8*)play_buf, 80);
+	iWriteQ.Send(buffer);
+	return;
+    }
+    
+    unsigned enc_len = 0;
+    
+    /* Call parent stream callback to get PCM samples to play,
+     * encode the PCM samples into G.711 and put it into APS buffer. 
+     */
+    while (enc_len < CPjAudioEngine::aps_samples_per_frame) {
+	if (play_buf_len == 0) {
+	    playCb_(userData_, 0, play_buf, 
+		    sizeof(parentStrm_->samples_per_frame<<1));
+	    play_buf_len = parentStrm_->samples_per_frame;
+	    play_buf_start = 0;
+	}
+	
+	unsigned tmp;
+	
+	tmp = PJ_MIN(play_buf_len, 
+		     CPjAudioEngine::aps_samples_per_frame - enc_len);
+	pjmedia_ulaw_encode((pj_uint8_t*)&play_buf[play_buf_start], 
+			    &play_buf[play_buf_start], 
+			    tmp);
+	buffer.iBuffer.Append((TUint8*)&play_buf[play_buf_start], tmp);
+	enc_len += tmp;
+	play_buf_len -= tmp;
+	play_buf_start += tmp;
+    }
 
     iWriteQ.Send(buffer);
 }
+
+//
+// End of inherited from MQueueHandlerObserver
+/////////////////////////////////////////////////////////////
 
 
@@ -642,6 +725,5 @@
 
     // Create the audio engine.
-    TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, strm->dir, 
-						   rec_cb, play_cb, 
+    TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, rec_cb, play_cb, 
 						   user_data));
     if (err != KErrNone) {
