diff -r 000000000000 -r a41df078684a kerneltest/e32test/multimedia/t_soundwav.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/multimedia/t_soundwav.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,831 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32test\multimedia\t_soundwav.cpp +// Record wav to a file "t_soundwav filename [rate] [channels] [seconds]" where filename does not exist. +// Play a wav file "t_soundwav filename" where filename exists. +// +// + +/** + @file Shared chunk sound driver WAV file player and recorder utility. +*/ + +#include +#include +#include +#include "t_soundutils.h" +#include + +#define CHECK(aValue) {Test(aValue);} +#define CHECK_NOERROR(aValue) { TInt v=(aValue); if(v) { Test.Printf(_L("Error value = %d\n"),v); Test(EFalse,__LINE__); }} +#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__); }} + +_LIT(KSndLddFileName,"ESOUNDSC.LDD"); +_LIT(KSndPddFileName,"SOUNDSC.PDD"); + +RTest Test(_L("T_SOUNDWAV")); +RSoundSc TxSoundDevice; +RSoundSc RxSoundDevice; + +TSoundFormatsSupportedV02Buf RecordCapsBuf; +TSoundFormatsSupportedV02Buf PlayCapsBuf; +TCurrentSoundFormatV02Buf PlayFormatBuf; +TCurrentSoundFormatV02Buf RecordFormatBuf; + +TBuf<256> CommandLine; +RFs Fs; + +// +// This is the WAV header structure used for playing and recording files +// +struct WAVEheader + { + char ckID[4]; // chunk id 'RIFF' + TUint32 ckSize; // chunk size + char wave_ckID[4]; // wave chunk id 'WAVE' + char fmt_ckID[4]; // format chunk id 'fmt ' + TUint32 fmt_ckSize; // format chunk size + TUint16 formatTag; // format tag currently pcm + TUint16 nChannels; // number of channels + TUint32 nSamplesPerSec; // sample rate in hz + TUint32 nAvgBytesPerSec; // average bytes per second + TUint16 nBlockAlign; // number of bytes per sample + TUint16 nBitsPerSample; // number of bits in a sample + char data_ckID[4]; // data chunk id 'data' + TUint32 data_ckSize; // length of data chunk + }; + +LOCAL_C TInt SamplesPerSecondToRate(TInt aRateInSamplesPerSecond,TSoundRate& aRate) + { + switch (aRateInSamplesPerSecond) + { + case 7350: aRate=ESoundRate7350Hz; break; + case 8000: aRate=ESoundRate8000Hz; break; + case 8820: aRate=ESoundRate8820Hz; break; + case 9600: aRate=ESoundRate9600Hz; break; + case 11025: aRate=ESoundRate11025Hz; break; + case 12000: aRate=ESoundRate12000Hz; break; + case 14700: aRate=ESoundRate14700Hz; break; + case 16000: aRate=ESoundRate16000Hz; break; + case 22050: aRate=ESoundRate22050Hz; break; + case 24000: aRate=ESoundRate24000Hz; break; + case 29400: aRate=ESoundRate29400Hz; break; + case 32000: aRate=ESoundRate32000Hz; break; + case 44100: aRate=ESoundRate44100Hz; break; + case 48000: aRate=ESoundRate48000Hz; break; + default: return(KErrArgument); + }; + return(KErrNone); + } + +LOCAL_C TInt RecordBufferSizeInBytes(TCurrentSoundFormatV02& aFormat) + { + switch (aFormat.iRate) + { + case ESoundRate7350Hz: return(8192); + case ESoundRate8000Hz: return(8192); + case ESoundRate8820Hz: return(12288); + case ESoundRate9600Hz: return(12288); + case ESoundRate11025Hz: return(12288); + case ESoundRate12000Hz: return(12288); + case ESoundRate14700Hz: return(12288); + case ESoundRate16000Hz: return(12288); + case ESoundRate22050Hz: return(16384); + case ESoundRate24000Hz: return(16384); + case ESoundRate29400Hz: return(24576); + case ESoundRate32000Hz: return(24576); + case ESoundRate44100Hz: return(32768); + case ESoundRate48000Hz: return(32768); + default: return(32768); + }; + } + +LOCAL_C TInt Load() + { + TInt r; + + Test.Start(_L("Load sound PDD")); + r=User::LoadPhysicalDevice(KSndPddFileName); + if (r==KErrNotFound) + { + Test.End(); + return(r); + } + CHECK(r==KErrNone || r==KErrAlreadyExists); + + Test.Next(_L("Load sound LDD")); + r=User::LoadLogicalDevice(KSndLddFileName); + CHECK(r==KErrNone || r==KErrAlreadyExists); + + Test.End(); + return(KErrNone); + } + +LOCAL_C TInt WavPlay() + { + RChunk chunk; + + // Parse the commandline and get a filename to use + TLex l(CommandLine); + TFileName thisfile=RProcess().FileName(); + TPtrC token=l.NextToken(); + if (token.MatchF(thisfile)==0) + token.Set(l.NextToken()); + + if (token.Length()==0) + { + // No args, skip to end + Test.Printf(_L("Invalid configuration\r\n")); + return(KErrArgument); + } + + Test.Next(_L("Play Wav file")); + + // Assume that the argument is a WAV filename + TFileName wavFilename=token; + TInt r; + RFile source; + r = source.Open(Fs,wavFilename,EFileRead); + if (r!=KErrNone) + { + Test.Printf(_L("Open failed(%d)\r\n"), r); + return(r); + } + + // Read the pcm header + WAVEheader header; + TPtr8 headerDes((TUint8 *)&header,sizeof(struct WAVEheader),sizeof(struct WAVEheader)); + r = source.Read(headerDes); + if (r!=KErrNone) + { + source.Close(); + return(r); + } + Test.Printf(_L("Header Read %d bytes\r\n"),headerDes.Size()); + + if (headerDes.Size() != sizeof(struct WAVEheader)) // EOF + { + Test.Printf(_L("Couldn't read a header(%d bytes)\r\n"),headerDes.Size()); + source.Close(); + return(KErrCorrupt); + } + + 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"), + header.nSamplesPerSec, header.nChannels, header.formatTag, header.nBitsPerSample, + header.nAvgBytesPerSec, header.nBlockAlign, header.data_ckSize, header.fmt_ckSize, header.ckSize); + + if (header.formatTag != 1) // not pcm + { + Test.Printf(_L("Format not PCM(%d)\r\n"),header.formatTag); + source.Close(); + return(KErrNotSupported); + } + + if (header.nBitsPerSample != 16) // not 16 bit + { + Test.Printf(_L("Format not 16 bit PCM(%d bits)\r\n"),header.nBitsPerSample); + source.Close(); + return(KErrNotSupported); + } + + TSoundRate rate; + if (SamplesPerSecondToRate(header.nSamplesPerSec,rate)!=KErrNone) + { + Test.Printf(_L("Format specifies a rate not supported(%d)\r\n"),header.nSamplesPerSec); + source.Close(); + return(KErrNotSupported); + } + + TxSoundDevice.AudioFormat(PlayFormatBuf); // Read back the current setting which must be valid. + PlayFormatBuf().iChannels = header.nChannels; + PlayFormatBuf().iRate = rate; + PlayFormatBuf().iEncoding = ESoundEncoding16BitPCM; + + // Set the play buffer configuration. + TInt bufSize=BytesPerSecond(PlayFormatBuf())/8; // Large enough to hold 1/8th second of data. + bufSize&=~(header.nBlockAlign-1); // Keep the buffer length a multiple of the bytes per sample (assumes 16bitPCM, 1 or 2 chans). + if (PlayCapsBuf().iRequestMinSize) + bufSize&=~(PlayCapsBuf().iRequestMinSize-1); // Keep the buffer length valid for the driver. + TTestSharedChunkBufConfig bufferConfig; + bufferConfig.iNumBuffers=3; + bufferConfig.iBufferSizeInBytes=bufSize; + bufferConfig.iFlags=0; + PrintBufferConf(bufferConfig,Test); + TPckg bufferConfigBuf(bufferConfig); + r=TxSoundDevice.SetBufferChunkCreate(bufferConfigBuf,chunk); + if (r!=KErrNone) + { + Test.Printf(_L("Buffer configuration not supported(%d)\r\n"),r); + source.Close(); + return(r); + } + TxSoundDevice.GetBufferConfig(bufferConfigBuf); // Read back the configuration - to get the buffer offsets + CHECK(bufferConfig.iBufferSizeInBytes==bufSize); + + // Set the audio play configuration. + TxSoundDevice.SetVolume(KSoundMaxVolume - (KSoundMaxVolume / 4)); // set volume to 75% + PrintConfig(PlayFormatBuf(),Test); + r=TxSoundDevice.SetAudioFormat(PlayFormatBuf); + if (r!=KErrNone) + { + Test.Printf(_L("Format not supported\r\n")); + source.Close(); + chunk.Close(); + return(r); + } + TxSoundDevice.ResetBytesTransferred(); + + TInt32 bytesToPlay = header.data_ckSize; + TTime starttime; + starttime.HomeTime(); + + TRequestStatus stat[3]; + TPtr8* tPtr[3]; + TInt i; + for (i=0;i<3;i++) + tPtr[i]=new TPtr8(NULL,0); + + // Start off by issuing a play request for each buffer (assuming that the file is long enough). Use the full size + // of each buffer. + TInt stillToRead=bytesToPlay; + TInt stillNotPlayed=bytesToPlay; + TUint flags; + for (i=0 ; i<3 ; i++) + { + // Setup the descriptor for reading in the data from the file. + tPtr[i]->Set(chunk.Base()+bufferConfig.iBufferOffsetList[i],0,bufSize); + + // If there is still data to read to play then read this into the descriptor + // and then write it to the driver. + if (stillToRead) + { + r=source.Read(*tPtr[i],Min(stillToRead,bufSize)); + if (r!=KErrNone) + { + Test.Printf(_L("Initial file read error(%d)\r\n"),r); + source.Close(); + chunk.Close(); + return(r); + } + stillToRead-=tPtr[i]->Length(); + flags=(stillToRead>0)?0:KSndFlagLastSample; + TxSoundDevice.PlayData(stat[i],bufferConfig.iBufferOffsetList[i],tPtr[i]->Length(),flags); + } + else + stat[i]=KRequestPending; + } + + FOREVER + { + // Wait for any one of the outstanding play requests to complete. + User::WaitForAnyRequest(); + + // Work out which buffer this applies to + for (i=0 ; i<3 ; i++) + { + if (stat[i]!=KRequestPending) + break; + } + if (i>=3) + { + Test.Printf(_L("I/O error\r\n")); + source.Close(); + chunk.Close(); + return(KErrGeneral); + } + + // Check that the transfer was succesful and whether we have now played all the file. + if (stat[i]!=KErrNone) + { + Test.Printf(_L("Play error(%d)\r\n"),stat[i].Int()); + source.Close(); + chunk.Close(); + return(stat[i].Int()); + } + Test.Printf(_L("Played %d bytes(%d) - %d\r\n"),tPtr[i]->Length(),i,stat[i].Int()); + stillNotPlayed-=tPtr[i]->Length(); + CHECK(stillNotPlayed>=0); + if (!stillNotPlayed) + break; + + // Still more to be played so read the next part of the file into the descriptor for this + // buffer and then write it to the driver. + if (stillToRead) + { + TInt len=Min(stillToRead,bufSize); + + // If we've got to the end of the file and the driver is particular about the request length then + // zero fill the entire buffer so we can play extra zeros after the last sample from the file. + if (lenFillZ(bufSize); + + // Read the next part of the file + r=source.Read(*tPtr[i],len); // This will alter the length of the descriptor + if (r!=KErrNone) + { + Test.Printf(_L("File read error(%d)\r\n"),r); + source.Close(); + chunk.Close(); + return(r); + } + stillToRead-=tPtr[i]->Length(); + + // If we've got to the end of the file and the driver is particular about the request length then + // round up the length to the next valid boundary. This is OK since we zero filled. + if (tPtr[i]->Length() < bufSize && PlayCapsBuf().iRequestMinSize) + { + TUint m=PlayCapsBuf().iRequestMinSize-1; + len=(tPtr[i]->Length() + m) & ~m; + } + + // Write it to the driver. + flags=(stillToRead>0)?0:KSndFlagLastSample; + TxSoundDevice.PlayData(stat[i],bufferConfig.iBufferOffsetList[i],len,flags); + } + else + stat[i]=KRequestPending; + } + + // Delete all the variables again. + for (i=0 ; i<3 ; i++) + delete tPtr[i]; + + TTime endtime; + endtime.HomeTime(); + + Test.Printf(_L("Done playing\r\n")); + Test.Printf(_L("Bytes played = %d\r\n"),TxSoundDevice.BytesTransferred()); + Test.Printf(_L("Delta time = %d\r\n"),endtime.Int64()-starttime.Int64()); + + chunk.Close(); + source.Close(); + return(KErrNone); + } + +LOCAL_C TInt WavRecord() + { + // Parse the commandline and get a filename to use + TLex l(CommandLine); + TParse destinationName; + if (destinationName.SetNoWild(l.NextToken(),0,0)!=KErrNone) + { + Test.Printf(_L("No arg, skipping\r\n")); + return(KErrArgument); + } + Test.Next(_L("Record Wav file")); + + // Open the file for writing + TInt r; + RFile destination; + r = destination.Replace(Fs,destinationName.FullName(),EFileWrite); + if (r!=KErrNone) + { + Test.Printf(_L("Open file for write failed(%d)\n"), r); + return(r); + } + Test.Printf(_L("File opened for write\r\n")); + + Test.Next(_L("Preparing to record")); + + // Get the rate + TLex cl(l.NextToken()); + TUint32 tmpRate; + TSoundRate rate; + r = cl.Val(tmpRate,EDecimal); + if (r == KErrNone && (r=SamplesPerSecondToRate(tmpRate,rate))==KErrNone) + { + Test.Printf(_L("Parsed rate: %d\r\n"), tmpRate); + RecordFormatBuf().iRate = rate; + } + else + { + Test.Printf(_L("Parse rate failed(%d)\r\n"),r); + RecordFormatBuf().iRate = ESoundRate32000Hz; + } + + // Get number of channels + TLex cl_chan(l.NextToken()); + TUint32 tmpChannels; + r = cl_chan.Val(tmpChannels,EDecimal); + if (r == KErrNone) + { + Test.Printf(_L("Parsed %d channels\r\n"),tmpChannels); + RecordFormatBuf().iChannels = tmpChannels; + } + else + { + Test.Printf(_L("Parse channels failed(%d)\r\n"), r); + RecordFormatBuf().iChannels = 2; + } + + RecordFormatBuf().iEncoding = ESoundEncoding16BitPCM; + + // Set the record buffer configuration. + RChunk chunk; + TTestSharedChunkBufConfig bufferConfig; + bufferConfig.iNumBuffers=4; + bufferConfig.iBufferSizeInBytes=RecordBufferSizeInBytes(RecordFormatBuf()); + if (RecordCapsBuf().iRequestMinSize) + bufferConfig.iBufferSizeInBytes&=~(RecordCapsBuf().iRequestMinSize-1); // Keep the buffer length valid for the driver. + bufferConfig.iFlags=0; + PrintBufferConf(bufferConfig,Test); + TPckg bufferConfigBuf(bufferConfig); + r=RxSoundDevice.SetBufferChunkCreate(bufferConfigBuf,chunk); + if (r!=KErrNone) + { + Test.Printf(_L("Buffer configuration not supported(%d)\r\n"),r); + return(r); + } + + // Set the audio record configuration. + RxSoundDevice.SetVolume(KSoundMaxVolume); + PrintConfig(RecordFormatBuf(),Test); + r=RxSoundDevice.SetAudioFormat(RecordFormatBuf); + if (r!=KErrNone) + { + Test.Printf(_L("Format not supported\r\n")); + return(r); + } + + // Get length in seconds + TLex cl_seconds(l.NextToken()); + TUint32 tmpSeconds; + r = cl_seconds.Val(tmpSeconds,EDecimal); + if (r == KErrNone) + { + Test.Printf(_L("Parsed %d seconds\r\n"),tmpSeconds); + } + else + { + Test.Printf(_L("Parse seconds failed(%d)\r\n"),r); + tmpSeconds=10; + } + TInt bytesToRecord = BytesPerSecond(RecordFormatBuf())*tmpSeconds; + + Test.Next(_L("Recording...")); + + // Lay down a file header + WAVEheader header; + TPtr8 headerDes((TUint8 *)&header, sizeof(struct WAVEheader), sizeof(struct WAVEheader)); + + // "RIFF" + header.ckID[0] = 'R'; header.ckID[1] = 'I'; + header.ckID[2] = 'F'; header.ckID[3] = 'F'; + // "WAVE" + header.wave_ckID[0] = 'W'; header.wave_ckID[1] = 'A'; + header.wave_ckID[2] = 'V'; header.wave_ckID[3] = 'E'; + // "fmt " + header.fmt_ckID[0] = 'f'; header.fmt_ckID[1] = 'm'; + header.fmt_ckID[2] = 't'; header.fmt_ckID[3] = ' '; + // "data" + header.data_ckID[0] = 'd'; header.data_ckID[1] = 'a'; + header.data_ckID[2] = 't'; header.data_ckID[3] = 'a'; + + header.nChannels = (TUint16)RecordFormatBuf().iChannels; + header.nSamplesPerSec = RateInSamplesPerSecond(RecordFormatBuf().iRate); + header.nBitsPerSample = 16; + header.nBlockAlign = TUint16((RecordFormatBuf().iChannels == 2) ? 4 : 2); + header.formatTag = 1; // type 1 is PCM + header.fmt_ckSize = 16; + header.nAvgBytesPerSec = BytesPerSecond(RecordFormatBuf()); + header.data_ckSize = bytesToRecord; + header.ckSize = bytesToRecord + sizeof(struct WAVEheader) - 8; + + 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"), + header.nSamplesPerSec, header.nChannels, header.formatTag, header.nBitsPerSample, + header.nAvgBytesPerSec, header.nBlockAlign, header.data_ckSize, header.fmt_ckSize, header.ckSize, sizeof(struct WAVEheader)); + + r = destination.Write(headerDes); + + TRequestStatus stat; + TInt length; + TPtrC8 buf; + + // Start off by issuing a record request. + TTime starttime; + starttime.HomeTime(); + TInt bytesRecorded = 0; + RxSoundDevice.RecordData(stat,length); + + FOREVER + { + // Wait for the outstanding record request to complete. + User::WaitForAnyRequest(); + if (stat==KRequestPending) + return(KErrGeneral); + + // Check whether the record request was succesful. + TInt retOffset=stat.Int(); + if (retOffset<0) + { + Test.Printf(_L("Record failed(%d)\r\n"),retOffset); + return(retOffset); + } + + // Successfully recorded another buffer so write the recorded data to the record file and release the buffer. + buf.Set((const TUint8*)(chunk.Base()+retOffset),length); + r=destination.Write(buf); + if (r!=KErrNone) + { + Test.Printf(_L("File write failed(%d)\r\n"),r); + return(r); + } + r=RxSoundDevice.ReleaseBuffer(retOffset); + if (r!=KErrNone) + { + Test.Printf(_L("Release buffer failed(%d)\r\n"),r); + return(r); + } + + Test.Printf(_L("Recorded %d more bytes - %d\r\n"),length,retOffset); + + // Check whether we have now recorded all the data. If more to record then queue a further request + bytesRecorded+=length; + if (bytesRecorded0) + { + WriteTone(bufferDes,PlayFormatBuf()); + r = destination.Write(bufferDes); + if (r!=KErrNone) + { + Test.Printf(_L("Write failed(%d)\r\n"),r); + break; + } + } + Test.Printf(_L("Finished\r\n")); + + delete buffer; + destination.Close(); + } +*/ +LOCAL_C void TestUnloadDrivers() + { + TInt r=User::FreeLogicalDevice(KDevSoundScName); + Test.Printf(_L("Unloading %S.LDD - %d\r\n"),&KDevSoundScName,r); + CHECK_NOERROR(r); + + TName pddName(KDevSoundScName); + _LIT(KPddWildcardExtension,".*"); + pddName.Append(KPddWildcardExtension); + TFindPhysicalDevice findPD(pddName); + TFullName findResult; + r=findPD.Next(findResult); + while (r==KErrNone) + { + r=User::FreePhysicalDevice(findResult); + Test.Printf(_L("Unloading %S.PDD - %d\r\n"),&findResult,r); + CHECK_NOERROR(r); + findPD.Find(pddName); // Reset the find handle now that we have deleted something from the container. + r=findPD.Next(findResult); + } + } + +TInt E32Main() + { + TInt r; + + __UHEAP_MARK; + + Test.Title(); + + Test.Start(_L("Load")); + if (Load()==KErrNotFound) + { + Test.Printf(_L("Shared chunk sound driver not supported - test skipped\r\n")); + Test.End(); + Test.Close(); + __UHEAP_MARKEND; + return(KErrNone); + } + + __KHEAP_MARK; + + Test.Next(_L("Open playback channel")); + r = TxSoundDevice.Open(KSoundScTxUnit0); + if (r!=KErrNone) + { + Test.Printf(_L("Open playback channel error(%d)\r\n"),r); + Test(0); + } + + Test.Next(_L("Open record channel")); + r = RxSoundDevice.Open(KSoundScRxUnit0); + if (r!=KErrNone) + { + Test.Printf(_L("Open record channel error(%d)\r\n"),r); + Test(0); + } + + Test.Next(_L("Query play formats supported")); + TxSoundDevice.Caps(PlayCapsBuf); + TSoundFormatsSupportedV02 playCaps=PlayCapsBuf(); + PrintCaps(playCaps,Test); + + Test.Next(_L("Query record formats supported")); + RxSoundDevice.Caps(RecordCapsBuf); + TSoundFormatsSupportedV02 recordCaps=RecordCapsBuf(); + PrintCaps(recordCaps,Test); + + Test.Next(_L("Connect to the file server")); + r = Fs.Connect(); + if (r!=KErrNone) + { + Test.Printf(_L("Connect to the file server error(%d)\r\n"),r); + Test(0); + } + + if (User::CommandLineLength()) + { + User::CommandLine(CommandLine); + TLex l(CommandLine); + TPtrC token=l.NextToken(); + + TInt count=0; + while (token.Length()!=0) + { + ++count; + token.Set(l.NextToken()); + } + Test.Printf(_L("Command line %d parameters\r\n"),count); + + if (count==1) // If 1 parameter try playing a file + r=WavPlay(); + else if (count) // If there is more than 1 parameter, try recording + r=WavRecord(); + + //TestWaveformGenerator(); + + } + + Fs.Close(); + + Test.Next(_L("Close channels")); + RxSoundDevice.Close(); + TxSoundDevice.Close(); + + __KHEAP_MARKEND; + + // Now that both the channels are closed, unload the LDD and the PDDs. + TestUnloadDrivers(); + + Test(r==KErrNone); + + Test.End(); + Test.Close(); + + Cleanup(); + + __UHEAP_MARKEND; + + return(KErrNone); + }