/*
* Copyright 2010-2016 OpenXcom Developers.
*
* This file is part of OpenXcom.
*
* OpenXcom 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.
*
* OpenXcom 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 OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SoundSet.h"
#include "CatFile.h"
#include "Sound.h"
#include "Exception.h"
#include <sstream>
#include <climits>
namespace OpenXcom
{
/**
* Sets up a new empty sound set.
*/
SoundSet::SoundSet() : _sharedSounds(INT_MAX)
{
}
/**
* Deletes the sounds from memory.
*/
SoundSet::~SoundSet()
{
for (std::map<int, Sound*>::iterator i = _sounds.begin(); i != _sounds.end(); ++i)
{
delete i->second;
}
}
/**
* Converts a 8Khz sample to 11Khz.
* @param oldsound Pointer to original sample buffer.
* @param oldsize Original buffer size.
* @param newsound Pointer to converted sample buffer.
* @return Converted buffer size.
*/
int SoundSet::convertSampleRate(Uint8 *oldsound, unsigned int oldsize, Uint8 *newsound) const
{
const Uint32 step16 = (8000 << 16) / 11025;
int newsize = 0;
for (Uint32 offset16 = 0; (offset16 >> 16) < oldsize; offset16 += step16, ++newsound, ++newsize)
{
*newsound = oldsound[offset16 >> 16];
}
return newsize;
}
/**
* Loads the contents of an X-Com CAT file which usually contains
* a set of sound files. The CAT starts with an index of the offset
* and size of every file contained within. Each file consists of a
* filename followed by its contents.
* @param filename Filename of the CAT set.
* @param wav Are the sounds in WAV format?
* @sa http://www.ufopaedia.org/index.php?title=SOUND
*/
void SoundSet::loadCat(const std::string &filename, bool wav)
{
// Load CAT file
CatFile sndFile (filename.c_str());
if (!sndFile)
{
throw Exception(filename + " not found");
}
// Load each sound file
for (int i = 0; i < sndFile.getAmount(); ++i)
{
// Read WAV chunk
unsigned char *sound = (unsigned char*) sndFile.load(i);
unsigned int size = sndFile.getObjectSize(i);
// If there's no WAV header (44 bytes), add it
// Assuming sounds are 6-bit 8000Hz (DOS version)
unsigned char *newsound = 0;
const int headerSize = 44;
if (!wav)
{
if (size > 5) size -= 5; // skip 5 garbage name bytes at beginning
if (size) size--; // omit trailing null byte
if (size != 0)
{
char header[] = {'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ',
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x11, 0x2b, 0x00, 0x00, 0x11, 0x2b, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00,
'd', 'a', 't', 'a', 0x00, 0x00, 0x00, 0x00};
// scale to 8 bits
for (unsigned int n = 0; n < size; ++n) sound[5 + n] *= 4;
// copy and do the conversion...
newsound = new unsigned char[headerSize + size*2];
memcpy(newsound, header, headerSize);
memcpy(newsound + headerSize, sound + 5, size);
int newsize = convertSampleRate(sound + 5, size, newsound + headerSize);
size = newsize + headerSize;
// Rewrite the number of samples in the WAV file
int headersize = newsize + 36;
int soundsize = newsize;
memcpy(newsound + 4, &headersize, sizeof(headersize));
memcpy(newsound + 40, &soundsize, sizeof(soundsize));
}
}
// so it's WAV, but in 8 khz, we have to convert it to 11 khz sound
else if (0x40 == sound[0x18] && 0x1F == sound[0x19] && 0x00 == sound[0x1A] && 0x00 == sound[0x1B])
{
unsigned char *sound2 = new unsigned char[size*2];
// rewrite the samplerate in the header to 11 khz
sound[0x18]=0x11; sound[0x19]=0x2B; sound[0x1C]=0x11; sound[0x1D]=0x2B;
// copy and do the conversion...
memcpy(sound2, sound, size);
int newsize = convertSampleRate(sound + headerSize, size - headerSize, sound2 + headerSize);
size = newsize + headerSize;
// Rewrite the number of samples in the WAV file
memcpy(sound2 + 0x28, &newsize, sizeof(newsize));
// Ok, now replace the original with the converted:
delete[] sound;
sound = sound2;
}
Sound *s = new Sound();
try
{
if (size == 0)
{
throw Exception("Invalid sound file");
}
if (wav)
s->load(sound, size);
else
s->load(newsound, size);
}
catch (const Exception &)
{
// Ignore junk in the file
}
_sounds[i] = s;
delete[] sound;
if (!wav)
{
delete[] newsound;
}
}
}
/**
* Returns a particular wave from the sound set.
* @param i Sound number in the set.
* @return Pointer to the respective sound.
*/
Sound *SoundSet::getSound(unsigned int i)
{
if (_sounds.find(i) != _sounds.end())
{
return _sounds[i];
}
return 0;
}
/**
* Creates and returns a particular wave in the sound set.
* @param i Sound number in the set.
* @return Pointer to the respective sound.
*/
Sound *SoundSet::addSound(unsigned int i)
{
_sounds[i] = new Sound();
return _sounds[i];
}
/**
* Set number of shared sound indexs that are accessible for all mods.
*/
void SoundSet::setMaxSharedSounds(int i)
{
if (i >= 0)
{
_sharedSounds = i;
}
else
{
_sharedSounds = 0;
}
}
/**
* Gets number of shared sound indexs that are accessible for all mods.
*/
int SoundSet::getMaxSharedSounds() const
{
return _sharedSounds;
}
/**
* Returns the total amount of sounds currently
* stored in the set.
* @return Number of sounds.
*/
size_t SoundSet::getTotalSounds() const
{
return _sounds.size();
}
/**
* Loads individual contents of a TFTD CAT file by index.
* a set of sound files. The CAT starts with an index of the offset
* and size of every file contained within. Each file consists of a
* filename followed by its contents.
* @param filename Filename of the CAT set.
* @param index which index in the cat file do we load?
* @sa http://www.ufopaedia.org/index.php?title=SOUND
*/
void SoundSet::loadCatbyIndex(const std::string &filename, int index)
{
// Load CAT file
CatFile sndFile (filename.c_str());
if (!sndFile)
{
throw Exception(filename + " not found");
}
if (index >= sndFile.getAmount())
{
std::ostringstream err;
err << filename << " does not contain " << index << " sound files.";
throw Exception(err.str());
}
// Read WAV chunk
unsigned char *sound = (unsigned char*) sndFile.load(index);
unsigned int size = sndFile.getObjectSize(index);
// there's no WAV header (44 bytes), add it
// sounds are 8-bit 11025Hz, signed
unsigned char *newsound = 0;
if (size != 0)
{
char header[] = {'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ',
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x11, 0x2b, 0x00, 0x00, 0x11, 0x2b, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00,
'd', 'a', 't', 'a', 0x00, 0x00, 0x00, 0x00};
if (size > 5) size -= 5; // skip 5 garbage name bytes at beginning
if (size) size--; // omit trailing null byte
int headersize = size + 36;
int soundsize = size;
memcpy(header + 4, &headersize, sizeof(headersize));
memcpy(header + 40, &soundsize, sizeof(soundsize));
newsound = new unsigned char[44 + size];
memcpy(newsound, header, 44);
// TFTD sounds are signed, so we need to convert them.
for (unsigned int n = 5; n < size + 5; ++n)
{
int value = (int)sound[n] + 128;
sound[n] = (uint8_t)value;
}
if (size) memcpy(newsound + 44, sound+5, size);
size = size + 44;
}
Sound *s = new Sound();
try
{
if (size == 0)
{
throw Exception("Invalid sound file");
}
s->load(newsound, size);
}
catch (const Exception &)
{
// Ignore junk in the file
}
_sounds[getTotalSounds()] = s;
delete[] sound;
delete[] newsound;
}
}
↑ V547 Expression 'size' is always true.
↑ V565 An empty exception handler. Silent suppression of exceptions can hide the presence of bugs in source code during testing.
↑ V565 An empty exception handler. Silent suppression of exceptions can hide the presence of bugs in source code during testing.