Serious-Engine/Sources/Engine/Sound/Wave.cpp
2016-03-11 15:57:17 +02:00

333 lines
8.9 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include "stdh.h"
#include <Engine/Base/Stream.h>
#include <Engine/Base/ErrorReporting.h>
#include <Engine/Sound/Wave.h>
/* ====================================================
*
* CONVERSION FUNCTIONS
*
*/
// check wave format
void PCMWaveInput::CheckWaveFormat_t(WAVEFORMATEX wfeCheck, char *pcErrorString)
{
// check format tag
if (wfeCheck.wFormatTag != 1) {
ThrowF_t(TRANS("%s: Invalid format tag, not a PCM Wave file!"), pcErrorString);
}
// check bits per sample
if (wfeCheck.wBitsPerSample != 8 &&
wfeCheck.wBitsPerSample != 16) {
ThrowF_t(TRANS("%s: Unknown Bits Per Sample value!"), pcErrorString);
}
// check number of channels
if (wfeCheck.nChannels != 1 &&
wfeCheck.nChannels != 2) {
ThrowF_t(TRANS("%s: Invalid number of channels!"), pcErrorString);
}
//ASSERT( wfeCheck.wBitsPerSample==16);
}
// Get next data
inline ULONG PCMWaveInput::GetData_t(CTStream *pCstrInput)
{
ASSERT(pwi_bInfoLoaded);
// read data according to bits per sample value
if (pwi_wfeWave.wBitsPerSample==8) {
// read UBYTE
UBYTE ubData;
*pCstrInput >> ubData;
return ((ULONG)ubData) <<16; // (shift) prepare data for shrink/expand operation
} else {
// read UWORD
SWORD swData;
*pCstrInput >> swData;
return ((ULONG)(swData+0x8000)) <<8; // (shift) prepare data for shrink/expand operation
}
}
// Store data
inline void PCMWaveInput::StoreData(ULONG ulData)
{
ASSERT( pwi_wfeDesired.wBitsPerSample==16);
*pwi_pswMemory++ = ((SWORD)(ulData>>8) -0x8000); // (shift) restore data format
}
/*
* Copy data
*/
void PCMWaveInput::CopyData_t(CTStream *pCstrInput)
{
// for all input data (mono and stereo)
ULONG ulDataCount = GetDataLength() * pwi_wfeWave.nChannels;
while (ulDataCount > 0) {
StoreData(GetData_t(pCstrInput)); // read and store data from input (hidden BitsPerSample conversion!)
ulDataCount--; // to next data
}
}
/*
* Shrink data
*/
// Shrink data
void PCMWaveInput::ShrinkData_t(CTStream *pCstrInput)
{
ASSERT(pwi_dRatio>1.0);
// *** MONO ***
if (pwi_wfeWave.nChannels == 1) {
DOUBLE dInterData, dTempData, dRatio;
ULONG ulDataCount;
// data intermediate value
dInterData = 0.0;
// for all input data (mono)
ulDataCount = GetDataLength();
dRatio = pwi_dRatio;
while (ulDataCount > 0) {
// read part of data (<100%)
if (dRatio<1.0) {
dTempData = GetData_t(pCstrInput);
dInterData += dTempData*dRatio;
StoreData(ULONG(dInterData/pwi_dRatio));
// new intermediate value
dRatio = 1 - dRatio;
dInterData = dTempData*dRatio;
dRatio = pwi_dRatio - dRatio;
// read complete data (100%)
} else {
dInterData += GetData_t(pCstrInput);
dRatio -= 1.0;
}
ulDataCount--; // to next data
}
StoreData(ULONG(dInterData/(pwi_dRatio-dRatio)));
// *** STEREO ***
} else if (pwi_wfeWave.nChannels == 2) {
DOUBLE dLInterData, dRInterData, dLTempData, dRTempData, dRatio;
ULONG ulDataCount;
// data intermediate value
dLInterData = 0.0;
dRInterData = 0.0;
// for all input data (mono)
ulDataCount = GetDataLength();
dRatio = pwi_dRatio;
while (ulDataCount > 0) {
// read part of data (<100%)
if (dRatio<1.0) {
dLTempData = GetData_t(pCstrInput);
dRTempData = GetData_t(pCstrInput);
dLInterData += dLTempData*dRatio;
dRInterData += dRTempData*dRatio;
StoreData(ULONG(dLInterData/pwi_dRatio));
StoreData(ULONG(dRInterData/pwi_dRatio));
// new intermediate value
dRatio = 1 - dRatio;
dLInterData = dLTempData*dRatio;
dRInterData = dRTempData*dRatio;
dRatio = pwi_dRatio - dRatio;
// read complete data (100%)
} else {
dLInterData += GetData_t(pCstrInput);
dRInterData += GetData_t(pCstrInput);
dRatio -= 1.0;
}
ulDataCount--; // to next data
}
StoreData(ULONG(dLInterData/(pwi_dRatio-dRatio)));
StoreData(ULONG(dRInterData/(pwi_dRatio-dRatio)));
}
}
/* ====================================================
*
* WAVE FUNCTIONS
*
*/
/*
* Load Wave info
*/
WAVEFORMATEX PCMWaveInput::LoadInfo_t(CTStream *pCstrInput)
{
// if already loaded -> exception
if (pwi_bInfoLoaded) {
throw (TRANS("PCM Wave Input: Info already loaded."));
}
/* Read Riff */
pCstrInput->ExpectID_t(CChunkID("RIFF")); // ID "RIFF"
(*pCstrInput) >> pwi_ulRiffLength; // Ucitaj duljinu file-a
/* Read Wave */
pCstrInput->ExpectID_t(CChunkID("WAVE")); // ID "WAVE"
pCstrInput->ExpectID_t(CChunkID("fmt ")); // ID "fmt "
// read Format Chunk length
SLONG slFmtLength;
(*pCstrInput) >> slFmtLength;
// read WAVE format
(*pCstrInput) >> pwi_wfeWave.wFormatTag;
(*pCstrInput) >> pwi_wfeWave.nChannels;
(*pCstrInput) >> pwi_wfeWave.nSamplesPerSec;
(*pCstrInput) >> pwi_wfeWave.nAvgBytesPerSec;
(*pCstrInput) >> pwi_wfeWave.nBlockAlign;
(*pCstrInput) >> pwi_wfeWave.wBitsPerSample;
pwi_wfeWave.cbSize = 0; // Only for PCM Wave !!!
// WARNING !!! - Only for PCM Wave - Skip extra information if exists
if( slFmtLength > 16) {
//WarningMessage("PCM Wave Input: Wave format Extra information skipped!");
pCstrInput->Seek_t(slFmtLength - 16, CTStream::SD_CUR);
}
// WARNING - If exist Fact chunk skip it (purpose unknown)
if( pCstrInput->GetID_t() == CChunkID("fact")) {
//WarningMessage("PCM Wave Input: Fact Chunk skipped!");
SLONG slSkipLength;
(*pCstrInput) >> slSkipLength;
pCstrInput->Seek_t(slSkipLength, CTStream::SD_CUR);
// seek back on Chunk ID
} else {
pCstrInput->Seek_t(-CID_LENGTH, CTStream::SD_CUR);
}
/* Read Data */
pCstrInput->ExpectID_t(CChunkID("data")); // ID "data"
// read Data length (in bytes)
(*pCstrInput) >> pwi_ulDataLength;
/* Check PCM format */
CheckWaveFormat_t(pwi_wfeWave, "PCM Wave Input (input)");
// mark Info loaded
pwi_bInfoLoaded = TRUE;
// ASSERT( pwi_wfeWave.wBitsPerSample==16);
// return Wave Format
return pwi_wfeWave;
}
/*
* Load and convert Wave data
*/
void PCMWaveInput::LoadData_t(CTStream *pCstrInput, SWORD *pswMemory, WAVEFORMATEX &SwfeDesired)
{
// if info not loaded -> exception
if (!pwi_bInfoLoaded) {
throw (TRANS("PCM Wave Input: Info not loaded."));
}
// if already loaded -> exception
if (pwi_bDataLoaded) {
throw (TRANS("PCM Wave Input: Data already loaded"));
}
// set memory pointer
pwi_pswMemory = pswMemory;
// store and check desired sound format
CheckWaveFormat_t(SwfeDesired, "PCM Wave Input (desired)");
pwi_wfeDesired = SwfeDesired;
// calculate expand/shrink ratio (number of channels remain the same)
pwi_dRatio = (DOUBLE)pwi_wfeDesired.nSamplesPerSec / (DOUBLE)pwi_wfeWave.nSamplesPerSec;
// determine converion type from input and desired sound frequency, and convert sound
if (pwi_dRatio < 1) {
pwi_dRatio = 1/pwi_dRatio;
ShrinkData_t(pCstrInput);
} else if (pwi_dRatio > 1) {
ASSERTALWAYS("Can't expand wave data");
memset(pwi_pswMemory, 0, DetermineBufferSize(pwi_wfeDesired));
// copy data
} else {
ASSERT(pwi_dRatio==1.0f);
CopyData_t(pCstrInput);
}
// data is loaded (and maybe converted from 16-bits)
if( pwi_wfeWave.wBitsPerSample==8) SwfeDesired.nBlockAlign *= 2;
pwi_bDataLoaded = TRUE;
}
/*
* Length in bytes
*/
ULONG PCMWaveInput::GetByteLength(void)
{
ASSERT(pwi_bInfoLoaded);
return pwi_ulDataLength;
}
/*
* Length in blocks
*/
ULONG PCMWaveInput::GetDataLength(void)
{
ASSERT(pwi_bInfoLoaded);
return GetByteLength() / (pwi_wfeWave.nChannels * pwi_wfeWave.wBitsPerSample/8);
}
ULONG PCMWaveInput::GetDataLength(WAVEFORMATEX SwfeDesired)
{
ASSERT(pwi_bInfoLoaded);
// return buffer size
return DetermineBufferSize(SwfeDesired) / (SwfeDesired.nChannels * SwfeDesired.wBitsPerSample/8);
}
/*
* Length in seconds
*/
DOUBLE PCMWaveInput::GetSecondsLength(void)
{
ASSERT(pwi_bInfoLoaded);
return (DOUBLE)GetDataLength() / (DOUBLE)pwi_wfeWave.nSamplesPerSec;
}
/*
* Buffer length in bytes
*/
ULONG PCMWaveInput::DetermineBufferSize(void)
{
return DetermineBufferSize(pwi_wfeWave);
}
ULONG PCMWaveInput::DetermineBufferSize( WAVEFORMATEX SwfeDesired)
{
ASSERT(pwi_bInfoLoaded);
DOUBLE dRatio;
// calculate ratio between formats
dRatio = (DOUBLE)SwfeDesired.nSamplesPerSec / (DOUBLE)pwi_wfeWave.nSamplesPerSec
* (DOUBLE)SwfeDesired.wBitsPerSample / (DOUBLE)pwi_wfeWave.wBitsPerSample;
// return buffer size (must calculate with data length to avoid miss align data, for example:
// 16 bit sound with 2 channels must be aligned to 4 bytes boundary and a multiply with
// random ratio can as result give any possible number
DOUBLE ret = ceil(dRatio*GetDataLength()) * (pwi_wfeWave.nChannels*(pwi_wfeWave.wBitsPerSample/8));
return (ULONG)ret;
}