palm-os-sdk/PalmOne/Samples/ExtendedSound/Src/Wave.c

370 lines
9.6 KiB
C

/***********************************************************************
*
* 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;
}