2003-08-15 18:00:22 +00:00
|
|
|
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
|
|
|
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This library 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
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
// License along with this library; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
2003-08-24 17:56:03 +00:00
|
|
|
#include "stdafx.h"
|
2003-08-15 19:41:26 +00:00
|
|
|
#include "sound.h"
|
|
|
|
#include "bits.h"
|
|
|
|
#include "debug.h"
|
2003-08-15 18:00:22 +00:00
|
|
|
#include <cstring>
|
|
|
|
#include <SDL_endian.h>
|
|
|
|
|
2003-08-17 16:02:48 +00:00
|
|
|
#define ST_SAMPLE_MAX 0x7fffL
|
|
|
|
#define ST_SAMPLE_MIN (-ST_SAMPLE_MAX - 1L)
|
|
|
|
|
2003-12-12 08:39:07 +00:00
|
|
|
static inline void clampedAdd(int16& a, int b) {
|
2003-08-17 16:02:48 +00:00
|
|
|
register int val = a + b;
|
|
|
|
|
|
|
|
if (val > ST_SAMPLE_MAX)
|
|
|
|
val = ST_SAMPLE_MAX;
|
|
|
|
else if (val < ST_SAMPLE_MIN)
|
|
|
|
val = ST_SAMPLE_MIN;
|
|
|
|
|
|
|
|
a = val;
|
|
|
|
}
|
|
|
|
|
2003-12-12 08:39:07 +00:00
|
|
|
void vimaInit();
|
|
|
|
void decompressVima(const char *src, int16 *dest, int destLen);
|
2003-08-15 18:00:22 +00:00
|
|
|
|
|
|
|
void Sound::init() {
|
2003-12-12 08:39:07 +00:00
|
|
|
vimaInit();
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Sound::Sound(const char *filename, const char *data, int /* len */) :
|
|
|
|
Resource(filename)
|
|
|
|
{
|
|
|
|
const char *extension = filename + std::strlen(filename) - 3;
|
|
|
|
const char *dataStart;
|
|
|
|
int numBlocks, codecsLen;
|
|
|
|
const char *codecsStart;
|
|
|
|
const char *headerPos = data;
|
|
|
|
int dataSize;
|
|
|
|
|
|
|
|
if (strcasecmp(extension, "wav") == 0 || strcasecmp(extension, "imc") == 0) {
|
|
|
|
// Read MCMP info
|
|
|
|
if (std::memcmp(data, "MCMP", 4) != 0)
|
|
|
|
error("Invalid file format in %s\n", filename);
|
|
|
|
|
|
|
|
// The first block is the WAVE or IMUS header
|
2003-12-12 08:39:07 +00:00
|
|
|
numBlocks = READ_BE_UINT16(data + 4);
|
2003-08-15 18:00:22 +00:00
|
|
|
codecsStart = data + 8 + numBlocks * 9;
|
2003-12-12 08:39:07 +00:00
|
|
|
codecsLen = READ_BE_UINT16(codecsStart - 2);
|
2003-08-15 18:00:22 +00:00
|
|
|
headerPos = codecsStart + codecsLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(extension, "wav") == 0) {
|
2003-12-12 08:39:07 +00:00
|
|
|
numChannels_ = READ_LE_UINT16(headerPos + 22);
|
|
|
|
dataStart = headerPos + 28 + READ_LE_UINT32(headerPos + 16);
|
|
|
|
dataSize = READ_LE_UINT32(dataStart - 4);
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
else if (strcasecmp(extension, "imc") == 0 ||
|
|
|
|
strcasecmp(extension, "imu") == 0) {
|
|
|
|
// Ignore MAP info for now...
|
|
|
|
if (memcmp(headerPos + 16, "FRMT", 4) != 0)
|
|
|
|
error("FRMT block not where it was expected\n");
|
2003-12-12 08:39:07 +00:00
|
|
|
numChannels_ = READ_BE_UINT32(headerPos + 40);
|
|
|
|
dataStart = headerPos + 24 + READ_BE_UINT32(headerPos + 12);
|
|
|
|
dataSize = READ_BE_UINT32(dataStart - 4);
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
error("Unrecognized extension for sound file %s\n", filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(extension, "wav") == 0 || strcasecmp(extension, "imc") == 0) {
|
|
|
|
// Uncompress the samples
|
|
|
|
numSamples_ = dataSize / 2;
|
2003-12-12 08:39:07 +00:00
|
|
|
samples_ = new int16[dataSize / 2];
|
|
|
|
int16 *destPos = samples_;
|
2003-08-15 18:00:22 +00:00
|
|
|
const char *srcPos = dataStart;
|
|
|
|
for (int i = 1; i < numBlocks; i++) { // Skip header block
|
2003-12-12 08:39:07 +00:00
|
|
|
if (std::strcmp(codecsStart + 5 * *(uint8 *)(data + 6 + i * 9),
|
2003-08-15 18:00:22 +00:00
|
|
|
"VIMA") != 0)
|
|
|
|
error("Unsupported codec %s\n",
|
2003-12-12 08:39:07 +00:00
|
|
|
codecsStart + 5 * *(uint8 *)(data + 6 + i * 9));
|
|
|
|
decompressVima(srcPos, destPos, READ_BE_UINT32(data + 7 + i * 9));
|
|
|
|
srcPos += READ_BE_UINT32(data + 11 + i * 9);
|
|
|
|
destPos += READ_BE_UINT32(data + 7 + i * 9) / 2;
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
numSamples_ = dataSize / 2;
|
2003-12-12 08:39:07 +00:00
|
|
|
samples_ = new int16[dataSize / 2];
|
2003-08-15 18:00:22 +00:00
|
|
|
std::memcpy(samples_, dataStart, dataSize);
|
|
|
|
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
|
|
|
for (int i = 0; i < numSamples_; i++)
|
|
|
|
samples_[i] = SDL_Swap16(samples_[i]);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
currPos_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sound::reset() {
|
|
|
|
currPos_ = 0;
|
|
|
|
}
|
|
|
|
|
2003-12-12 08:39:07 +00:00
|
|
|
void Sound::mix(int16 *data, int samples) {
|
2003-08-15 18:00:22 +00:00
|
|
|
while (samples > 0 && currPos_ < numSamples_) {
|
2003-08-17 16:02:48 +00:00
|
|
|
clampedAdd(*data, samples_[currPos_]);
|
2003-08-15 18:00:22 +00:00
|
|
|
data++;
|
|
|
|
if (numChannels_ == 1) {
|
|
|
|
*data += samples_[currPos_];
|
|
|
|
samples--;
|
|
|
|
data++;
|
|
|
|
}
|
|
|
|
currPos_++;
|
|
|
|
samples--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Sound::~Sound() {
|
|
|
|
delete[] samples_;
|
|
|
|
}
|