/*********************************************************************** * * Copyright (c) 2001 Palm Computing, Inc. or its subsidiaries. * All rights reserved. * ***********************************************************************/ /*********************************************************************** * * File: * Wave.c * * Description: * WAV functions. Very bad code. But good enough fo the sample code. * * Author: * November 22, 2002 Created by Nicolas Pabion * ***********************************************************************/ #include "Wave.h" #define MisalignedReadBEUInt16(pointer, offset) \ (((UInt16)(*(((UInt8 *)(pointer)) + offset + sizeof(UInt8)))) | \ (((UInt16)(*(((UInt8 *)(pointer)) + offset))) << 8)) #define MisalignedReadBEUInt32(pointer, offset) \ (((UInt32)(MisalignedReadBEUInt16((((UInt8 *)(pointer)) + sizeof(UInt16)), offset))) | \ (((UInt32)(MisalignedReadBEUInt16(pointer, offset))) << 16)) #define Swap16(n) (((((UInt16) n) << 8) & 0xFF00) | \ ((((UInt16) n) >> 8) & 0x00FF)) #define Swap32(n) (((((UInt32) n) << 24) & 0xFF000000) | \ ((((UInt32) n) << 8) & 0x00FF0000) | \ ((((UInt32) n) >> 8) & 0x0000FF00) | \ ((((UInt32) n) >> 24) & 0x000000FF)) #define TYPE_RIFF (Swap32('FFIR')) #define TYPE_WAVE (Swap32('EVAW')) #define TYPE_FMT (Swap32(' tmf')) #define TYPE_DATA (Swap32('atad')) /*********************************************************************** * * FUNCTION: PrvReadFile * * DESCRIPTION: * * PARAMETERS: * * RETURNED: * ***********************************************************************/ static Err PrvReadFile(UInt8 *bufferP, UInt32 *bufferSizeP, PlayerInfo *playerInfoP) { Err err = errNone; UInt32 dataRead = 0; if(playerInfoP->fileType == VFS_File) { err = VFSFileRead(playerInfoP->file.fileRef, *bufferSizeP, bufferP, &dataRead); } else { dataRead = FileRead(playerInfoP->file.fileHandle, bufferP, 1, *bufferSizeP, &err); } *bufferSizeP = dataRead; return err; } /*********************************************************************** * * FUNCTION: PrvSeekFile * * DESCRIPTION: * * PARAMETERS: * * RETURNED: * ***********************************************************************/ static Err PrvSeekFile(UInt32 offset, PlayerInfo *playerInfoP) { Err err = errNone; if(playerInfoP->fileType == VFS_File) { err = VFSFileSeek(playerInfoP->file.fileRef, vfsOriginBeginning, offset); } else { err = FileSeek(playerInfoP->file.fileHandle, offset, fileOriginBeginning); } return err; } /*********************************************************************** * * FUNCTION: ReadWAVEHeader * * DESCRIPTION: * * PARAMETERS: * * RETURNED: * ***********************************************************************/ Err ReadWAVEHeader(PlayerInfo *playerInfoP) { WaveHeader header; Err err = -1; UInt32 type = 0; UInt32 size = 0; UInt32 dataRead = 256; UInt32 dataUsed = 0; UInt32 dataOffset = 0; Boolean dataFound = false; UInt8 *tempP = NULL; UInt8 *bufferP = MemPtrNew(256); if( !bufferP ) goto Done; tempP = bufferP; MemSet(&header, sizeof(header), 0); // Read a chunk PrvReadFile(bufferP, &dataRead, playerInfoP); // Make sure we have enough to read from if( dataRead < 44) goto Done; if( MisalignedReadBEUInt32(bufferP, 0) != 'RIFF' ) goto Done; // Check Wave if( MisalignedReadBEUInt32(bufferP, 8) != 'WAVE' ) goto Done; bufferP += 12; dataUsed -= 12; dataOffset += 20; // Look for the fmt and data section while( !dataFound ) { // Read more if needed if( dataRead - dataUsed < 4 ) { dataRead = 256; dataUsed = 0; PrvReadFile(bufferP, &dataRead, playerInfoP); } // Make sure we have enough data if( dataRead < 4 ) goto Done; type = MisalignedReadBEUInt32(bufferP, 0); size = Swap32(MisalignedReadBEUInt32(bufferP, 4)); // Check for the data section if( type == 'data' ) { // We're done dataFound = true; } else if( type == 'fmt ') { UInt16 format = 0; UInt16 numChannels = 0; UInt32 byteRate = 0; UInt16 blockAlign = 0; UInt16 bitsPerSample = 0; // Read the fmt section format = Swap16(MisalignedReadBEUInt16(bufferP, 8)); if( format != WAVE_TYPE_PCM && format != WAVE_TYPE_IMAADPCM ) goto Done; playerInfoP->streamFormat = (format==WAVE_TYPE_PCM)?sndFormatPCM:sndFormatIMA_ADPCM; // Get the number of channels numChannels = Swap16(MisalignedReadBEUInt16(bufferP, 10)); if( numChannels < 1 || numChannels > 2) goto Done; playerInfoP->streamWidth = (numChannels==1)?sndMono:sndStereo; // Get the sample rate playerInfoP->sampleRate = Swap32(MisalignedReadBEUInt32(bufferP, 12)); if( playerInfoP->sampleRate < 8000 || playerInfoP->sampleRate > 96000 ) goto Done; // Get the byte rate byteRate = Swap32(MisalignedReadBEUInt32(bufferP, 16)); // Get the block align blockAlign = Swap16(MisalignedReadBEUInt16(bufferP, 20)); if( blockAlign & (blockAlign - 1) ) goto Done; // Get the number of bits per sample bitsPerSample = Swap16(MisalignedReadBEUInt16(bufferP, 22)); if( format == WAVE_TYPE_PCM ) { if( blockAlign != ((numChannels * bitsPerSample) / 8)) goto Done; if( bitsPerSample != 8 && bitsPerSample != 16 ) goto Done; playerInfoP->sampleType = (bitsPerSample==8)?sndUInt8:sndInt16Little; } else // ADPCM { if( blockAlign < 256 ) goto Done; if( bitsPerSample != 4) goto Done; playerInfoP->blockAlign = blockAlign; } bufferP += size + 8; dataUsed -= size + 8; dataOffset += size + 8; } else { dataOffset += 8 + size; dataUsed -= 8 + size; bufferP += 8 + size; } } // Advance the file to the right position err = PrvSeekFile(dataOffset, playerInfoP); if(err) goto Done; // Made it! err = errNone; Done: if( tempP ) MemPtrFree(tempP); return err; } /*********************************************************************** * * FUNCTION: WritePCM_WAVEHeader * * DESCRIPTION: * * PARAMETERS: * * RETURNED: * ***********************************************************************/ Err WritePCM_WAVEHeader(PlayerInfo *playerInfoP) { WavePCM pcm; Err err = errNone; UInt32 dataWritten = 0; UInt16 numChannels = (playerInfoP->streamWidth==sndMono)?1:2; UInt32 bitsPerSample = (playerInfoP->sampleType==sndUInt8)?8:16; UInt32 blockAlign = numChannels * bitsPerSample / 8; // RIFF Header pcm.header.riff.header.type = TYPE_RIFF; pcm.header.riff.header.size = Swap32(sizeof(WavePCM) + playerInfoP->dataSize - sizeof(ChunkHeader)); pcm.header.waveType = TYPE_WAVE; // fmt sub-chunk pcm.header.fmt.header.type = TYPE_FMT; pcm.header.fmt.header.size = Swap32(sizeof(FmtSubChunk) - sizeof(ChunkHeader)); pcm.header.fmt.audioFormat = Swap16(WAVE_TYPE_PCM); pcm.header.fmt.channels = Swap16(numChannels); pcm.header.fmt.sampleRate = Swap32(playerInfoP->sampleRate); pcm.header.fmt.avgByteRate = Swap32(playerInfoP->sampleRate * blockAlign); pcm.header.fmt.blockAlign = Swap16(blockAlign); pcm.header.fmt.bitsPerSample= Swap16(bitsPerSample); // data sub-chunk pcm.data.type = TYPE_DATA; pcm.data.size = Swap32(playerInfoP->dataSize); if(playerInfoP->fileType == VFS_File) { err = VFSFileWrite(playerInfoP->file.fileRef, sizeof(pcm), &pcm, &dataWritten); } else { dataWritten = FileWrite(playerInfoP->file.fileHandle, &pcm, 1, sizeof(pcm), &err); } return err; } /*********************************************************************** * * FUNCTION: WriteADPCM_WAVEHeader * * DESCRIPTION: * * PARAMETERS: * * RETURNED: * ***********************************************************************/ Err WriteADPCM_WAVEHeader(PlayerInfo *playerInfoP) { WaveIMAADPCM adpcm; Err err = errNone; UInt32 dataWritten = 0; UInt16 numChannels = (playerInfoP->streamWidth==sndMono)?1:2; UInt16 bitsPerSample = 4; // ADPCM always encodes to 4-bit samples UInt16 blockAlign = 256 * numChannels; UInt32 samplesPerBlock = 0; // Claculate the block size switch(playerInfoP->sampleRate) { case 8000: case 11025: case 16000: break; case 22050: case 32000: blockAlign *= 2; break; case 44100: blockAlign *= 4; break; } samplesPerBlock = ((blockAlign - (4 * numChannels)) * 8) / (bitsPerSample * numChannels) + 1; // RIFF Header adpcm.header.riff.header.type = TYPE_RIFF; adpcm.header.riff.header.size = Swap32(sizeof(WaveIMAADPCM) + playerInfoP->dataSize - sizeof(ChunkHeader)); adpcm.header.waveType = TYPE_WAVE; // fmt sub-chunk adpcm.header.fmt.header.type = TYPE_FMT; adpcm.header.fmt.header.size = Swap32(sizeof(FmtSubChunk) - sizeof(ChunkHeader) + sizeof(ExtraDataBlock)); adpcm.header.fmt.audioFormat = Swap16(WAVE_TYPE_IMAADPCM); adpcm.header.fmt.channels = Swap16(numChannels); adpcm.header.fmt.sampleRate = Swap32(playerInfoP->sampleRate); adpcm.header.fmt.avgByteRate = Swap32(0); adpcm.header.fmt.blockAlign = Swap16(blockAlign); adpcm.header.fmt.bitsPerSample = Swap16(bitsPerSample); adpcm.extra.size = Swap16(sizeof(adpcm.extra.samplesPerBlock)); adpcm.extra.samplesPerBlock = Swap16(samplesPerBlock); // data sub-chunk adpcm.data.type = TYPE_DATA; adpcm.data.size = Swap32(playerInfoP->dataSize); if(playerInfoP->fileType == VFS_File) { err = VFSFileWrite(playerInfoP->file.fileRef, sizeof(adpcm), &adpcm, &dataWritten); } else { dataWritten = FileWrite(playerInfoP->file.fileHandle, &adpcm, 1, sizeof(adpcm), &err); } return err; }