mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 04:07:05 +00:00
182 lines
4.7 KiB
C++
182 lines
4.7 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program 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 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* The code in this file is based on information found at
|
|
* http://www.borg.com/~jglatt/tech/aiff.htm
|
|
*
|
|
* We currently only implement uncompressed AIFF. If we ever need AIFF-C, SoX
|
|
* (http://sox.sourceforge.net) may be a good place to start from.
|
|
*/
|
|
|
|
#include "common/endian.h"
|
|
#include "common/stream.h"
|
|
#include "common/textconsole.h"
|
|
|
|
#include "audio/decoders/aiff.h"
|
|
#include "audio/decoders/raw.h"
|
|
|
|
namespace Audio {
|
|
|
|
uint32 readExtended(Common::SeekableReadStream &stream) {
|
|
// The sample rate is stored as an "80 bit IEEE Standard 754 floating
|
|
// point number (Standard Apple Numeric Environment [SANE] data type
|
|
// Extended).
|
|
|
|
byte buf[10];
|
|
uint32 mantissa;
|
|
uint32 last = 0;
|
|
byte exp;
|
|
|
|
stream.read(buf, 10);
|
|
mantissa = READ_BE_UINT32(buf + 2);
|
|
exp = 30 - buf[1];
|
|
|
|
while (exp--) {
|
|
last = mantissa;
|
|
mantissa >>= 1;
|
|
}
|
|
|
|
if (last & 0x00000001)
|
|
mantissa++;
|
|
|
|
return mantissa;
|
|
}
|
|
|
|
bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags) {
|
|
byte buf[4];
|
|
|
|
stream.read(buf, 4);
|
|
if (memcmp(buf, "FORM", 4) != 0) {
|
|
warning("loadAIFFFromStream: No 'FORM' header");
|
|
return false;
|
|
}
|
|
|
|
stream.readUint32BE();
|
|
|
|
// This could be AIFC, but we don't handle that case.
|
|
|
|
stream.read(buf, 4);
|
|
if (memcmp(buf, "AIFF", 4) != 0) {
|
|
warning("loadAIFFFromStream: No 'AIFF' header");
|
|
return false;
|
|
}
|
|
|
|
// From here on, we only care about the COMM and SSND chunks, which are
|
|
// the only required chunks.
|
|
|
|
bool foundCOMM = false;
|
|
bool foundSSND = false;
|
|
|
|
uint16 numChannels = 0, bitsPerSample = 0;
|
|
uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0;
|
|
|
|
while (!(foundCOMM && foundSSND) && !stream.err() && !stream.eos()) {
|
|
uint32 length, pos;
|
|
|
|
stream.read(buf, 4);
|
|
length = stream.readUint32BE();
|
|
pos = stream.pos();
|
|
|
|
if (memcmp(buf, "COMM", 4) == 0) {
|
|
foundCOMM = true;
|
|
numChannels = stream.readUint16BE();
|
|
numSampleFrames = stream.readUint32BE();
|
|
bitsPerSample = stream.readUint16BE();
|
|
rate = readExtended(stream);
|
|
size = numSampleFrames * numChannels * (bitsPerSample / 8);
|
|
} else if (memcmp(buf, "SSND", 4) == 0) {
|
|
foundSSND = true;
|
|
offset = stream.readUint32BE();
|
|
blockSize = stream.readUint32BE();
|
|
soundOffset = stream.pos();
|
|
}
|
|
|
|
stream.seek(pos + length);
|
|
}
|
|
|
|
if (!foundCOMM) {
|
|
warning("loadAIFFFromStream: Cound not find 'COMM' chunk");
|
|
return false;
|
|
}
|
|
|
|
if (!foundSSND) {
|
|
warning("loadAIFFFromStream: Cound not find 'SSND' chunk");
|
|
return false;
|
|
}
|
|
|
|
// We only implement a subset of the AIFF standard.
|
|
|
|
if (numChannels < 1 || numChannels > 2) {
|
|
warning("loadAIFFFromStream: Only 1 or 2 channels are supported, not %d", numChannels);
|
|
return false;
|
|
}
|
|
|
|
if (bitsPerSample != 8 && bitsPerSample != 16) {
|
|
warning("loadAIFFFromStream: Only 8 or 16 bits per sample are supported, not %d", bitsPerSample);
|
|
return false;
|
|
}
|
|
|
|
if (offset != 0 || blockSize != 0) {
|
|
warning("loadAIFFFromStream: Block-aligned data is not supported");
|
|
return false;
|
|
}
|
|
|
|
// Samples are always signed, and big endian.
|
|
|
|
flags = 0;
|
|
if (bitsPerSample == 16)
|
|
flags |= Audio::FLAG_16BITS;
|
|
if (numChannels == 2)
|
|
flags |= Audio::FLAG_STEREO;
|
|
|
|
stream.seek(soundOffset);
|
|
|
|
// Stream now points at the sample data
|
|
|
|
return true;
|
|
}
|
|
|
|
SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream,
|
|
DisposeAfterUse::Flag disposeAfterUse) {
|
|
int size, rate;
|
|
byte *data, flags;
|
|
|
|
if (!loadAIFFFromStream(*stream, size, rate, flags)) {
|
|
if (disposeAfterUse == DisposeAfterUse::YES)
|
|
delete stream;
|
|
return 0;
|
|
}
|
|
|
|
data = (byte *)malloc(size);
|
|
assert(data);
|
|
stream->read(data, size);
|
|
|
|
if (disposeAfterUse == DisposeAfterUse::YES)
|
|
delete stream;
|
|
|
|
// Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES.
|
|
return makeRawStream(data, size, rate, flags);
|
|
}
|
|
|
|
} // End of namespace Audio
|