/*

	Play sound buffer
	
*/

#include "netfone.h"

/*  GSMDECOMP  --  Uncompress the contents of a sound buffer using GSM.  */

static void gsmdecomp(soundbuf *sb)
{
    gsm_signal dst[160];
    int i, j, l = 0;
    char *dpx = ((char *) sb->buffer.buffer_val) + sizeof(short);
    static char dcb[BUFL];
    short declen = ntohs(*((short *) sb->buffer.buffer_val));
    
    if (declen <= 0 || declen > 1600) {
        declen = 1600;
    }
    for (i = 0; i < (int) (sb->buffer.buffer_len - sizeof(short));
                i += sizeof(gsm_frame)) {
        gsm_decode(gsmh, (gsm_byte *) dpx, dst);
        dpx += sizeof(gsm_frame);
        for (j = 0; j < 160; j++) {
            dcb[l++] = audio_s2u(dst[j]);
        }
    }
    memcpy(sb->buffer.buffer_val, dcb, declen);
    sb->buffer.buffer_len = declen;
}

/*	ADPCMDECOMP  --  Decompress the contents of a sound buffer using ADPCM.  */

void adpcmdecomp( struct soundbuf *sb)
{
#define TINY_PACKETS 512
	char *dp = (char *) sb->buffer.buffer_val;
	unsigned char *sp;
	static unsigned char dob[TINY_PACKETS * 2];
	struct adpcm_state adpcm;

	/* Restore the decoder state from the copy saved in the packet,
       independent of the byte order of the machine we're running on. */

	sp = (unsigned char *) dp + (sb->buffer.buffer_len - 3);
	adpcm.valprev = (short) ((((int) sp[0]) << 8) | ((int) sp[1]));
	adpcm.index = sp[2];
	sb->buffer.buffer_len -= 3;

	adpcm_decoder_u(dp, dob, (int) (sb->buffer.buffer_len * 2), &adpcm);
	sb->buffer.buffer_len *= 2;
	memcpy(dp, dob, (size_t) sb->buffer.buffer_len);
}

/*	LPCDECOMP  --  Uncompress the contents of a sound buffer using LPC.  */

static void lpcdecomp(struct soundbuf *sb, LPCLIENT_DATA c)
{
	int i, l = 0;
	char *dpx = ((char *) sb->buffer.buffer_val) + sizeof(short);
	static char dcb[LPC_FRAME_SIZE * 10];
	short declen = ntohs(*((short *) sb->buffer.buffer_val));

	if (declen <= 0 || declen > LPC_FRAME_SIZE * 10) {
		declen = LPC_FRAME_SIZE * 10;
	}
	for (i = 0; l < declen; i += sizeof(lpcparams_t)) {
		lpcparams_t *lp = (lpcparams_t *) (dpx + i);
		
		lpc_synthesize(dcb + l, lp, &(c->lpc_state));
		l += LPC_FRAME_SIZE;
	}
	memcpy(sb->buffer.buffer_val, dcb, declen);
	sb->buffer.buffer_len = declen;
}

/*	LPC10DECOMP  --  Uncompress the contents of a sound buffer using LPC10.  */

static void lpc10decomp(struct soundbuf *sb, LPCLIENT_DATA c)
{
	int j;
	char *dpx = ((char *) sb->buffer.buffer_val);
	char dcb[BUFL];

	j = lpc10decode(dpx, dcb, sb->buffer.buffer_len);
	memcpy(sb->buffer.buffer_val, dcb, j);
	sb->buffer.buffer_len = j;
}

/*  PLAYSOUND  --  Play a sound buffer, decrypting and decompressing
				   as required.  */
				   
void playSound(HWND hWnd, LPCLIENT_DATA pClientData, soundbuf *d,
			   int bitsPerSample, int samplesPerSecond)
{
	LPWAVEHDR wh;
	short *sbuf;
	unsigned char *ulp;
	WORD stat;
	int i, len;
    char *val, **specsamp;
    static unsigned char auxbuf[BUFL + 2];
#ifdef CRYPTO    
    char bbuf[8], tbuf[8];
#endif    
    
    /*	If the message queue is close to exhaustion, ditch the output
    	buffer to avoid a hangup due to queue overflow.  */
    	
    if (outputPending >= ((3 * messageQueueSize) / 4)) {
		propeller(IDC_PH_INPUT_LOST, ++inputPacketsLost);
    	return;
    }
	
	wh = (LPWAVEHDR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
			sizeof(WAVEHDR) + sizeof(CHAR *));
	if (wh == NULL) {
		return;
	}

	/* If the audio monitor is displayed and displaying input,
	   hide the original samples in a buffer linked to a
	   cell at the end of the wave header so we can update the
	   spectrum synchronously with the playing of the audio. */

	specsamp = (char **) (((char *) wh) + sizeof(WAVEHDR));
	*specsamp = NULL;
	
	/* Perform requested decryption and decompression of the
	   received data. */
	   
    len = (int) d->buffer.buffer_len;
    val = d->buffer.buffer_val;

    /* If the fSetDest bit is on, turn up the volume all the way
       if fDestJack is set.  This indicates an attempt to get the
       user's attention, even in case he's turned the volume all
       the way down.  This only happens if the user has requested
       it by setting the "Set Maximum Volume on Ring" workaround.
       Why?  Well, for public consumption we say that it's annoying
       to allow a remote user to override your chosen volume level.
       In reality, the primary motivation is that volume setting is
       one of the buggiest areas in sound card drivers and, without
       naming names, a recent (admittedly beta) driver from the
       largest vendor of sound cards for a (supposedly final release)
       operating system from the largest vendor of such products
       appears to zero the microphone gain whenever you set the output
       volume.  Imagine what fun this causes for users and how many
       E-mails to your humble author this results in.  So, pull it
       unless the user explicitly enables the volume reset. */

    if (waAudioRingMaxVolume && (d->compression & fSetDest)) {
        if (!(d->compression & fDestJack)) {
        	waveOutSetVolume(hWaveOut, (DWORD) MAKELONG(0xFFFF, 0xFFFF));
        }
    }

#ifdef CRYPTO	    

    /* If message is encrypted, decrypt. */

    if ((d->compression & fEncOTP) && (pClientData->otpFileName[0])) {
        int i, slen = (len + 7) & (~7);

        for (i = 0; i < slen; i ++) {
            val[i] ^= pClientData->otp[i];
        }
    }
    if ((d->compression & fEncPGP)) {
        unsigned short iv[4];
        int slen = (len + 7) & (~7);
        char twibble[16];
        
        /*	Special gimmick: if we receive a PGP-encrypted packet while
        	we're still waiting for PGP to complete decrypting the session
        	key, ditch it.  This is a lot easier on the user's ears when
        	he's trying furiously to enter the private key phrase.  */
        	
        if (pClientData->pgpFileName[0] != 0) {
			propeller(IDC_PH_INPUT_LOST, ++inputPacketsLost);
			return;
        } else if (pClientData->pgpkey[0] != 0) { 
	        memcpy(twibble, pClientData->pgpkey + 1, 16);
	        memset(iv, 0, sizeof(iv));
	        initcfb_idea(iv, twibble, TRUE);
	        ideacfb(val, slen);
	        close_idea();
        }
    }

    if ((d->compression & fEncBF) && pClientData->blowfish_spec) {
        unsigned char iv[8];
        int slen = (len + 7) & (~7);
        
        memset(iv, 0, sizeof(iv));
        BF_cbc_encrypt((unsigned char *) val,
					   (unsigned char *) val,
					   slen, &(pClientData->blowfishkey), iv, BF_DECRYPT);
    }
    
    if ((d->compression & fEncIDEA) && pClientData->ideaKeyString[0]) {
        unsigned short iv[4];
        int slen = (len + 7) & (~7);
        char twibble[16];
        
        memcpy(twibble, pClientData->ideakey + 1, 16);
        memset(iv, 0, sizeof(iv));
        initcfb_idea(iv, twibble, TRUE);
        ideacfb(val, slen);
        close_idea();
    }

    if ((d->compression & fEncDES) && pClientData->desKeyString[0]) {
        int i;
        char twibble[8];
        
        memcpy(twibble, pClientData->deskey + 1, 8);
        setkey(twibble);
        for (i = 0; i < len; i += 8) {
            memcpy(tbuf, val + i, 8);
            dedes(val + i);

            /* Reverse cipher block chaining. */

            if (i > 0) {
                int j;

                for (j = 0; j < 8; j++) {
                    val[(i + j)] ^= bbuf[j];
                }
            }
            memcpy(bbuf, tbuf, 8);
        }
    }
#endif    

    /* If message is compressed, decompress appropriately. */

    if (d->compression & fCompGSM) {
        gsmdecomp(d);
        len = (int) d->buffer.buffer_len;
    }
    
    if (d->compression & fCompADPCM) {
    	adpcmdecomp(d);
        len = (int) d->buffer.buffer_len;
    }
    
    if (d->compression & fCompLPC) {
    	lpcdecomp(d, pClientData);
        len = (int) d->buffer.buffer_len;
    }

	if (d->compression & fCompLPC10) {
		lpc10decomp(d, pClientData);
		len = (int) d->buffer.buffer_len;
	}

    if (d->compression & fComp2X) {
#ifdef OLD2X
        int i;
        register char *ab = auxbuf;

        for (i = 0; i < len; i++) {
            *ab++ = i == 0 ? *val :
                (audio_s2u((audio_u2s(*val) + audio_u2s(val[-1])) / 2));
            *ab++ = *val++;
        }
        len *= 2;
#else
		int olen = len * 2;
		rate_flow(&pClientData->rateconv,
				  (unsigned char *) val, (unsigned char *) auxbuf,
				  &len, &olen);
		len = olen;
#endif
		memcpy(val, auxbuf, len);
		d->buffer.buffer_len = len;
    }

#ifdef OLDWAY
	//	Allow spectrum display to examine the samples, if it so wishes

	spectrumUpdate(val, (WORD) len, 1, 8000, 8000, 1, TRUE);
#else

	/*	If monitoring received audio, save original ulaw samples
		in a buffer linked to the end of the wave packet so they
		can be passed to the audio monitor right after they're
		done playing.  */

	if (hDlgSpectral && !spectrumTransmitOnly) {
		char *specbuf = malloc(len + sizeof(WORD));

		if (specbuf != NULL) {
			*((WORD *) specbuf) = len;
			memcpy(specbuf + sizeof(WORD), val, len);
			*specsamp = specbuf;
		}
	}
#endif
    
    if (samplesPerSecond == 11025) {
	    if (bitsPerSample == 16) {	   
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 16 bit signed linear format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (((BUFL * ((DWORD) sizeof(short)) * 12) / 8))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			sbuf = (short *) wh->lpData;
			ulp = (unsigned char *) val;
			
			for (i = 0; i < len; i++) {
				int j = i & 7;
				*sbuf++ = audio_u2s(*ulp);
				//	This should be written out for better optimisation
				if (j > 0 && !(j & 1)) {
					*sbuf++ = audio_u2s(*ulp);
				} else if (j % 320 == 319) {
					*sbuf++ = audio_u2s(*ulp);
				}
				ulp++; 
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) sbuf) - wh->lpData);
		} else if (bitsPerSample == 8) {
			BYTE *bbuf;
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 8 bit PCM format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (((BUFL * ((DWORD) sizeof(short)) * 12) / 16))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			bbuf = (BYTE *) wh->lpData;
			ulp = (unsigned char *) val;
	
			for (i = 0; i < len; i++) {
				int j = i & 7;
				*bbuf++ = audio_u2c(*ulp);
				//	This should be written out for better optimisation
				if (j > 0 && !(j & 1)) {
					*bbuf++ = audio_u2c(*ulp);
				} else if (j % 320 == 319) {
					*bbuf++ = audio_u2c(*ulp);
				}
				ulp++; 
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) bbuf) - wh->lpData);
		}
	} else {	// samplesPerSecond == 8000
	    if (bitsPerSample == 16) {	   
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 16 bit signed linear format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (len * sizeof(short))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			sbuf = (short *) wh->lpData;
			ulp = (unsigned char *) val;
			
			for (i = 0; i < len; i++) {
				*sbuf++ = audio_u2s(*ulp++);
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) sbuf) - wh->lpData);
		} else if (bitsPerSample == 8) {
			BYTE  *bbuf;
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 8 bit PCM format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (len * sizeof(BYTE))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			bbuf = (BYTE  *) wh->lpData;
			ulp = (unsigned char *) val;
	
			for (i = 0; i < len; i++) {
				*bbuf++ = audio_u2c(*ulp++);
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) bbuf) - wh->lpData);
		}
	}
    
    //	Give the answering machine a chance to save the buffer
    
    if (!(d->compression & fPlayback)) {
    	answerSave(pClientData->inetSock.sin_addr, pClientData->szHost, d);
    }
	
	wh->dwFlags = 0;
	waveOutPrepareHeader(hWaveOut, wh, sizeof(WAVEHDR));
#ifdef DELAY_OUTPUT
	if (outputPending == 0)
		waveOutPause(hWaveOut);
	else if (outputPending == 10)
		waveOutRestart(hWaveOut);
#endif

	/* If output is paused due to anti-jitter, but we've used up half
	   our message queue with held packets, start output anyway.  A
	   little jitter is better than packets discarded due to the
	   message queue limitation. */

	if (jitterPause && (outputPending >= (messageQueueSize / 2))) {
		jitterPause = FALSE;
		waveOutRestart(hWaveOut);
//OutputDebugString("Restart for queue length\r\n");		
	}
	 
	stat = waveOutWrite(hWaveOut, wh, sizeof(WAVEHDR));
	if (stat == 0) {
		outputPending++;
		if (hDlgPropeller != NULL) {
			char s[80];
					
			if (outputPending == 0) {
				wsprintf(s, Format(6));
			} else {
				wsprintf(s, Format(7), outputPending);
			}
			SetDlgItemText(hDlgPropeller, IDC_PH_AUDIO_OUT_QUEUE, s);
		}
	} else {	
	    char et[MAXERRORLENGTH];
	    	
	    waveOutGetErrorText(stat, et, sizeof et);
	    waveOutUnprepareHeader(hWaveOut, wh, sizeof(WAVEHDR));
	    GlobalFreePtr(wh);
        MsgBox(hWnd, MB_OK | MB_ICONEXCLAMATION, Format(45), et);
	    return;
	}
}

/*  DECODESOUND  --  Decode a sound buffer, decrypting and decompressing
				   as required.  */
				   
void decodeSound(LPCLIENT_DATA pClientData, soundbuf *d,
			   int bitsPerSample, int samplesPerSecond, LPSTR* lpPCMData, DWORD* pdwPCMLength)
{
	LPWAVEHDR wh;
	short *sbuf;
	unsigned char *ulp;
	int i, len;
    char *val, **specsamp;
    static unsigned char auxbuf[BUFL + 2];
#ifdef CRYPTO    
    char bbuf[8], tbuf[8];
#endif    
    
    /*	If the message queue is close to exhaustion, ditch the output
    	buffer to avoid a hangup due to queue overflow.  */
    	
    if (outputPending >= ((3 * messageQueueSize) / 4)) {
		propeller(IDC_PH_INPUT_LOST, ++inputPacketsLost);
    	return;
    }
	
	wh = (LPWAVEHDR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
			sizeof(WAVEHDR) + sizeof(CHAR *));
	if (wh == NULL) {
		return;
	}

	/* If the audio monitor is displayed and displaying input,
	   hide the original samples in a buffer linked to a
	   cell at the end of the wave header so we can update the
	   spectrum synchronously with the playing of the audio. */

	specsamp = (char **) (((char *) wh) + sizeof(WAVEHDR));
	*specsamp = NULL;
	
	/* Perform requested decryption and decompression of the
	   received data. */
	   
    len = (int) d->buffer.buffer_len;
    val = d->buffer.buffer_val;

    /* If the fSetDest bit is on, turn up the volume all the way
       if fDestJack is set.  This indicates an attempt to get the
       user's attention, even in case he's turned the volume all
       the way down.  This only happens if the user has requested
       it by setting the "Set Maximum Volume on Ring" workaround.
       Why?  Well, for public consumption we say that it's annoying
       to allow a remote user to override your chosen volume level.
       In reality, the primary motivation is that volume setting is
       one of the buggiest areas in sound card drivers and, without
       naming names, a recent (admittedly beta) driver from the
       largest vendor of sound cards for a (supposedly final release)
       operating system from the largest vendor of such products
       appears to zero the microphone gain whenever you set the output
       volume.  Imagine what fun this causes for users and how many
       E-mails to your humble author this results in.  So, pull it
       unless the user explicitly enables the volume reset. */

    if (waAudioRingMaxVolume && (d->compression & fSetDest)) {
        if (!(d->compression & fDestJack)) {
        	waveOutSetVolume(hWaveOut, (DWORD) MAKELONG(0xFFFF, 0xFFFF));
        }
    }

#ifdef CRYPTO	    

    /* If message is encrypted, decrypt. */

    if ((d->compression & fEncOTP) && (pClientData->otpFileName[0])) {
        int i, slen = (len + 7) & (~7);

        for (i = 0; i < slen; i ++) {
            val[i] ^= pClientData->otp[i];
        }
    }
    if ((d->compression & fEncPGP)) {
        unsigned short iv[4];
        int slen = (len + 7) & (~7);
        char twibble[16];
        
        /*	Special gimmick: if we receive a PGP-encrypted packet while
        	we're still waiting for PGP to complete decrypting the session
        	key, ditch it.  This is a lot easier on the user's ears when
        	he's trying furiously to enter the private key phrase.  */
        	
        if (pClientData->pgpFileName[0] != 0) {
			propeller(IDC_PH_INPUT_LOST, ++inputPacketsLost);
			return;
        } else if (pClientData->pgpkey[0] != 0) { 
	        memcpy(twibble, pClientData->pgpkey + 1, 16);
	        memset(iv, 0, sizeof(iv));
	        initcfb_idea(iv, twibble, TRUE);
	        ideacfb(val, slen);
	        close_idea();
        }
    }

    if ((d->compression & fEncBF) && pClientData->blowfish_spec) {
        unsigned char iv[8];
        int slen = (len + 7) & (~7);
        
        memset(iv, 0, sizeof(iv));
        BF_cbc_encrypt((unsigned char *) val,
					   (unsigned char *) val,
					   slen, &(pClientData->blowfishkey), iv, BF_DECRYPT);
    }
    
    if ((d->compression & fEncIDEA) && pClientData->ideaKeyString[0]) {
        unsigned short iv[4];
        int slen = (len + 7) & (~7);
        char twibble[16];
        
        memcpy(twibble, pClientData->ideakey + 1, 16);
        memset(iv, 0, sizeof(iv));
        initcfb_idea(iv, twibble, TRUE);
        ideacfb(val, slen);
        close_idea();
    }

    if ((d->compression & fEncDES) && pClientData->desKeyString[0]) {
        int i;
        char twibble[8];
        
        memcpy(twibble, pClientData->deskey + 1, 8);
        setkey(twibble);
        for (i = 0; i < len; i += 8) {
            memcpy(tbuf, val + i, 8);
            dedes(val + i);

            /* Reverse cipher block chaining. */

            if (i > 0) {
                int j;

                for (j = 0; j < 8; j++) {
                    val[(i + j)] ^= bbuf[j];
                }
            }
            memcpy(bbuf, tbuf, 8);
        }
    }
#endif    

    /* If message is compressed, decompress appropriately. */

    if (d->compression & fCompGSM) {
        gsmdecomp(d);
        len = (int) d->buffer.buffer_len;
    }
    
    if (d->compression & fCompADPCM) {
    	adpcmdecomp(d);
        len = (int) d->buffer.buffer_len;
    }
    
    if (d->compression & fCompLPC) {
    	lpcdecomp(d, pClientData);
        len = (int) d->buffer.buffer_len;
    }

	if (d->compression & fCompLPC10) {
		lpc10decomp(d, pClientData);
		len = (int) d->buffer.buffer_len;
	}

    if (d->compression & fComp2X) {
#ifdef OLD2X
        int i;
        register char *ab = auxbuf;

        for (i = 0; i < len; i++) {
            *ab++ = i == 0 ? *val :
                (audio_s2u((audio_u2s(*val) + audio_u2s(val[-1])) / 2));
            *ab++ = *val++;
        }
        len *= 2;
#else
		int olen = len * 2;
		rate_flow(&pClientData->rateconv,
				  (unsigned char *) val, (unsigned char *) auxbuf,
				  &len, &olen);
		len = olen;
#endif
		memcpy(val, auxbuf, len);
		d->buffer.buffer_len = len;
    }

#ifdef OLDWAY
	//	Allow spectrum display to examine the samples, if it so wishes

	spectrumUpdate(val, (WORD) len, 1, 8000, 8000, 1, TRUE);
#else

	/*	If monitoring received audio, save original ulaw samples
		in a buffer linked to the end of the wave packet so they
		can be passed to the audio monitor right after they're
		done playing.  */

	if (hDlgSpectral && !spectrumTransmitOnly) {
		char *specbuf = malloc(len + sizeof(WORD));

		if (specbuf != NULL) {
			*((WORD *) specbuf) = len;
			memcpy(specbuf + sizeof(WORD), val, len);
			*specsamp = specbuf;
		}
	}
#endif
    
    if (samplesPerSecond == 11025) {
	    if (bitsPerSample == 16) {	   
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 16 bit signed linear format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (((BUFL * ((DWORD) sizeof(short)) * 12) / 8))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			sbuf = (short *) wh->lpData;
			ulp = (unsigned char *) val;
			
			for (i = 0; i < len; i++) {
				int j = i & 7;
				*sbuf++ = audio_u2s(*ulp);
				//	This should be written out for better optimisation
				if (j > 0 && !(j & 1)) {
					*sbuf++ = audio_u2s(*ulp);
				} else if (j % 320 == 319) {
					*sbuf++ = audio_u2s(*ulp);
				}
				ulp++; 
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) sbuf) - wh->lpData);
		} else if (bitsPerSample == 8) {
			BYTE *bbuf;
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 8 bit PCM format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (((BUFL * ((DWORD) sizeof(short)) * 12) / 16))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			bbuf = (BYTE *) wh->lpData;
			ulp = (unsigned char *) val;
	
			for (i = 0; i < len; i++) {
				int j = i & 7;
				*bbuf++ = audio_u2c(*ulp);
				//	This should be written out for better optimisation
				if (j > 0 && !(j & 1)) {
					*bbuf++ = audio_u2c(*ulp);
				} else if (j % 320 == 319) {
					*bbuf++ = audio_u2c(*ulp);
				}
				ulp++; 
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) bbuf) - wh->lpData);
		}
	} else {	// samplesPerSecond == 8000
	    if (bitsPerSample == 16) {	   
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 16 bit signed linear format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (len * sizeof(short))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			sbuf = (short *) wh->lpData;
			ulp = (unsigned char *) val;
			
			for (i = 0; i < len; i++) {
				*sbuf++ = audio_u2s(*ulp++);
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) sbuf) - wh->lpData);
		} else if (bitsPerSample == 8) {
			BYTE  *bbuf;
			
			/* Convert the resulting u-law samples in the sound buffer
			   to 8 bit PCM format. */
			   
			wh->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
				(DWORD) (len * sizeof(BYTE))); 
			if (wh->lpData == NULL) {
				GlobalFreePtr(wh);
				return;
			}
			bbuf = (BYTE  *) wh->lpData;
			ulp = (unsigned char *) val;
	
			for (i = 0; i < len; i++) {
				*bbuf++ = audio_u2c(*ulp++);
			}
			wh->dwBufferLength = wh->dwBytesRecorded =
				(((LPSTR) bbuf) - wh->lpData);
		}
	}

  *lpPCMData = wh->lpData;
  *pdwPCMLength = wh->dwBytesRecorded;

  GlobalFreePtr(wh);
  
  return;
}