os/kernelhwsrv/kerneltest/e32test/multimedia/t_soundwav.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // e32test\multimedia\t_soundwav.cpp
    15 // Record wav to a file "t_soundwav filename [rate] [channels] [seconds]" where filename does not exist.
    16 // Play a wav file "t_soundwav filename" where filename exists.
    17 // 
    18 //
    19 
    20 /**
    21  @file Shared chunk sound driver WAV file player and recorder utility.
    22 */
    23 
    24 #include <e32test.h>
    25 #include <e32def.h>
    26 #include <e32def_private.h>
    27 #include "t_soundutils.h"
    28 #include <f32file.h>
    29 
    30 #define CHECK(aValue) {Test(aValue);}
    31 #define CHECK_NOERROR(aValue) { TInt v=(aValue); if(v) { Test.Printf(_L("Error value = %d\n"),v); Test(EFalse,__LINE__); }}
    32 #define CHECK_EQUAL(aValue1,aValue2) { TInt v1=(aValue1); TInt v2=(aValue2); if(v1!=v2) { Test.Printf(_L("Error value = %d\n"),v1); Test(EFalse,__LINE__); }}
    33 
    34 _LIT(KSndLddFileName,"ESOUNDSC.LDD");
    35 _LIT(KSndPddFileName,"SOUNDSC.PDD");
    36 
    37 RTest Test(_L("T_SOUNDWAV"));
    38 RSoundSc TxSoundDevice;
    39 RSoundSc RxSoundDevice;
    40 
    41 TSoundFormatsSupportedV02Buf RecordCapsBuf;
    42 TSoundFormatsSupportedV02Buf PlayCapsBuf;
    43 TCurrentSoundFormatV02Buf PlayFormatBuf;
    44 TCurrentSoundFormatV02Buf RecordFormatBuf;
    45 
    46 TBuf<256> CommandLine;
    47 RFs Fs;
    48 
    49 //
    50 // This is the WAV header structure used for playing and recording files
    51 //
    52 struct WAVEheader
    53 	{
    54 	char ckID[4];				// chunk id 'RIFF'
    55 	TUint32 ckSize;				// chunk size
    56 	char wave_ckID[4];			// wave chunk id 'WAVE'
    57 	char fmt_ckID[4];			// format chunk id 'fmt '
    58 	TUint32 fmt_ckSize;			// format chunk size
    59 	TUint16 formatTag;			// format tag currently pcm
    60 	TUint16 nChannels;			// number of channels
    61 	TUint32 nSamplesPerSec;		// sample rate in hz
    62 	TUint32 nAvgBytesPerSec;	// average bytes per second
    63 	TUint16 nBlockAlign;		// number of bytes per sample
    64 	TUint16 nBitsPerSample;		// number of bits in a sample
    65 	char data_ckID[4];			// data chunk id 'data'
    66 	TUint32 data_ckSize;		// length of data chunk
    67 	};
    68 	
    69 LOCAL_C TInt SamplesPerSecondToRate(TInt aRateInSamplesPerSecond,TSoundRate& aRate)
    70 	{
    71 	switch (aRateInSamplesPerSecond)
    72 		{
    73 		case 7350: 	aRate=ESoundRate7350Hz; break;
    74 		case 8000: 	aRate=ESoundRate8000Hz; break;
    75 		case 8820: 	aRate=ESoundRate8820Hz; break;
    76 		case 9600: 	aRate=ESoundRate9600Hz; break;
    77 		case 11025: aRate=ESoundRate11025Hz; break;
    78 		case 12000: aRate=ESoundRate12000Hz; break;
    79 		case 14700:	aRate=ESoundRate14700Hz; break;
    80 		case 16000: aRate=ESoundRate16000Hz; break;
    81 		case 22050: aRate=ESoundRate22050Hz; break;
    82 		case 24000: aRate=ESoundRate24000Hz; break;
    83 		case 29400: aRate=ESoundRate29400Hz; break;
    84 		case 32000: aRate=ESoundRate32000Hz; break;
    85 		case 44100: aRate=ESoundRate44100Hz; break;
    86 		case 48000: aRate=ESoundRate48000Hz; break;
    87 		default: return(KErrArgument);
    88 		};
    89 	return(KErrNone);	
    90 	}
    91 	
    92 LOCAL_C TInt RecordBufferSizeInBytes(TCurrentSoundFormatV02& aFormat)
    93 	{
    94 	switch (aFormat.iRate)
    95 		{
    96 		case ESoundRate7350Hz: return(8192);
    97 		case ESoundRate8000Hz: return(8192);
    98 		case ESoundRate8820Hz: return(12288);
    99 		case ESoundRate9600Hz: return(12288);
   100 		case ESoundRate11025Hz: return(12288);
   101 		case ESoundRate12000Hz: return(12288);
   102 		case ESoundRate14700Hz: return(12288);
   103 		case ESoundRate16000Hz: return(12288);
   104 		case ESoundRate22050Hz: return(16384);
   105 		case ESoundRate24000Hz: return(16384);
   106 		case ESoundRate29400Hz: return(24576);
   107 		case ESoundRate32000Hz: return(24576);
   108 		case ESoundRate44100Hz: return(32768);
   109 		case ESoundRate48000Hz: return(32768);
   110 		default: return(32768);
   111 		};	
   112 	}	
   113 	
   114 LOCAL_C TInt Load()
   115 	{
   116 	TInt r;
   117 
   118 	Test.Start(_L("Load sound PDD"));
   119 	r=User::LoadPhysicalDevice(KSndPddFileName);
   120 	if (r==KErrNotFound)
   121 		{
   122 		Test.End();
   123 		return(r);
   124 		}
   125 	CHECK(r==KErrNone || r==KErrAlreadyExists);
   126 	
   127 	Test.Next(_L("Load sound LDD"));
   128 	r=User::LoadLogicalDevice(KSndLddFileName);
   129 	CHECK(r==KErrNone || r==KErrAlreadyExists);
   130 
   131 	Test.End();
   132 	return(KErrNone);
   133 	}
   134 
   135 LOCAL_C TInt WavPlay()
   136 	{
   137 	RChunk chunk;
   138 	
   139 	// Parse the commandline and get a filename to use
   140 	TLex l(CommandLine);
   141 	TFileName thisfile=RProcess().FileName();
   142 	TPtrC token=l.NextToken();
   143 	if (token.MatchF(thisfile)==0)
   144 		token.Set(l.NextToken());
   145 
   146 	if (token.Length()==0)
   147 		{
   148 		// No args, skip to end
   149 		Test.Printf(_L("Invalid configuration\r\n"));
   150 		return(KErrArgument);
   151 		}
   152 		
   153 	Test.Next(_L("Play Wav file"));
   154 
   155 	// Assume that the argument is a WAV filename
   156 	TFileName wavFilename=token;
   157 	TInt r;
   158 	RFile source;
   159 	r = source.Open(Fs,wavFilename,EFileRead);
   160 	if (r!=KErrNone)
   161 		{
   162 		Test.Printf(_L("Open failed(%d)\r\n"), r);
   163 		return(r);
   164 		}
   165 
   166 	// Read the pcm header
   167 	WAVEheader header;
   168 	TPtr8 headerDes((TUint8 *)&header,sizeof(struct WAVEheader),sizeof(struct WAVEheader));
   169 	r = source.Read(headerDes);
   170 	if (r!=KErrNone)
   171 		{
   172 		source.Close();
   173 		return(r);
   174 		}
   175 	Test.Printf(_L("Header Read %d bytes\r\n"),headerDes.Size());
   176 
   177 	if (headerDes.Size() != sizeof(struct WAVEheader)) // EOF
   178 		{
   179 		Test.Printf(_L("Couldn't read a header(%d bytes)\r\n"),headerDes.Size());
   180 		source.Close();
   181 		return(KErrCorrupt);
   182 		}
   183 
   184 	Test.Printf(_L("Header rate:%d channels:%d tag:%d bits:%d (%d bytes/s) align %d datalen:%d fmt_ckSize:%d ckSize:%d\n"),
   185 			header.nSamplesPerSec, header.nChannels, header.formatTag, header.nBitsPerSample,
   186 			header.nAvgBytesPerSec, header.nBlockAlign, header.data_ckSize, header.fmt_ckSize, header.ckSize);
   187 
   188 	if (header.formatTag != 1) // not pcm
   189 		{
   190 		Test.Printf(_L("Format not PCM(%d)\r\n"),header.formatTag);
   191 		source.Close();
   192 		return(KErrNotSupported);
   193 		}
   194 
   195 	if (header.nBitsPerSample != 16) // not 16 bit
   196 		{
   197 		Test.Printf(_L("Format not 16 bit PCM(%d bits)\r\n"),header.nBitsPerSample);
   198 		source.Close();
   199 		return(KErrNotSupported);
   200 		}
   201 		
   202 	TSoundRate rate;	
   203 	if (SamplesPerSecondToRate(header.nSamplesPerSec,rate)!=KErrNone)	
   204 		{
   205 		Test.Printf(_L("Format specifies a rate not supported(%d)\r\n"),header.nSamplesPerSec);
   206 		source.Close();
   207 		return(KErrNotSupported);
   208 		}
   209 
   210 	TxSoundDevice.AudioFormat(PlayFormatBuf);	// Read back the current setting which must be valid.
   211 	PlayFormatBuf().iChannels = header.nChannels;
   212 	PlayFormatBuf().iRate = rate;
   213 	PlayFormatBuf().iEncoding = ESoundEncoding16BitPCM;
   214 	
   215 	// Set the play buffer configuration.
   216 	TInt bufSize=BytesPerSecond(PlayFormatBuf())/8; 	// Large enough to hold 1/8th second of data.
   217 	bufSize&=~(header.nBlockAlign-1);					// Keep the buffer length a multiple of the bytes per sample (assumes 16bitPCM, 1 or 2 chans).
   218 	if (PlayCapsBuf().iRequestMinSize)
   219 		bufSize&=~(PlayCapsBuf().iRequestMinSize-1); 	// Keep the buffer length valid for the driver.
   220 	TTestSharedChunkBufConfig bufferConfig;
   221 	bufferConfig.iNumBuffers=3;
   222 	bufferConfig.iBufferSizeInBytes=bufSize;
   223 	bufferConfig.iFlags=0;	
   224 	PrintBufferConf(bufferConfig,Test);
   225 	TPckg<TTestSharedChunkBufConfig> bufferConfigBuf(bufferConfig);
   226 	r=TxSoundDevice.SetBufferChunkCreate(bufferConfigBuf,chunk);
   227 	if (r!=KErrNone)
   228 		{
   229 		Test.Printf(_L("Buffer configuration not supported(%d)\r\n"),r);
   230 		source.Close();
   231 		return(r);
   232 		}
   233 	TxSoundDevice.GetBufferConfig(bufferConfigBuf);			// Read back the configuration - to get the buffer offsets
   234 	CHECK(bufferConfig.iBufferSizeInBytes==bufSize);
   235 
   236 	// Set the audio play configuration.
   237 	TxSoundDevice.SetVolume(KSoundMaxVolume - (KSoundMaxVolume / 4)); // set volume to 75%
   238 	PrintConfig(PlayFormatBuf(),Test);
   239 	r=TxSoundDevice.SetAudioFormat(PlayFormatBuf);
   240 	if (r!=KErrNone)
   241 		{
   242 		Test.Printf(_L("Format not supported\r\n"));
   243 		source.Close();
   244 		chunk.Close();
   245 		return(r);
   246 		}
   247 	TxSoundDevice.ResetBytesTransferred();
   248 
   249 	TInt32 bytesToPlay = header.data_ckSize;
   250 	TTime starttime;
   251 	starttime.HomeTime();
   252 
   253 	TRequestStatus stat[3];
   254 	TPtr8* tPtr[3];
   255 	TInt i;
   256 	for (i=0;i<3;i++)
   257 		tPtr[i]=new TPtr8(NULL,0); 
   258 
   259 	TTime startTime;
   260 	startTime.HomeTime();
   261 	
   262 	// Start off by issuing a play request for each buffer (assuming that the file is long enough). Use the full size
   263 	// of each buffer.
   264 	TInt stillToRead=bytesToPlay;
   265 	TInt stillNotPlayed=bytesToPlay;
   266 	TUint flags;
   267 	for (i=0 ; i<3 ; i++)
   268 		{
   269 		// Setup the descriptor for reading in the data from the file.
   270 		tPtr[i]->Set(chunk.Base()+bufferConfig.iBufferOffsetList[i],0,bufSize); 
   271 		
   272 		// If there is still data to read to play then read this into the descriptor
   273 		// and then write it to the driver.
   274 		if (stillToRead)
   275 			{
   276 			r=source.Read(*tPtr[i],Min(stillToRead,bufSize));
   277 			if (r!=KErrNone)
   278 				{
   279 				Test.Printf(_L("Initial file read error(%d)\r\n"),r);
   280 				source.Close();
   281 				chunk.Close();
   282 				return(r);
   283 				}
   284 			stillToRead-=tPtr[i]->Length();
   285 			flags=(stillToRead>0)?0:KSndFlagLastSample;
   286 			TxSoundDevice.PlayData(stat[i],bufferConfig.iBufferOffsetList[i],tPtr[i]->Length(),flags);
   287 			}
   288 		else
   289 			stat[i]=KRequestPending;	
   290 		}	
   291 		
   292 	FOREVER
   293 		{
   294 		// Wait for any one of the outstanding play requests to complete.
   295 		User::WaitForAnyRequest();
   296 
   297 		TTime currentTime;
   298 		currentTime.HomeTime();
   299 		TInt64 elapsedTime = currentTime.Int64()-startTime.Int64();	// us
   300 		TTimeIntervalMicroSecondsBuf timePlayedBuf;
   301 		if(TxSoundDevice.TimePlayed(timePlayedBuf) == KErrNone)
   302 			{
   303 			// Compare TimePlayed with the actual elapsed time. They should be different, but not drift apart too badly...
   304 			TInt32 offset = TInt32(elapsedTime - timePlayedBuf().Int64());
   305 			Test.Printf(_L("\telapsedTime - TimePlayed = %d ms\n"), offset/1000);
   306 			}		
   307 	
   308 		// Work out which buffer this applies to
   309 		for (i=0 ; i<3 ; i++)
   310 			{
   311 			if (stat[i]!=KRequestPending)
   312 				break;
   313 			}
   314 		if (i>=3)
   315 			{
   316 			Test.Printf(_L("I/O error\r\n"));
   317 			source.Close();
   318 			chunk.Close();
   319 			return(KErrGeneral);
   320 			}
   321 	
   322 		// Check that the transfer was succesful and whether we have now played all the file.
   323 		if (stat[i]!=KErrNone)
   324 			{
   325 			Test.Printf(_L("Play error(%d)\r\n"),stat[i].Int());
   326 			source.Close();
   327 			chunk.Close();
   328 			return(stat[i].Int());
   329 			}
   330 		Test.Printf(_L("Played %d bytes(%d) - %d\r\n"),tPtr[i]->Length(),i,stat[i].Int());
   331 		stillNotPlayed-=tPtr[i]->Length();
   332 		CHECK(stillNotPlayed>=0);
   333 		if (!stillNotPlayed)
   334 			break;
   335 	
   336 		// Still more to be played so read the next part of the file into the descriptor for this
   337 		// buffer and then write it to the driver.
   338 		if (stillToRead)
   339 			{
   340 			TInt len=Min(stillToRead,bufSize);
   341 		
   342 			// If we've got to the end of the file and the driver is particular about the request length then 
   343 			// zero fill the entire buffer so we can play extra zeros after the last sample from the file.
   344 			if (len<bufSize && PlayCapsBuf().iRequestMinSize)
   345 				tPtr[i]->FillZ(bufSize);
   346 		
   347 			// Read the next part of the file 
   348 			r=source.Read(*tPtr[i],len);			// This will alter the length of the descriptor
   349 			if (r!=KErrNone)
   350 				{
   351 				Test.Printf(_L("File read error(%d)\r\n"),r);
   352 				source.Close();
   353 				chunk.Close();
   354 				return(r);
   355 				}
   356 			stillToRead-=tPtr[i]->Length();
   357 		
   358 			// If we've got to the end of the file and the driver is particular about the request length then
   359 			// round up the length to the next valid boundary. This is OK since we zero filled.
   360 			if (tPtr[i]->Length() < bufSize && PlayCapsBuf().iRequestMinSize)
   361 				{
   362 				TUint m=PlayCapsBuf().iRequestMinSize-1;
   363 				len=(tPtr[i]->Length() + m) & ~m;
   364 				}
   365 		
   366 			// Write it to the driver.
   367 			flags=(stillToRead>0)?0:KSndFlagLastSample;
   368 			TxSoundDevice.PlayData(stat[i],bufferConfig.iBufferOffsetList[i],len,flags);
   369 			}
   370 		else
   371 			stat[i]=KRequestPending;		
   372 		}
   373 	
   374 	// Delete all the variables again.	
   375 	for (i=0 ; i<3 ; i++)
   376 		delete tPtr[i];
   377 	
   378 	TTime endtime;
   379 	endtime.HomeTime();
   380 
   381 	Test.Printf(_L("Done playing\r\n"));
   382 	Test.Printf(_L("Bytes played = %d\r\n"),TxSoundDevice.BytesTransferred());
   383 	Test.Printf(_L("Delta time = %d\r\n"),endtime.Int64()-starttime.Int64());
   384 
   385 	chunk.Close();
   386 	source.Close();
   387 	return(KErrNone);
   388 	}
   389 
   390 LOCAL_C TInt WavRecord()
   391 	{
   392 	// Parse the commandline and get a filename to use
   393 	TLex l(CommandLine);
   394 	TParse destinationName;
   395 	if (destinationName.SetNoWild(l.NextToken(),0,0)!=KErrNone)
   396 		{
   397 		Test.Printf(_L("No arg, skipping\r\n"));
   398 		return(KErrArgument);
   399 		}
   400 	Test.Next(_L("Record Wav file"));
   401 
   402 	// Open the file for writing
   403 	TInt r;
   404 	RFile destination;
   405 	r = destination.Replace(Fs,destinationName.FullName(),EFileWrite);
   406 	if (r!=KErrNone)
   407 		{
   408 		Test.Printf(_L("Open file for write failed(%d)\n"), r);
   409 		return(r);
   410 		}		
   411 	Test.Printf(_L("File opened for write\r\n"));
   412 	
   413 	Test.Next(_L("Preparing to record"));
   414 	
   415 	// Get the rate
   416 	TLex cl(l.NextToken());
   417 	TUint32 tmpRate;
   418 	TSoundRate rate;
   419 	r = cl.Val(tmpRate,EDecimal);
   420 	if (r == KErrNone && (r=SamplesPerSecondToRate(tmpRate,rate))==KErrNone)
   421 		{
   422 		Test.Printf(_L("Parsed rate: %d\r\n"), tmpRate);
   423 		RecordFormatBuf().iRate = rate;
   424 		}
   425 	else
   426 		{
   427 		Test.Printf(_L("Parse rate failed(%d)\r\n"),r);
   428 		RecordFormatBuf().iRate = ESoundRate32000Hz;
   429 		}
   430 
   431 	// Get number of channels
   432 	TLex cl_chan(l.NextToken());
   433 	TUint32 tmpChannels;
   434 	r = cl_chan.Val(tmpChannels,EDecimal);
   435 	if (r == KErrNone)
   436 		{
   437 		Test.Printf(_L("Parsed %d channels\r\n"),tmpChannels);
   438 		RecordFormatBuf().iChannels = tmpChannels;
   439 		}
   440 	else
   441 		{
   442 		Test.Printf(_L("Parse channels failed(%d)\r\n"), r);
   443 		RecordFormatBuf().iChannels = 2;
   444 		}
   445 
   446 	RecordFormatBuf().iEncoding = ESoundEncoding16BitPCM;
   447 	
   448 	// Set the record buffer configuration.
   449 	RChunk chunk;
   450 	TTestSharedChunkBufConfig bufferConfig;
   451 	bufferConfig.iNumBuffers=4;
   452 	bufferConfig.iBufferSizeInBytes=RecordBufferSizeInBytes(RecordFormatBuf());
   453 	if (RecordCapsBuf().iRequestMinSize)
   454 		bufferConfig.iBufferSizeInBytes&=~(RecordCapsBuf().iRequestMinSize-1); 	// Keep the buffer length valid for the driver.
   455 	bufferConfig.iFlags=0;	
   456 	PrintBufferConf(bufferConfig,Test);
   457 	TPckg<TTestSharedChunkBufConfig> bufferConfigBuf(bufferConfig);
   458 	r=RxSoundDevice.SetBufferChunkCreate(bufferConfigBuf,chunk);
   459 	if (r!=KErrNone)
   460 		{
   461 		Test.Printf(_L("Buffer configuration not supported(%d)\r\n"),r);
   462 		return(r);
   463 		}
   464 	
   465 	// Set the audio record configuration.
   466 	RxSoundDevice.SetVolume(KSoundMaxVolume);
   467 	PrintConfig(RecordFormatBuf(),Test);
   468 	r=RxSoundDevice.SetAudioFormat(RecordFormatBuf);
   469 	if (r!=KErrNone)
   470 		{
   471 		Test.Printf(_L("Format not supported\r\n"));
   472 		return(r);
   473 		}
   474 
   475 	// Get length in seconds
   476 	TLex cl_seconds(l.NextToken());
   477 	TUint32 tmpSeconds;
   478 	r = cl_seconds.Val(tmpSeconds,EDecimal);
   479 	if (r == KErrNone)
   480 		{
   481 		Test.Printf(_L("Parsed %d seconds\r\n"),tmpSeconds);
   482 		}
   483 	else
   484 		{
   485 		Test.Printf(_L("Parse seconds failed(%d)\r\n"),r);
   486 		tmpSeconds=10;
   487 		}
   488 	TInt bytesToRecord = BytesPerSecond(RecordFormatBuf())*tmpSeconds;	
   489 		
   490 	Test.Next(_L("Recording..."));
   491 	
   492 	// Lay down a file header
   493 	WAVEheader header;
   494 	TPtr8 headerDes((TUint8 *)&header, sizeof(struct WAVEheader), sizeof(struct WAVEheader));
   495 
   496 	// "RIFF"
   497 	header.ckID[0] = 'R'; header.ckID[1] = 'I';
   498 	header.ckID[2] = 'F'; header.ckID[3] = 'F';
   499 	// "WAVE"
   500 	header.wave_ckID[0] = 'W'; header.wave_ckID[1] = 'A';
   501 	header.wave_ckID[2] = 'V'; header.wave_ckID[3] = 'E';
   502 	// "fmt "
   503 	header.fmt_ckID[0] = 'f'; header.fmt_ckID[1] = 'm';
   504 	header.fmt_ckID[2] = 't'; header.fmt_ckID[3] = ' ';
   505 	// "data"
   506 	header.data_ckID[0] = 'd'; header.data_ckID[1] = 'a';
   507 	header.data_ckID[2] = 't'; header.data_ckID[3] = 'a';
   508 
   509 	header.nChannels		= (TUint16)RecordFormatBuf().iChannels;
   510 	header.nSamplesPerSec	= RateInSamplesPerSecond(RecordFormatBuf().iRate);
   511 	header.nBitsPerSample	= 16;
   512 	header.nBlockAlign		= TUint16((RecordFormatBuf().iChannels == 2) ? 4 : 2);
   513 	header.formatTag		= 1;	// type 1 is PCM
   514 	header.fmt_ckSize		= 16;
   515 	header.nAvgBytesPerSec	= BytesPerSecond(RecordFormatBuf());
   516 	header.data_ckSize		= bytesToRecord;
   517 	header.ckSize			= bytesToRecord + sizeof(struct WAVEheader) - 8;
   518 
   519 	Test.Printf(_L("Header rate:%d channels:%d tag:%d bits:%d (%d bytes/s) align %d datalen:%d fmt_ckSize:%d ckSize:%d\r\n"),
   520 			header.nSamplesPerSec, header.nChannels, header.formatTag, header.nBitsPerSample,
   521 			header.nAvgBytesPerSec, header.nBlockAlign, header.data_ckSize, header.fmt_ckSize, header.ckSize, sizeof(struct WAVEheader));
   522 
   523 	r = destination.Write(headerDes);
   524 
   525 	TRequestStatus stat;
   526 	TInt length;
   527 	TPtrC8 buf;
   528 
   529 	TTime startTime;
   530 	startTime.HomeTime();
   531 	
   532 	// Start off by issuing a record request.
   533 	TTime starttime;
   534 	starttime.HomeTime();
   535 	TInt bytesRecorded = 0;
   536 	RxSoundDevice.RecordData(stat,length);
   537 
   538 	TInt pausesToDo = 10;
   539 	pausesToDo = 0;
   540 	FOREVER
   541 		{
   542 		// Wait for the outstanding record request to complete.
   543         User::After(6000);
   544 
   545 		User::WaitForAnyRequest();
   546 		if (stat==KRequestPending)
   547 			return(KErrGeneral);
   548 
   549 		TTime currentTime;
   550 		currentTime.HomeTime();
   551 		TInt64 elapsedTime = currentTime.Int64()-startTime.Int64();	// us
   552 		TTimeIntervalMicroSecondsBuf timeRecordedBuf;
   553 		if(RxSoundDevice.TimeRecorded(timeRecordedBuf) == KErrNone)
   554 			{
   555 			// Compare TimeRecorded with the actual elapsed time. They should be different, but not drift apart too badly...
   556 			TInt32 offset = TInt32(elapsedTime - timeRecordedBuf().Int64());
   557 			Test.Printf(_L("\telapsedTime - TimeRecorded = %d ms\n"), offset/1000);
   558 			}		
   559 			
   560 		// Check whether the record request was succesful.
   561 		TInt retOffset=stat.Int();
   562 		if (retOffset<0)
   563 			{
   564 			Test.Printf(_L("Record failed(%d)\r\n"),retOffset);
   565 			return(retOffset);
   566 			}
   567 		
   568 		// Successfully recorded another buffer so write the recorded data to the record file and release the buffer.
   569 		buf.Set((const TUint8*)(chunk.Base()+retOffset),length);
   570 		r=destination.Write(buf);
   571 		if (r!=KErrNone)
   572 			{
   573 			Test.Printf(_L("File write failed(%d)\r\n"),r);
   574 			return(r);
   575 			}
   576 		r=RxSoundDevice.ReleaseBuffer(retOffset);
   577 		if (r!=KErrNone)
   578 			{
   579 			Test.Printf(_L("Release buffer failed(%d)\r\n"),r);
   580 			return(r);
   581 			}
   582 		
   583 		Test.Printf(_L("Recorded %d more bytes - %d\r\n"),length,retOffset);
   584 
   585 		if((pausesToDo > 0) && (bytesRecorded > bytesToRecord/2))
   586 			{
   587 			--pausesToDo;
   588 			Test.Printf(_L("Pause\r\n"));
   589 			RxSoundDevice.Pause();
   590 			Test.Printf(_L("Paused, sleeping for 0.5 seconds\r\n"));
   591 			User::After(500*1000);
   592             Test.Printf(_L("Resume\r\n"));
   593 			RxSoundDevice.Resume();
   594 			}
   595 		
   596 		// Check whether we have now recorded all the data. If more to record then queue a further request
   597 		bytesRecorded+=length;
   598 		if (bytesRecorded<bytesToRecord)
   599 		    {
   600             Test.Printf(_L("RecordData\r\n"));
   601 			RxSoundDevice.RecordData(stat,length);
   602 		    }
   603 		else
   604 			break;
   605 		}
   606 	
   607 	RxSoundDevice.CancelRecordData();	// Stop the driver from recording.
   608 	
   609 	TTime endtime;
   610 	endtime.HomeTime();
   611 
   612 	TInt64 elapsedTime = endtime.Int64()-starttime.Int64();	// us
   613 	Test.Printf(_L("Delta time = %d\r\n"),I64LOW(elapsedTime));
   614 	Test.Printf(_L("Seconds in buffer: %d (%d)\r\n"), bytesRecorded / header.nAvgBytesPerSec, (bytesRecorded / header.nAvgBytesPerSec)*1000000);
   615 
   616 	if (I64LOW(elapsedTime) <= (bytesRecorded / header.nAvgBytesPerSec)*1000000)
   617 		{
   618 		Test.Printf(_L("Time travelling; record took less time than it should have done\r\n"));
   619 		return(KErrGeneral);
   620 		}
   621 	
   622 	chunk.Close();
   623 	destination.Close();
   624 
   625 	Test.Printf(_L("Record finished\r\n"));
   626 	return(KErrNone);
   627 	}
   628 	
   629 // Quick test block to write the output of the signal generator to a file.
   630 // You'll need to comment out the reference to playdata in writetone and link
   631 // the function into the command line processing (search on this function name).	
   632 /*
   633 LOCAL_C void TestWaveformGenerator()
   634 	{
   635 	
   636 	Test.Next(_L("Testing waveform generator"));
   637 	// Parse the commandline and get a filename to use
   638 	TLex l(CommandLine);
   639 	TParse destinationName;
   640 	if (destinationName.SetNoWild(l.NextToken(),0,0)!=KErrNone)
   641 		{
   642 		Test.Printf(_L("No arg, skipping\r\n"));
   643 		return;
   644 		}
   645 
   646 	// Open the file for writing
   647 	TInt r;
   648 	RFile destination;
   649 	r = destination.Replace(Fs,destinationName.FullName(),EFileWrite);
   650 	if (r!=KErrNone)
   651 		{
   652 		Test.Printf(_L("Open file for write failed(%d)\r\n"), r);
   653 		}
   654 	
   655 	Test.Printf(_L("File opened for write\r\n"));
   656 	Test.Next(_L("Preparing to record data"));
   657 
   658 	// Get the rate
   659 	TLex cl(l.NextToken());
   660 	TUint32 tmpRate;
   661 	TSoundRate rate;
   662 	r = cl.Val(tmpRate,EDecimal);
   663 	if (r == KErrNone && (r=SamplesPerSecondToRate(tmpRate,rate))==KErrNone)
   664 		{
   665 		Test.Printf(_L("Parsed rate: %d\r\n"), tmpRate);
   666 		PlayFormatBuf().iRate = rate;
   667 		}
   668 	else
   669 		{
   670 		Test.Printf(_L("Parse rate failed(%d)\r\n"),r);
   671 		PlayFormatBuf().iRate = ESoundRate32000Hz;
   672 		}
   673 
   674 	// Get number of channels
   675 	TLex cl_chan(l.NextToken());
   676 	TUint32 tmpChannels;
   677 	r = cl_chan.Val(tmpChannels,EDecimal);
   678 	if (r == KErrNone)
   679 		{
   680 		Test.Printf(_L("Parsed %d channels\r\n"),tmpChannels);
   681 		PlayFormatBuf().iChannels = tmpChannels;
   682 		}
   683 	else
   684 		{
   685 		Test.Printf(_L("Parse channels failed(%d)\r\n"), r);
   686 		PlayFormatBuf().iChannels = 2;
   687 		}
   688 
   689 	PlayFormatBuf().iEncoding = ESoundEncoding16BitPCM;
   690 	PrintConfig(PlayFormatBuf(),Test);
   691 	
   692 	TInt bufferSize=BytesPerSecond(PlayFormatBuf())/8;
   693 	TUint8* buffer = (TUint8*)User::Alloc(bufferSize*sizeof(TUint8));
   694 	if (buffer==NULL)
   695 		{
   696 		Test.Printf(_L("Out of memory\r\n"));
   697 		return;
   698 		}
   699 	TPtr8 bufferDes(buffer,bufferSize,bufferSize);	
   700 
   701 	Test.Next(_L("Recording..."));
   702 	TInt i = BytesPerSecond(PlayFormatBuf())*10/bufferSize;
   703 	TInt bytesToRecord = i * bufferSize;
   704 
   705 	// Lay down a file header
   706 	WAVEheader header;
   707 	TPtr8 headerDes((TUint8 *)&header, sizeof(struct WAVEheader), sizeof(struct WAVEheader));
   708 
   709 	// "RIFF"
   710 	header.ckID[0] = 'R'; header.ckID[1] = 'I';
   711 	header.ckID[2] = 'F'; header.ckID[3] = 'F';
   712 	// "WAVE"
   713 	header.wave_ckID[0] = 'W'; header.wave_ckID[1] = 'A';
   714 	header.wave_ckID[2] = 'V'; header.wave_ckID[3] = 'E';
   715 	// "fmt "
   716 	header.fmt_ckID[0] = 'f'; header.fmt_ckID[1] = 'm';
   717 	header.fmt_ckID[2] = 't'; header.fmt_ckID[3] = ' ';
   718 	// "data"
   719 	header.data_ckID[0] = 'd'; header.data_ckID[1] = 'a';
   720 	header.data_ckID[2] = 't'; header.data_ckID[3] = 'a';
   721 
   722 	header.nChannels		= PlayFormatBuf().iChannels;
   723 	header.nSamplesPerSec	= RateInSamplesPerSecond(PlayFormatBuf().iRate);
   724 	header.nBitsPerSample	= 16;
   725 	header.nBlockAlign		= 4;
   726 	header.formatTag		= 1;
   727 	header.fmt_ckSize		= 16;
   728 	header.nAvgBytesPerSec	= BytesPerSecond(PlayFormatBuf());
   729 	header.data_ckSize		= bytesToRecord;
   730 	header.ckSize			= bytesToRecord + sizeof(struct WAVEheader) - 8;
   731 
   732 	Test.Printf(_L("Header rate:%d channels:%d tag:%d bits:%d (%d bytes/s) align %d datalen:%d fmt_ckSize:%d ckSize:%d\r\n"),
   733 			header.nSamplesPerSec, header.nChannels, header.formatTag, header.nBitsPerSample,
   734 			header.nAvgBytesPerSec, header.nBlockAlign, header.data_ckSize, header.fmt_ckSize, header.ckSize);
   735 
   736 	r = destination.Write(headerDes);
   737 
   738 	MakeSineTable(PlayFormatBuf());
   739 	SetToneFrequency(440,PlayFormatBuf()); // 'A'
   740 
   741 	while(--i>0)
   742 		{
   743 		WriteTone(bufferDes,PlayFormatBuf());
   744 		r = destination.Write(bufferDes);
   745 		if (r!=KErrNone)
   746 			{
   747 			Test.Printf(_L("Write failed(%d)\r\n"),r);
   748 			break;
   749 			}
   750 		}
   751 	Test.Printf(_L("Finished\r\n"));
   752 
   753 	delete buffer;
   754 	destination.Close();
   755 	}
   756 */
   757 LOCAL_C void TestUnloadDrivers()
   758 	{
   759 	TInt r=User::FreeLogicalDevice(KDevSoundScName);
   760 	Test.Printf(_L("Unloading %S.LDD - %d\r\n"),&KDevSoundScName,r);
   761 	CHECK_NOERROR(r);
   762 	
   763 	TName pddName(KDevSoundScName);
   764 	_LIT(KPddWildcardExtension,".*");
   765 	pddName.Append(KPddWildcardExtension);
   766 	TFindPhysicalDevice findPD(pddName);
   767 	TFullName findResult;
   768 	r=findPD.Next(findResult);
   769 	while (r==KErrNone)
   770 		{
   771 		r=User::FreePhysicalDevice(findResult);
   772 		Test.Printf(_L("Unloading %S.PDD - %d\r\n"),&findResult,r);
   773 		CHECK_NOERROR(r);
   774 		findPD.Find(pddName); // Reset the find handle now that we have deleted something from the container.
   775 		r=findPD.Next(findResult);
   776 		} 
   777 	}
   778 	
   779 TInt E32Main()
   780 	{
   781 	TInt r;
   782 
   783 	__UHEAP_MARK;
   784 
   785 	Test.Title();
   786 
   787 	Test.Start(_L("Load"));
   788 	if (Load()==KErrNotFound)
   789 		{
   790 		Test.Printf(_L("Shared chunk sound driver not supported - test skipped\r\n"));
   791 		Test.End();
   792 		Test.Close();
   793 		__UHEAP_MARKEND;
   794 		return(KErrNone);
   795 		}
   796 
   797 	__KHEAP_MARK;
   798 
   799 	Test.Next(_L("Open playback channel"));
   800 	r = TxSoundDevice.Open(KSoundScTxUnit0);
   801 	if (r!=KErrNone)
   802 		{
   803 		Test.Printf(_L("Open playback channel error(%d)\r\n"),r);
   804 		Test(0);
   805 		}
   806 	
   807 	Test.Next(_L("Open record channel"));
   808 	r = RxSoundDevice.Open(KSoundScRxUnit0);
   809 	if (r!=KErrNone)
   810 		{
   811 		Test.Printf(_L("Open record channel error(%d)\r\n"),r);
   812 		Test(0);
   813 		}
   814 	
   815 	Test.Next(_L("Query play formats supported"));
   816 	TxSoundDevice.Caps(PlayCapsBuf);
   817 	TSoundFormatsSupportedV02 playCaps=PlayCapsBuf();
   818 	PrintCaps(playCaps,Test);
   819 
   820 	Test.Next(_L("Query record formats supported"));
   821 	RxSoundDevice.Caps(RecordCapsBuf);
   822 	TSoundFormatsSupportedV02 recordCaps=RecordCapsBuf();
   823 	PrintCaps(recordCaps,Test);
   824 	
   825 	Test.Next(_L("Connect to the file server"));
   826 	r = Fs.Connect();
   827 	if (r!=KErrNone)
   828 		{
   829 		Test.Printf(_L("Connect to the file server error(%d)\r\n"),r);
   830 		Test(0);
   831 		}
   832 		
   833 	if (User::CommandLineLength())
   834 		{
   835 		User::CommandLine(CommandLine);
   836 		TLex l(CommandLine);
   837 		TPtrC token=l.NextToken();
   838 
   839 		TInt count=0;
   840 		while (token.Length()!=0)
   841 			{
   842 			++count;
   843 			token.Set(l.NextToken());
   844 			}
   845 		Test.Printf(_L("Command line %d parameters\r\n"),count);
   846 
   847 		if (count==1)		// If 1 parameter try playing a file
   848 			r=WavPlay();
   849 		else if (count)		// If there is more than 1 parameter, try recording
   850 			r=WavRecord();
   851 		
   852 		//TestWaveformGenerator();
   853 			
   854 		}
   855 	
   856 	Fs.Close();
   857 	
   858 	Test.Next(_L("Close channels"));
   859 	RxSoundDevice.Close();
   860 	TxSoundDevice.Close();
   861 	
   862 	__KHEAP_MARKEND;
   863 
   864 	// Now that both the channels are closed, unload the LDD and the PDDs.
   865 	TestUnloadDrivers();
   866 
   867 	Test(r==KErrNone);
   868 
   869 	Test.End();
   870 	Test.Close();
   871 
   872 	Cleanup();
   873 	
   874 	__UHEAP_MARKEND;
   875 
   876 	return(KErrNone);
   877 	}