kerneltest/e32test/multimedia/t_soundwav.cpp
changeset 43 96e5fb8b040d
equal deleted inserted replaced
-1:000000000000 43:96e5fb8b040d
       
     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 	}