/*
===========================================================================
ARX FATALIS GPL Source Code
Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
.
In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "Athena_Stream_WAV.h"
#include
#include
#include "Athena_Codec_RAW.h"
#include "Athena_Codec_ADPCM.h"
#include "Athena_FileIO.h"
namespace ATHENA
{
#define AS_FORMAT_PCM(x) ((WAVEFORMATEX *)x)
#define AS_FORMAT_ADPCM(x) ((ADPCMWAVEFORMAT *)x)
///////////////////////////////////////////////////////////////////////////////
// //
// Class ChunkFile //
// //
///////////////////////////////////////////////////////////////////////////////
class ChunkFile
{
public:
//Constructor and destructor
ChunkFile(FILE * file);
~ChunkFile();
//I/O
aalSBool Read(aalVoid *, const aalULong &);
aalSBool Skip(const aalULong &);
aalSBool Find(const char *);
aalSBool Check(const char *);
aalULong Size()
{
return offset;
};
aalSBool Restart();
private:
//Data
FILE * file;
aalULong offset;
};
///////////////////////////////////////////////////////////////////////////////
// //
// Constructor and destructor //
// //
///////////////////////////////////////////////////////////////////////////////
StreamWAV::StreamWAV() :
stream(NULL),
codec(NULL),
status(NULL),
format(NULL),
size(0), outsize(0),
offset(0),
cursor(0)
{
}
StreamWAV::~StreamWAV()
{
if (codec) delete codec;
free(status);
free(format);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Setup //
// //
///////////////////////////////////////////////////////////////////////////////
aalError StreamWAV::SetStream(FILE * _stream)
{
if (!_stream) return AAL_ERROR_FILEIO;
stream = _stream;
format = malloc(sizeof(WAVEFORMATEX));
if (!format) return AAL_ERROR_MEMORY;
ChunkFile wave(stream);
// Check for 'RIFF' chunk id and skip file size
if (wave.Check("RIFF") ||
wave.Skip(4) ||
wave.Check("WAVE") ||
wave.Find("fmt ") ||
wave.Read(&AS_FORMAT_PCM(format)->wFormatTag, 2) ||
wave.Read(&AS_FORMAT_PCM(format)->nChannels, 2) ||
wave.Read(&AS_FORMAT_PCM(format)->nSamplesPerSec, 4) ||
wave.Read(&AS_FORMAT_PCM(format)->nAvgBytesPerSec, 4) ||
wave.Read(&AS_FORMAT_PCM(format)->nBlockAlign, 2) ||
wave.Read(&AS_FORMAT_PCM(format)->wBitsPerSample, 2))
return AAL_ERROR_FORMAT;
// Get codec specific infos from header for non-PCM format
if (AS_FORMAT_PCM(format)->wFormatTag != WAVE_FORMAT_PCM)
{
aalVoid * ptr;
//Load extra bytes from header
if (wave.Read(&AS_FORMAT_PCM(format)->cbSize, 2)) return AAL_ERROR_FORMAT;
ptr = realloc(format, sizeof(WAVEFORMATEX) + AS_FORMAT_PCM(format)->cbSize);
if (!ptr) return AAL_ERROR_MEMORY;
format = ptr;
wave.Read((char *)format + sizeof(WAVEFORMATEX), AS_FORMAT_PCM(format)->cbSize);
// Get sample count from the 'fact' chunk
wave.Find("fact");
wave.Read(&outsize, 4);
}
// Create codec
switch (AS_FORMAT_PCM(format)->wFormatTag)
{
case WAVE_FORMAT_PCM :
codec = new CodecRAW;
break;
case WAVE_FORMAT_ADPCM :
outsize <<= 1;
codec = new CodecADPCM;
break;
default :
return AAL_ERROR_FORMAT;
}
// Check for 'data' chunk id, get data size and offset
wave.Restart();
wave.Skip(12);
if (wave.Find("data")) return AAL_ERROR_FORMAT;
size = wave.Size();
if (AS_FORMAT_PCM(format)->wFormatTag == WAVE_FORMAT_PCM) outsize = size;
else outsize *= AS_FORMAT_PCM(format)->nChannels;
offset = FileTell(stream);
aalError error;
error = codec->SetStream(stream);
if (error) return error;
error = codec->SetHeader(format);
if (error) return error;
return AAL_OK;
}
aalError StreamWAV::SetFormat(const aalFormat &)
{
return AAL_ERROR;
}
aalError StreamWAV::SetLength(const aalULong &)
{
return AAL_ERROR;
}
aalError StreamWAV::SetPosition(const aalULong & position)
{
if (position >= outsize) return AAL_ERROR_FILEIO;
cursor = position;
// Reset stream position at the begining of data chunk
if (FileSeek(stream, offset, SEEK_SET)) return AAL_ERROR_FILEIO;
return codec->SetPosition(cursor);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Status //
// //
///////////////////////////////////////////////////////////////////////////////
aalError StreamWAV::GetStream(FILE *&_stream)
{
_stream = stream;
return AAL_OK;
}
aalError StreamWAV::GetFormat(aalFormat & _format)
{
_format.frequency = AS_FORMAT_PCM(format)->nSamplesPerSec;
_format.channels = AS_FORMAT_PCM(format)->nChannels;
switch (AS_FORMAT_PCM(format)->wFormatTag)
{
case WAVE_FORMAT_PCM :
_format.quality = AS_FORMAT_PCM(format)->wBitsPerSample;
break;
case WAVE_FORMAT_ADPCM :
_format.quality = 16;
break;
}
return AAL_OK;
}
aalError StreamWAV::GetLength(aalULong & _length)
{
_length = outsize;
return AAL_OK;
}
aalError StreamWAV::GetPosition(aalULong & _position)
{
return codec->GetPosition(_position);
}
///////////////////////////////////////////////////////////////////////////////
// //
// I/O //
// //
///////////////////////////////////////////////////////////////////////////////
aalError StreamWAV::Read(aalVoid * buffer, const aalULong & to_read, aalULong & _read)
{
_read = 0;
if (cursor >= outsize) return AAL_OK;
aalULong count(cursor + to_read > outsize ? outsize - cursor : to_read);
aalError error;
error = codec->Read(buffer, count, _read);
if (error) return error;
cursor += _read;
return AAL_OK;
}
aalError StreamWAV::Write(aalVoid *, const aalULong &, aalULong & write)
{
write = 0;
return AAL_ERROR;
}
///////////////////////////////////////////////////////////////////////////////
// //
// Constructor and destructor //
// //
///////////////////////////////////////////////////////////////////////////////
// Constructor //
ChunkFile::ChunkFile(FILE * ptr) : file(ptr), offset(0)
{
}
ChunkFile::~ChunkFile()
{
}
///////////////////////////////////////////////////////////////////////////////
// //
// I/O //
// //
///////////////////////////////////////////////////////////////////////////////
// Read! //
aalSBool ChunkFile::Read(aalVoid * buffer, const aalULong & size)
{
if (FileRead(buffer, 1, size, file) != size) return AAL_SFALSE;
if (offset) offset -= size;
return AAL_STRUE;
}
// Skip! //
aalSBool ChunkFile::Skip(const aalULong & size)
{
if (FileSeek(file, size, SEEK_CUR)) return AAL_SFALSE;
if (offset) offset -= size;
return AAL_STRUE;
}
// Return AAL_OK if chunk is found //
aalSBool ChunkFile::Find(const char * id)
{
aalUByte cc[4];
FileSeek(file, offset, SEEK_CUR);
while (FileRead(cc, 4, 1, file))
{
if (!FileRead(&offset, 4, 1, file)) return AAL_SFALSE;
if (!memcmp(cc, id, 4)) return AAL_STRUE;
if (FileSeek(file, offset, SEEK_CUR)) return AAL_SFALSE;
}
return AAL_SFALSE;
}
// Return AAL_OK if next four bytes = chunk id; //
aalSBool ChunkFile::Check(const char * id)
{
aalUByte cc[4];
if (!FileRead(cc, 4, 1, file)) return AAL_SFALSE;
if (memcmp(cc, id, 4)) return AAL_SFALSE;
if (offset) offset -= 4;
return AAL_STRUE;
}
aalSBool ChunkFile::Restart()
{
offset = 0;
if (FileSeek(file, 0, SEEK_SET)) return AAL_SFALSE;
return AAL_STRUE;
}
}//ATHENA::