synced with scummvm svn rev 47951

This commit is contained in:
Pawel Kolodziejski 2011-04-10 15:33:20 +02:00
parent 55415ee174
commit 41be88f8b7
40 changed files with 3940 additions and 1089 deletions

View file

@ -45,6 +45,14 @@ Common::EventManager *BaseBackend::getEventManager() {
return s_eventManager;
}
/*
FIXME: Maybe we should push the default config file loading/saving code below
out to all the backends?
*/
#if defined(UNIX)
#if defined(SAMSUNGTV)
#define DEFAULT_CONFIG_FILE "/dtv/usb/sda1/.scummvmrc"

View file

@ -1,49 +0,0 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*/
#if defined(__PSP__)
#include "backends/fs/psp/psp-fs-factory.h"
#include "backends/fs/psp/psp-fs.cpp"
DECLARE_SINGLETON(PSPFilesystemFactory);
AbstractFSNode *PSPFilesystemFactory::makeRootFileNode() const {
return new PSPFilesystemNode();
}
AbstractFSNode *PSPFilesystemFactory::makeCurrentDirectoryFileNode() const {
char buf[MAXPATHLEN];
char * ret = 0;
PowerMan.beginCriticalSection();
ret = getcwd(buf, MAXPATHLEN);
PowerMan.endCriticalSection();
return (ret ? new PSPFilesystemNode(buf) : NULL);
}
AbstractFSNode *PSPFilesystemFactory::makeFileNodePath(const Common::String &path) const {
return new PSPFilesystemNode(path, true);
}
#endif

View file

@ -1,226 +0,0 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*/
#ifdef __PSP__
#include "engines/engine.h"
#include "backends/fs/abstract-fs.h"
#include "backends/fs/psp/psp-stream.h"
#include <sys/stat.h>
#include <unistd.h>
#include <pspkernel.h>
#define ROOT_PATH "ms0:/"
#include "backends/platform/psp/trace.h"
/**
* Implementation of the ScummVM file system API based on PSPSDK API.
*
* Parts of this class are documented in the base interface class, AbstractFSNode.
*/
class PSPFilesystemNode : public AbstractFSNode {
protected:
Common::String _displayName;
Common::String _path;
bool _isDirectory;
bool _isValid;
public:
/**
* Creates a PSPFilesystemNode with the root node as path.
*/
PSPFilesystemNode();
/**
* Creates a PSPFilesystemNode for a given path.
*
* @param path Common::String with the path the new node should point to.
* @param verify true if the isValid and isDirectory flags should be verified during the construction.
*/
PSPFilesystemNode(const Common::String &p, bool verify = true);
virtual bool exists() const;
virtual Common::String getDisplayName() const { return _displayName; }
virtual Common::String getName() const { return _displayName; }
virtual Common::String getPath() const { return _path; }
virtual bool isDirectory() const { return _isDirectory; }
virtual bool isReadable() const;
virtual bool isWritable() const;
virtual AbstractFSNode *getChild(const Common::String &n) const;
virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const;
virtual AbstractFSNode *getParent() const;
virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream();
};
PSPFilesystemNode::PSPFilesystemNode() {
_isDirectory = true;
_displayName = "Root";
_isValid = true;
_path = ROOT_PATH;
}
PSPFilesystemNode::PSPFilesystemNode(const Common::String &p, bool verify) {
assert(p.size() > 0);
_path = p;
_displayName = lastPathComponent(_path, '/');
_isValid = true;
_isDirectory = true;
if (verify) {
struct stat st;
if (PowerMan.beginCriticalSection()==PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPFilesystemNode::PSPFilesystemNode\n");
_isValid = (0 == stat(_path.c_str(), &st));
PowerMan.endCriticalSection();
_isDirectory = S_ISDIR(st.st_mode);
}
}
bool PSPFilesystemNode::exists() const {
int ret = 0;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPFilesystemNode::exists()\n"); // Make sure to block in case of suspend
ret = access(_path.c_str(), F_OK);
PowerMan.endCriticalSection();
return ret == 0;
}
bool PSPFilesystemNode::isReadable() const {
int ret = 0;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPFilesystemNode::isReadable()\n"); // Make sure to block in case of suspend
ret = access(_path.c_str(), R_OK);
PowerMan.endCriticalSection();
return ret == 0;
}
bool PSPFilesystemNode::isWritable() const {
int ret = 0;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPFilesystemNode::isWritable()\n"); // Make sure to block in case of suspend
ret = access(_path.c_str(), W_OK);
PowerMan.endCriticalSection();
return ret == 0;
}
AbstractFSNode *PSPFilesystemNode::getChild(const Common::String &n) const {
// FIXME: Pretty lame implementation! We do no error checking to speak
// of, do not check if this is a special node, etc.
assert(_isDirectory);
Common::String newPath(_path);
if (_path.lastChar() != '/')
newPath += '/';
newPath += n;
return new PSPFilesystemNode(newPath, true);
}
bool PSPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
assert(_isDirectory);
//TODO: honor the hidden flag
bool ret = true;
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPFilesystemNode::getChildren\n"); // Make sure to block in case of suspend
int dfd = sceIoDopen(_path.c_str());
if (dfd > 0) {
SceIoDirent dir;
memset(&dir, 0, sizeof(dir));
while (sceIoDread(dfd, &dir) > 0) {
// Skip 'invisible files
if (dir.d_name[0] == '.')
continue;
PSPFilesystemNode entry;
entry._isValid = true;
entry._displayName = dir.d_name;
Common::String newPath(_path);
if (newPath.lastChar() != '/')
newPath += '/';
newPath += dir.d_name;
entry._path = newPath;
entry._isDirectory = dir.d_stat.st_attr & FIO_SO_IFDIR;
// Honor the chosen mode
if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
(mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory))
continue;
myList.push_back(new PSPFilesystemNode(entry));
}
sceIoDclose(dfd);
ret = true;
} else { // dfd <= 0
ret = false;
}
PowerMan.endCriticalSection();
return ret;
}
AbstractFSNode *PSPFilesystemNode::getParent() const {
if (_path == ROOT_PATH)
return 0;
const char *start = _path.c_str();
const char *end = lastPathComponent(_path, '/');
return new PSPFilesystemNode(Common::String(start, end - start), false);
}
Common::SeekableReadStream *PSPFilesystemNode::createReadStream() {
return PSPIoStream::makeFromPath(getPath(), false);
}
Common::WriteStream *PSPFilesystemNode::createWriteStream() {
return PSPIoStream::makeFromPath(getPath(), true);
}
#endif //#ifdef __PSP__

View file

@ -1,303 +0,0 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#ifdef __PSP__
#include <SDL/SDL_thread.h>
#include <SDL/SDL_mutex.h>
#include "backends/platform/psp/trace.h"
#include "backends/platform/psp/powerman.h"
#include "backends/fs/psp/psp-stream.h"
#include <errno.h>
PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode)
: StdioStream((void *)1), _path(path), _writeMode(writeMode) {
assert(!path.empty());
_handle = (void *)0; // Need to do this since base class asserts not 0.
_ferror = false;
_feof = false;
_pos = 0;
#ifdef __PSP_DEBUG_SUSPEND__
_errorSuspend = 0;
_errorSource = 0;
_errorPos = 0;
_errorHandle = 0;
_suspendCount = 0;
#endif
}
PSPIoStream::~PSPIoStream() {
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPIoStream::~PSPIoStream()\n");
PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended
// Must do this before fclose() or resume() will reopen.
fclose((FILE *)_handle); // We don't need a critical section(?). Worst case, the handle gets closed on its own
PowerMan.endCriticalSection();
}
// Function to open the file pointed to by the path.
//
//
void *PSPIoStream::open() {
if (PowerMan.beginCriticalSection() == PowerManager::Blocked) {
// No need to open. Just return the _handle resume() already opened.
PSPDebugSuspend("Suspended in PSPIoStream::open\n");
}
_handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open
PowerMan.registerSuspend(this); // Register with the powermanager to be suspended
PowerMan.endCriticalSection();
return _handle;
}
bool PSPIoStream::err() const {
if (_ferror)
PSPDebugSuspend("In PSPIoStream::err - mem_ferror=%d, source=%d, suspend error=%d, pos=%d, _errorPos=%d, _errorHandle=%p, suspendCount=%d _handle\n",
_ferror, _errorSource, _errorSuspend, _pos, _errorPos, _errorHandle, _suspendCount);
return _ferror;
}
void PSPIoStream::clearErr() {
_ferror = false; // Remove regular error bit
}
bool PSPIoStream::eos() const {
return _feof;
}
int32 PSPIoStream::pos() const {
return _pos;
}
int32 PSPIoStream::size() const {
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPIoStream::size()\n");
fseek((FILE *)_handle, 0, SEEK_END);
int32 length = ftell((FILE *)_handle);
fseek((FILE *)_handle, _pos, SEEK_SET);
if (_pos < 0 || length < 0) { // Check for errors
PSPDebugSuspend("In PSPIoStream::size(). encountered an error!\n");
_ferror = true;
length = -1; // If our oldPos is bad, we want length to be bad too to signal
clearerr((FILE *)_handle);
#ifdef __PSP_DEBUG_SUSPEND__
_errorSource = 2;
#endif
}
PowerMan.endCriticalSection();
return length;
}
bool PSPIoStream::seek(int32 offs, int whence) {
// Check if we can access the file
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPIoStream::seek()\n");
int ret = fseek((FILE *)_handle, offs, whence);
if (ret != 0) {
_ferror = true;
PSPDebugSuspend("In PSPIoStream::seek(). encountered an error!\n");
clearerr((FILE *)_handle);
_feof = feof((FILE *)_handle);
#ifdef __PSP_DEBUG_SUSPEND__
_errorSource = 3;
#endif
}
else { // everything ok
_feof = false; // Reset eof flag since we know it was ok
}
_pos = ftell((FILE *)_handle); // update pos
PowerMan.endCriticalSection();
return ret == 0;
}
uint32 PSPIoStream::read(void *ptr, uint32 len) {
// Check if we can access the file
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPIoStream::read()\n");
size_t ret = fread((byte *)ptr, 1, len, (FILE *)_handle);
_pos += ret; // Update pos
if (ret != len) { // Check for eof
_feof = feof((FILE *)_handle);
if (!_feof) { // It wasn't an eof. Must be an error
_ferror = true;
clearerr((FILE *)_handle);
_pos = ftell((FILE *)_handle); // Update our position
PSPDebugSuspend("In PSPIoStream::read(). encountered an error!\n");
#ifdef __PSP_DEBUG_SUSPEND__
_errorSource = 4;
#endif
}
}
PowerMan.endCriticalSection();
return ret;
}
uint32 PSPIoStream::write(const void *ptr, uint32 len) {
// Check if we can access the file
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPIoStream::read()\n");
size_t ret = fwrite(ptr, 1, len, (FILE *)_handle);
_pos += ret;
if (ret != len) { // Set error
_ferror = true;
clearerr((FILE *)_handle);
_pos = ftell((FILE *)_handle); // Update pos
PSPDebugTrace("In PSPIoStream::write(). encountered an error!\n");
#ifdef __PSP_DEBUG_SUSPEND__
_errorSource = 5;
#endif
}
PowerMan.endCriticalSection();
return ret;
}
bool PSPIoStream::flush() {
// Enter critical section
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSPDebugSuspend("Suspended in PSPIoStream::read()\n");
int ret = fflush((FILE *)_handle);
if (ret != 0) {
_ferror = true;
clearerr((FILE *)_handle);
PSPDebugSuspend("In PSPIoStream::flush(). encountered an error!\n");
#ifdef __PSP_DEBUG_SUSPEND__
_errorSource = 6;
#endif
}
PowerMan.endCriticalSection();
return ret == 0;
}
// For the PSP, since we're building in suspend support, we moved opening
// the actual file to an open function since we need an actual PSPIoStream object to suspend.
//
PSPIoStream *PSPIoStream::makeFromPath(const Common::String &path, bool writeMode) {
PSPIoStream *stream = new PSPIoStream(path, writeMode);
if (stream->open() > 0) {
return stream;
} else {
delete stream;
return 0;
}
}
/*
* Function to suspend the IO stream (called by PowerManager)
*/
int PSPIoStream::suspend() {
#ifdef __PSP_DEBUG_SUSPEND__
_suspendCount++;
if (_handle > 0 && _pos < 0) {
_errorSuspend = SuspendError;
_errorPos = _pos;
_errorHandle = _handle;
}
#endif /* __PSP_DEBUG_SUSPEND__ */
if (_handle > 0) {
fclose((FILE *)_handle); // close our file descriptor
_handle = (void *)0xFFFFFFFF; // Set handle to non-null invalid value so makeFromPath doesn't return error
}
return 0;
}
/*
* Function to resume the IO stream (called by Power Manager)
*/
int PSPIoStream::resume() {
int ret = 0;
#ifdef __PSP_DEBUG_SUSPEND__
_suspendCount--;
#endif
// We reopen our file descriptor
_handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb");
if (_handle <= 0) {
PSPDebugSuspend("PSPIoStream::resume(): Couldn't reopen file %s\n", _path.c_str());
}
// Resume our previous position
if (_handle > 0 && _pos > 0) {
ret = fseek((FILE *)_handle, _pos, SEEK_SET);
#ifdef __PSP_DEBUG_SUSPEND__
if (ret != 0) { // Check for problem
_errorSuspend = ResumeError;
_errorPos = _pos;
_errorHandle = _handle;
}
#endif
}
return ret;
}
#endif /* __PSP__ */

View file

@ -1,86 +0,0 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#ifndef PSPSTREAM_H_
#define PSPSTREAM_H_
#include "backends/fs/stdiostream.h"
#include "backends/platform/psp/powerman.h"
#include "common/list.h"
/*
* Class to handle special suspend/resume needs of PSP IO Streams
*/
class PSPIoStream : public StdioStream, public Suspendable {
protected:
Common::String _path; /* Need to maintain for reopening after suspend */
bool _writeMode; /* "" */
int _pos; /* "" */
mutable int _ferror; /* Save file ferror */
mutable bool _feof; /* and eof */
enum {
SuspendError = 2,
ResumeError = 3
};
int _errorSuspend;
mutable int _errorSource;
#ifdef __PSP_DEBUG_SUSPEND__
int _errorPos;
void * _errorHandle;
int _suspendCount;
#endif /* __PSP_DEBUG_SUSPEND__ */
public:
/**
* Given a path, invoke fopen on that path and wrap the result in a
* PSPIoStream instance.
*/
static PSPIoStream *makeFromPath(const Common::String &path, bool writeMode);
PSPIoStream(const Common::String &path, bool writeMode);
virtual ~PSPIoStream();
void * open(); // open the file pointed to by the file path
bool err() const;
void clearErr();
bool eos() const;
virtual uint32 write(const void *dataPtr, uint32 dataSize);
virtual bool flush();
virtual int32 pos() const;
virtual int32 size() const;
virtual bool seek(int32 offs, int whence = SEEK_SET);
virtual uint32 read(void *dataPtr, uint32 dataSize);
int suspend(); /* Suspendable interface (power manager) */
int resume(); /* " " */
};
#endif /* PSPSTREAM_H_ */

View file

@ -142,7 +142,7 @@ int MidiDriver_CORE::open() {
}
if (err != noErr)
warning("Failed loading custom sound font '%s' (error %d)\n", soundfont, err);
warning("Failed loading custom sound font '%s' (error %ld)\n", soundfont, err);
}
#ifdef COREAUDIO_DISABLE_REVERB

View file

@ -109,8 +109,8 @@ void MidiDriver_CoreMIDI::close() {
}
void MidiDriver_CoreMIDI::send(uint32 b) {
assert(mOutPort != 0);
assert(mDest != 0);
assert(mOutPort != NULL);
assert(mDest != NULL);
// Extract the MIDI data
byte status_byte = (b & 0x000000FF);
@ -153,8 +153,8 @@ void MidiDriver_CoreMIDI::send(uint32 b) {
}
void MidiDriver_CoreMIDI::sysEx(const byte *msg, uint16 length) {
assert(mOutPort != 0);
assert(mDest != 0);
assert(mOutPort != NULL);
assert(mDest != NULL);
byte buf[384];
MIDIPacketList *packetList = (MIDIPacketList *)buf;

View file

@ -1,7 +1,8 @@
/* Residual - A 3D game interpreter
*
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001-2006 The ScummVM project
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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
@ -16,6 +17,9 @@
* 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.
*
* $URL$
* $Id$
*/
/*

View file

@ -7,7 +7,6 @@ MODULE_OBJS := \
fs/stdiostream.o \
fs/amigaos4/amigaos4-fs-factory.o \
fs/posix/posix-fs-factory.o \
fs/psp/psp-fs-factory.o \
fs/windows/windows-fs-factory.o \
keymapper/action.o \
keymapper/keymap.o \

View file

@ -212,6 +212,7 @@ bool OSystem_SDL::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
return handleJoyAxisMotion(ev, event);
case SDL_VIDEOEXPOSE:
/* Residual doesn't support this */
//_forceFull = true;
break;
@ -335,6 +336,7 @@ bool OSystem_SDL::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
event.type = Common::EVENT_MOUSEMOVE;
fillMouseEvent(event, ev.motion.x, ev.motion.y);
/* Residual doesn't support this */
//setMousePos(event.mouse.x, event.mouse.y);
return true;
}

View file

@ -446,8 +446,6 @@ void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x,
if (w == 0 || h == 0)
return;
/* Residual doesn't support this
_mouseCurState.hotX = hotspot_x;
_mouseCurState.hotY = hotspot_y;

View file

@ -386,16 +386,36 @@ bool OSystem_SDL::hasFeature(Feature f) {
}
void OSystem_SDL::setFeatureState(Feature f, bool enable) {
/* switch (f) {
switch (f) {
case kFeatureFullscreenMode:
//setFullscreenMode(enable);
break;
/* case kFeatureAspectRatioCorrection:
//setAspectRatioCorrection(enable);
break;
case kFeatureAutoComputeDirtyRects:
if (enable)
_modeFlags |= DF_WANT_RECT_OPTIM;
else
_modeFlags &= ~DF_WANT_RECT_OPTIM;
break;
case kFeatureIconifyWindow:
if (enable)
SDL_WM_IconifyWindow();*/
break;
default:
break;
}*/
}
}
bool OSystem_SDL::getFeatureState(Feature f) {
switch (f) {
case kFeatureFullscreenMode:
return _fullscreen;
/* case kFeatureAspectRatioCorrection:
return _videoMode.aspectRatioCorrection;
case kFeatureAutoComputeDirtyRects:
return _modeFlags & DF_WANT_RECT_OPTIM;*/
default:
return false;
}
@ -616,12 +636,12 @@ void OSystem_SDL::setupMixer() {
_samplesPerSec = SAMPLES_PER_SEC;
// Determine the sample buffer size. We want it to store enough data for
// about 1/16th of a second. Note that it must be a power of two.
// So e.g. at 22050 Hz, we request a sample buffer size of 2048.
// at least 1/16th of a second (though at maximum 8192 samples). Note
// that it must be a power of two. So e.g. at 22050 Hz, we request a
// sample buffer size of 2048.
int samples = 8192;
while (16 * samples >= _samplesPerSec) {
while (samples * 16 > _samplesPerSec * 2)
samples >>= 1;
}
memset(&desired, 0, sizeof(desired));
desired.freq = _samplesPerSec;

View file

@ -32,7 +32,7 @@
#include "common/fs.h"
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/libz.h"
#include "common/zlib.h"
#include <errno.h> // for removeSavefile()

View file

@ -1,68 +0,0 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#ifdef __PSP__
#include "backends/saves/psp/psp-saves.h"
#include "backends/platform/psp/powerman.h"
#include "common/config-manager.h"
#include "common/savefile.h"
#include <pspkernel.h>
#define PSP_DEFAULT_SAVE_PATH "ms0:/residual_savegames"
PSPSaveFileManager::PSPSaveFileManager() {
// Register default savepath
ConfMan.registerDefault("savepath", PSP_DEFAULT_SAVE_PATH);
}
/*
PSPSaveFileManager::PSPSaveFileManager(const Common::String &defaultSavepath)
: DefaultSaveFileManager(defaultSavepath) {
}
*/
void PSPSaveFileManager::checkPath(const Common::FSNode &dir) {
const char *savePath = dir.getPath().c_str();
clearError();
PowerMan.beginCriticalSection();
//check if the save directory exists
SceUID fd = sceIoDopen(savePath);
if (fd < 0) {
//No? then let's create it.
sceIoMkdir(savePath, 0777);
} else {
//it exists, so close it again.
sceIoDclose(fd);
}
PowerMan.endCriticalSection();
}
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

After

Width:  |  Height:  |  Size: 598 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

After

Width:  |  Height:  |  Size: 598 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

After

Width:  |  Height:  |  Size: 598 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

After

Width:  |  Height:  |  Size: 598 KiB

Before After
Before After

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<keyboard initial_mode="lowercase" v_align="bottom" h_align="centre">
<!-- coords key = "start x, start y, end x, end y" -->
<!-- Lowercase -->
<mode name="lowercase" resolutions="320x240,640x480">
<layout resolution="320x240" bitmap="lowercase320x240.bmp" transparent_color="255,0,255">
@ -20,7 +20,8 @@
<area shape="rect" coords="235,26,253,43" target="f11" />
<area shape="rect" coords="255,26,272,45" target="f12" />
<area shape="rect" coords="276,27,310,43" target="del" />
<area shape="rect" coords="276,47,308,64" target="backspace" />
<area shape="rect" coords="276,46,299,65" target="delete" />
<area shape="rect" coords="300,46,311,65" target="backspace" />
<area shape="rect" coords="8,68,32,85" target="tab" />
<area shape="rect" coords="36,68,53,85" target="q" />
<area shape="rect" coords="57,68,75,86" target="w" />
@ -76,8 +77,8 @@
<area shape="rect" coords="202,110,219,128" target="," />
<area shape="rect" coords="223,110,241,128" target="." />
<area shape="rect" coords="243,110,261,128" target="/" />
<area shape="rect" coords="277,133,292,148" target="ok" />
<area shape="rect" coords="292,133,309,148" target="cancel" />
<area shape="rect" coords="269,131,288,150" target="ok" />
<area shape="rect" coords="292,131,311,150" target="cancel" />
</map>
</layout>
<layout resolution="640x480" bitmap="lowercase640x480.bmp" transparent_color="255,0,255">
@ -97,7 +98,8 @@
<area shape="rect" coords="470,53,506,88" target="f11" />
<area shape="rect" coords="511,52,546,90" target="f12" />
<area shape="rect" coords="552,55,620,87" target="del" />
<area shape="rect" coords="553,94,618,129" target="backspace" />
<area shape="rect" coords="552,93,598,131" target="delete" />
<area shape="rect" coords="601,93,623,131" target="backspace" />
<area shape="rect" coords="17,136,66,171" target="tab" />
<area shape="rect" coords="73,137,108,171" target="q" />
<area shape="rect" coords="114,136,150,172" target="w" />
@ -153,8 +155,8 @@
<area shape="rect" coords="405,220,440,256" target="," />
<area shape="rect" coords="447,220,483,256" target="." />
<area shape="rect" coords="487,220,524,256" target="/" />
<area shape="rect" coords="555,266,585,298" target="ok" />
<area shape="rect" coords="585,266,619,298" target="cancel" />
<area shape="rect" coords="538,266,576,298" target="ok" />
<area shape="rect" coords="585,266,622,298" target="cancel" />
</map>
</layout>
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
@ -230,6 +232,7 @@
<event name="ok" type="submit" />
<event name="cancel" type="cancel" />
<event name="quit" type="submit" />
<event name="delete" type="delete" />
</mode>
<!-- Uppercase -->
@ -251,7 +254,8 @@
<area shape="rect" coords="235,26,253,43" target="f11" />
<area shape="rect" coords="255,26,272,45" target="f12" />
<area shape="rect" coords="276,27,310,43" target="del" />
<area shape="rect" coords="276,47,308,64" target="backspace" />
<area shape="rect" coords="276,46,299,65" target="delete" />
<area shape="rect" coords="300,46,311,65" target="backspace" />
<area shape="rect" coords="8,68,32,85" target="tab" />
<area shape="rect" coords="36,68,53,85" target="Q" />
<area shape="rect" coords="57,68,75,86" target="W" />
@ -307,6 +311,8 @@
<area shape="rect" coords="202,110,219,128" target="," />
<area shape="rect" coords="223,110,241,128" target="." />
<area shape="rect" coords="243,110,261,128" target="/" />
<area shape="rect" coords="269,131,288,150" target="ok" />
<area shape="rect" coords="292,131,311,150" target="cancel" />
</map>
</layout>
<layout resolution="640x480" bitmap="uppercase640x480.bmp" transparent_color="255,0,255">
@ -326,7 +332,8 @@
<area shape="rect" coords="470,53,506,88" target="f11" />
<area shape="rect" coords="511,52,546,90" target="f12" />
<area shape="rect" coords="552,55,620,87" target="del" />
<area shape="rect" coords="553,94,618,129" target="backspace" />
<area shape="rect" coords="552,93,598,131" target="delete" />
<area shape="rect" coords="601,93,623,131" target="backspace" />
<area shape="rect" coords="17,136,66,171" target="tab" />
<area shape="rect" coords="73,137,108,171" target="Q" />
<area shape="rect" coords="114,136,150,172" target="W" />
@ -382,6 +389,8 @@
<area shape="rect" coords="405,220,440,256" target="," />
<area shape="rect" coords="447,220,483,256" target="." />
<area shape="rect" coords="487,220,524,256" target="/" />
<area shape="rect" coords="538,266,576,298" target="ok" />
<area shape="rect" coords="585,266,622,298" target="cancel" />
</map>
</layout>
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
@ -454,7 +463,10 @@
<event name="7" type="key" code="55" ascii="55" modifiers="" />
<event name="8" type="key" code="56" ascii="56" modifiers="" />
<event name="9" type="key" code="57" ascii="57" modifiers="" />
<event name="ok" type="submit" />
<event name="cancel" type="cancel" />
<event name="quit" type="submit" />
<event name="delete" type="delete" />
</mode>
<!-- Lowercase Symbols -->
@ -489,7 +501,8 @@
<area shape="rect" coords="215,46,232,64" target=")" />
<area shape="rect" coords="235,47,252,65" target="_" />
<area shape="rect" coords="255,46,272,65" target="+" />
<area shape="rect" coords="276,47,308,64" target="backspace" />
<area shape="rect" coords="276,46,299,65" target="delete" />
<area shape="rect" coords="300,46,311,65" target="backspace" />
<area shape="rect" coords="8,68,32,85" target="tab" />
<area shape="rect" coords="36,68,53,85" target="q" />
<area shape="rect" coords="57,68,75,86" target="w" />
@ -532,6 +545,8 @@
<area shape="rect" coords="9,130,33,148" target="ctrl" />
<area shape="rect" coords="38,130,61,147" target="alt" />
<area shape="rect" coords="67,130,262,148" target="space" />
<area shape="rect" coords="269,131,288,150" target="ok" />
<area shape="rect" coords="292,131,311,150" target="cancel" />
</map>
</layout>
<layout resolution="640x480" bitmap="lowercase-symbols640x480.bmp" transparent_color="255,0,255">
@ -564,7 +579,8 @@
<area shape="rect" coords="431,93,465,130" target=")" />
<area shape="rect" coords="471,94,505,131" target="_" />
<area shape="rect" coords="511,93,546,131" target="+" />
<area shape="rect" coords="553,94,618,129" target="backspace" />
<area shape="rect" coords="552,93,598,131" target="delete" />
<area shape="rect" coords="601,93,623,131" target="backspace" />
<area shape="rect" coords="17,136,66,171" target="tab" />
<area shape="rect" coords="73,137,108,171" target="q" />
<area shape="rect" coords="114,136,150,172" target="w" />
@ -607,6 +623,8 @@
<area shape="rect" coords="19,260,68,296" target="ctrl" />
<area shape="rect" coords="76,261,123,295" target="alt" />
<area shape="rect" coords="135,261,525,297" target="space" />
<area shape="rect" coords="538,266,576,298" target="ok" />
<area shape="rect" coords="585,266,622,298" target="cancel" />
</map>
</layout>
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
@ -679,7 +697,10 @@
<event name="x" type="key" code="120" ascii="120" modifiers="" />
<event name="y" type="key" code="121" ascii="121" modifiers="" />
<event name="z" type="key" code="122" ascii="122" modifiers="" />
<event name="ok" type="submit" />
<event name="cancel" type="cancel" />
<event name="quit" type="submit" />
<event name="delete" type="delete" />
</mode>
<!-- Uppercase Symbols -->
@ -714,7 +735,8 @@
<area shape="rect" coords="215,46,232,64" target=")" />
<area shape="rect" coords="235,47,252,65" target="_" />
<area shape="rect" coords="255,46,272,65" target="+" />
<area shape="rect" coords="276,47,308,64" target="backspace" />
<area shape="rect" coords="276,46,299,65" target="delete" />
<area shape="rect" coords="300,46,311,65" target="backspace" />
<area shape="rect" coords="8,68,32,85" target="tab" />
<area shape="rect" coords="36,68,53,85" target="Q" />
<area shape="rect" coords="57,68,75,86" target="W" />
@ -757,6 +779,8 @@
<area shape="rect" coords="9,130,33,148" target="ctrl" />
<area shape="rect" coords="38,130,61,147" target="alt" />
<area shape="rect" coords="67,130,262,148" target="space" />
<area shape="rect" coords="269,131,288,150" target="ok" />
<area shape="rect" coords="292,131,311,150" target="cancel" />
</map>
</layout>
<layout resolution="640x480" bitmap="uppercase-symbols640x480.bmp" transparent_color="255,0,255">
@ -789,7 +813,8 @@
<area shape="rect" coords="431,93,465,130" target=")" />
<area shape="rect" coords="471,94,505,131" target="_" />
<area shape="rect" coords="511,93,546,131" target="+" />
<area shape="rect" coords="553,94,618,129" target="backspace" />
<area shape="rect" coords="552,93,598,131" target="delete" />
<area shape="rect" coords="601,93,623,131" target="backspace" />
<area shape="rect" coords="17,136,66,171" target="tab" />
<area shape="rect" coords="73,137,108,171" target="Q" />
<area shape="rect" coords="114,136,150,172" target="W" />
@ -832,6 +857,8 @@
<area shape="rect" coords="19,260,68,296" target="ctrl" />
<area shape="rect" coords="76,261,123,295" target="alt" />
<area shape="rect" coords="135,261,525,297" target="space" />
<area shape="rect" coords="538,266,576,298" target="ok" />
<area shape="rect" coords="585,266,622,298" target="cancel" />
</map>
</layout>
<event name="esc" type="key" code="27" ascii="27" modifiers="" />
@ -914,7 +941,9 @@
<event name="7" type="key" code="55" ascii="55" modifiers="" />
<event name="8" type="key" code="56" ascii="56" modifiers="" />
<event name="9" type="key" code="57" ascii="57" modifiers="" />
<event name="ok" type="submit" />
<event name="cancel" type="cancel" />
<event name="quit" type="submit" />
<event name="delete" type="delete" />
</mode>
</keyboard>

View file

@ -28,6 +28,7 @@
#include "backends/vkeybd/virtual-keyboard-gui.h"
#include "graphics/cursorman.h"
#include "graphics/fontman.h"
#include "gui/GuiManager.h"
namespace Common {

View file

@ -32,7 +32,7 @@
#include "common/scummsys.h"
#include "common/rect.h"
#include "common/system.h"
#include "graphics/fontman.h"
#include "graphics/font.h"
#include "graphics/surface.h"
namespace Common {

334
common/zlib.cpp Normal file
View file

@ -0,0 +1,334 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#include "common/zlib.h"
#include "common/util.h"
#if defined(USE_ZLIB)
#ifdef __SYMBIAN32__
#include <zlib\zlib.h>
#else
#include <zlib.h>
#endif
#if ZLIB_VERNUM < 0x1204
#error Version 1.2.0.4 or newer of zlib is required for this code
#endif
#endif
namespace Common {
#if defined(USE_ZLIB)
bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen) {
return Z_OK == ::uncompress(dst, dstLen, src, srcLen);
}
/**
* A simple wrapper class which can be used to wrap around an arbitrary
* other SeekableReadStream and will then provide on-the-fly decompression support.
* Assumes the compressed data to be in gzip format.
*/
class GZipReadStream : public Common::SeekableReadStream {
protected:
enum {
BUFSIZE = 16384 // 1 << MAX_WBITS
};
byte _buf[BUFSIZE];
Common::SeekableReadStream *_wrapped;
z_stream _stream;
int _zlibErr;
uint32 _pos;
uint32 _origSize;
bool _eos;
public:
GZipReadStream(Common::SeekableReadStream *w) : _wrapped(w) {
assert(w != 0);
_stream.zalloc = Z_NULL;
_stream.zfree = Z_NULL;
_stream.opaque = Z_NULL;
// Verify file header is correct
w->seek(0, SEEK_SET);
uint16 header = w->readUint16BE();
assert(header == 0x1F8B ||
((header & 0x0F00) == 0x0800 && header % 31 == 0));
if (header == 0x1F8B) {
// Retrieve the original file size
w->seek(-4, SEEK_END);
_origSize = w->readUint32LE();
} else {
// Original size not available in zlib format
_origSize = 0;
}
_pos = 0;
w->seek(0, SEEK_SET);
_eos = false;
// Adding 32 to windowBits indicates to zlib that it is supposed to
// automatically detect whether gzip or zlib headers are used for
// the compressed file. This feature was added in zlib 1.2.0.4,
// released 10 August 2003.
// Note: This is *crucial* for savegame compatibility, do *not* remove!
_zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
if (_zlibErr != Z_OK)
return;
// Setup input buffer
_stream.next_in = _buf;
_stream.avail_in = 0;
}
~GZipReadStream() {
inflateEnd(&_stream);
delete _wrapped;
}
bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); }
void clearErr() {
// only reset _eos; I/O errors are not recoverable
_eos = false;
}
uint32 read(void *dataPtr, uint32 dataSize) {
_stream.next_out = (byte *)dataPtr;
_stream.avail_out = dataSize;
// Keep going while we get no error
while (_zlibErr == Z_OK && _stream.avail_out) {
if (_stream.avail_in == 0 && !_wrapped->eos()) {
// If we are out of input data: Read more data, if available.
_stream.next_in = _buf;
_stream.avail_in = _wrapped->read(_buf, BUFSIZE);
}
_zlibErr = inflate(&_stream, Z_NO_FLUSH);
}
// Update the position counter
_pos += dataSize - _stream.avail_out;
if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0)
_eos = true;
return dataSize - _stream.avail_out;
}
bool eos() const {
return _eos;
}
int32 pos() const {
return _pos;
}
int32 size() const {
return _origSize;
}
bool seek(int32 offset, int whence = SEEK_SET) {
int32 newPos = 0;
assert(whence != SEEK_END); // SEEK_END not supported
switch (whence) {
case SEEK_SET:
newPos = offset;
break;
case SEEK_CUR:
newPos = _pos + offset;
}
assert(newPos >= 0);
if ((uint32)newPos < _pos) {
// To search backward, we have to restart the whole decompression
// from the start of the file. A rather wasteful operation, best
// to avoid it. :/
#if DEBUG
warning("Backward seeking in GZipReadStream detected");
#endif
_pos = 0;
_wrapped->seek(0, SEEK_SET);
_zlibErr = inflateReset(&_stream);
if (_zlibErr != Z_OK)
return false; // FIXME: STREAM REWRITE
_stream.next_in = _buf;
_stream.avail_in = 0;
}
offset = newPos - _pos;
// Skip the given amount of data (very inefficient if one tries to skip
// huge amounts of data, but usually client code will only skip a few
// bytes, so this should be fine.
byte tmpBuf[1024];
while (!err() && offset > 0) {
offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset));
}
_eos = false;
return true; // FIXME: STREAM REWRITE
}
};
/**
* A simple wrapper class which can be used to wrap around an arbitrary
* other WriteStream and will then provide on-the-fly compression support.
* The compressed data is written in the gzip format.
*/
class GZipWriteStream : public Common::WriteStream {
protected:
enum {
BUFSIZE = 16384 // 1 << MAX_WBITS
};
byte _buf[BUFSIZE];
Common::WriteStream *_wrapped;
z_stream _stream;
int _zlibErr;
void processData(int flushType) {
// This function is called by both write() and finalize().
while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
if (_stream.avail_out == 0) {
if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
_zlibErr = Z_ERRNO;
break;
}
_stream.next_out = _buf;
_stream.avail_out = BUFSIZE;
}
_zlibErr = deflate(&_stream, flushType);
}
}
public:
GZipWriteStream(Common::WriteStream *w) : _wrapped(w) {
assert(w != 0);
_stream.zalloc = Z_NULL;
_stream.zfree = Z_NULL;
_stream.opaque = Z_NULL;
// Adding 16 to windowBits indicates to zlib that it is supposed to
// write gzip headers. This feature was added in zlib 1.2.0.4,
// released 10 August 2003.
// Note: This is *crucial* for savegame compatibility, do *not* remove!
_zlibErr = deflateInit2(&_stream,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
MAX_WBITS + 16,
8,
Z_DEFAULT_STRATEGY);
assert(_zlibErr == Z_OK);
_stream.next_out = _buf;
_stream.avail_out = BUFSIZE;
_stream.avail_in = 0;
_stream.next_in = 0;
}
~GZipWriteStream() {
finalize();
deflateEnd(&_stream);
delete _wrapped;
}
bool err() const {
// CHECKME: does Z_STREAM_END make sense here?
return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
}
void clearErr() {
// Note: we don't reset the _zlibErr here, as it is not
// clear in general how
_wrapped->clearErr();
}
void finalize() {
if (_zlibErr != Z_OK)
return;
// Process whatever remaining data there is.
processData(Z_FINISH);
// Since processData only writes out blocks of size BUFSIZE,
// we may have to flush some stragglers.
uint remainder = BUFSIZE - _stream.avail_out;
if (remainder > 0) {
if (_wrapped->write(_buf, remainder) != remainder) {
_zlibErr = Z_ERRNO;
}
}
// Finalize the wrapped savefile, too
_wrapped->finalize();
}
uint32 write(const void *dataPtr, uint32 dataSize) {
if (err())
return 0;
// Hook in the new data ...
// Note: We need to make a const_cast here, as zlib is not aware
// of the const keyword.
_stream.next_in = const_cast<byte *>((const byte *)dataPtr);
_stream.avail_in = dataSize;
// ... and flush it to disk
processData(Z_NO_FLUSH);
return dataSize - _stream.avail_in;
}
};
#endif // USE_ZLIB
Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped) {
#if defined(USE_ZLIB)
if (toBeWrapped) {
uint16 header = toBeWrapped->readUint16BE();
bool isCompressed = (header == 0x1F8B ||
((header & 0x0F00) == 0x0800 &&
header % 31 == 0));
toBeWrapped->seek(-2, SEEK_CUR);
if (isCompressed)
return new GZipReadStream(toBeWrapped);
}
#endif
return toBeWrapped;
}
Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped) {
#if defined(USE_ZLIB)
if (toBeWrapped)
return new GZipWriteStream(toBeWrapped);
#endif
return toBeWrapped;
}
} // End of namespace Common

72
common/zlib.h Normal file
View file

@ -0,0 +1,72 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#ifndef COMMON_ZLIB_H
#define COMMON_ZLIB_H
#include "common/sys.h"
#include "common/stream.h"
namespace Common {
#if defined(USE_ZLIB)
/**
* Thin wrapper around zlib's uncompress() function. This wrapper makes
* it possible to uncompress data in engines without being forced to link
* them against zlib, thus simplifying the build system.
*
* @return true on success (i.e. Z_OK), false otherwise
*/
bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen);
#endif
/**
* Take an arbitrary SeekableReadStream and wrap it in a custom stream which
* provides transparent on-the-fly decompression. Assumes the data it
* retrieves from the wrapped stream to be either uncompressed or in gzip
* format. In the former case, the original stream is returned unmodified
* (and in particular, not wrapped).
*
* It is safe to call this with a NULL parameter (in this case, NULL is
* returned).
*/
Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped);
/**
* Take an arbitrary WriteStream and wrap it in a custom stream which provides
* transparent on-the-fly compression. The compressed data is written in the
* gzip format, unless ZLIB support has been disabled, in which case the given
* stream is returned unmodified (and in particular, not wrapped).
*
* It is safe to call this with a NULL parameter (in this case, NULL is
* returned).
*/
Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped);
} // End of namespace Common
#endif

743
sound/decoders/flac.cpp Normal file
View file

@ -0,0 +1,743 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#include "sound/decoders/flac.h"
#ifdef USE_FLAC
#include "common/debug.h"
#include "common/stream.h"
#include "common/util.h"
#include "sound/audiostream.h"
#include "sound/audiocd.h"
#define FLAC__NO_DLL // that MS-magic gave me headaches - just link the library you like
#include <FLAC/export.h>
// check if we have FLAC >= 1.1.3; LEGACY_FLAC code can be removed once FLAC-1.1.3 propagates everywhere
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8
#define LEGACY_FLAC
#else
#undef LEGACY_FLAC
#endif
#ifdef LEGACY_FLAC
// Before FLAC 1.1.3, we needed to use the stream decoder API.
#include <FLAC/seekable_stream_decoder.h>
typedef uint FLAC_size_t;
#else
// With FLAC 1.1.3, the stream decoder API was merged into the regular
// stream API. In order to stay compatible with older FLAC versions, we
// simply add some typedefs and #ifdefs to map between the old and new API.
// We use the typedefs (instead of only #defines) in order to somewhat
// improve the readability of the code.
#include <FLAC/stream_decoder.h>
typedef size_t FLAC_size_t;
// Add aliases for the old names
typedef FLAC__StreamDecoderState FLAC__SeekableStreamDecoderState;
typedef FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus;
typedef FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus;
typedef FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus;
typedef FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus;
typedef FLAC__StreamDecoder FLAC__SeekableStreamDecoder;
#endif
namespace Audio {
#pragma mark -
#pragma mark --- FLAC stream ---
#pragma mark -
static const uint MAX_OUTPUT_CHANNELS = 2;
class FLACStream : public SeekableAudioStream {
protected:
Common::SeekableReadStream *_inStream;
bool _disposeAfterUse;
::FLAC__SeekableStreamDecoder *_decoder;
/** Header of the stream */
FLAC__StreamMetadata_StreamInfo _streaminfo;
/** index + 1(!) of the last sample to be played */
FLAC__uint64 _lastSample;
/** total play time */
Timestamp _length;
/** true if the last sample was decoded from the FLAC-API - there might still be data in the buffer */
bool _lastSampleWritten;
typedef int16 SampleType;
enum { BUFTYPE_BITS = 16 };
enum {
// Maximal buffer size. According to the FLAC format specification, the block size is
// a 16 bit value (in fact it seems the maximal block size is 32768, but we play it safe).
BUFFER_SIZE = 65536
};
struct {
SampleType bufData[BUFFER_SIZE];
SampleType *bufReadPos;
uint bufFill;
} _sampleCache;
SampleType *_outBuffer;
uint _requestedSamples;
typedef void (*PFCONVERTBUFFERS)(SampleType*, const FLAC__int32*[], uint, const uint, const uint8);
PFCONVERTBUFFERS _methodConvertBuffers;
public:
FLACStream(Common::SeekableReadStream *inStream, bool dispose);
virtual ~FLACStream();
int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const { return _streaminfo.channels >= 2; }
int getRate() const { return _streaminfo.sample_rate; }
bool endOfData() const {
// End of data is reached if there either is no valid stream data available,
// or if we reached the last sample and completely emptied the sample cache.
return _streaminfo.channels == 0 || (_lastSampleWritten && _sampleCache.bufFill == 0);
}
bool seek(const Timestamp &where);
Timestamp getLength() const { return _length; }
bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC ; }
protected:
uint getChannels() const { return MIN<uint>(_streaminfo.channels, MAX_OUTPUT_CHANNELS); }
bool allocateBuffer(uint minSamples);
inline FLAC__StreamDecoderState getStreamDecoderState() const;
inline bool processSingleBlock();
inline bool processUntilEndOfMetadata();
bool seekAbsolute(FLAC__uint64 sample);
inline ::FLAC__SeekableStreamDecoderReadStatus callbackRead(FLAC__byte buffer[], FLAC_size_t *bytes);
inline ::FLAC__SeekableStreamDecoderSeekStatus callbackSeek(FLAC__uint64 absoluteByteOffset);
inline ::FLAC__SeekableStreamDecoderTellStatus callbackTell(FLAC__uint64 *absoluteByteOffset);
inline ::FLAC__SeekableStreamDecoderLengthStatus callbackLength(FLAC__uint64 *streamLength);
inline bool callbackEOF();
inline ::FLAC__StreamDecoderWriteStatus callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
inline void callbackMetadata(const ::FLAC__StreamMetadata *metadata);
inline void callbackError(::FLAC__StreamDecoderErrorStatus status);
private:
static ::FLAC__SeekableStreamDecoderReadStatus callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], FLAC_size_t *bytes, void *clientData);
static ::FLAC__SeekableStreamDecoderSeekStatus callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData);
static ::FLAC__SeekableStreamDecoderTellStatus callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData);
static ::FLAC__SeekableStreamDecoderLengthStatus callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData);
static FLAC__bool callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData);
static ::FLAC__StreamDecoderWriteStatus callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData);
static void callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData);
static void callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData);
void setBestConvertBufferMethod();
static void convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
static void convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
};
FLACStream::FLACStream(Common::SeekableReadStream *inStream, bool dispose)
#ifdef LEGACY_FLAC
: _decoder(::FLAC__seekable_stream_decoder_new()),
#else
: _decoder(::FLAC__stream_decoder_new()),
#endif
_inStream(inStream),
_disposeAfterUse(dispose),
_length(0, 1000), _lastSample(0),
_outBuffer(NULL), _requestedSamples(0), _lastSampleWritten(false),
_methodConvertBuffers(&FLACStream::convertBuffersGeneric)
{
assert(_inStream);
memset(&_streaminfo, 0, sizeof(_streaminfo));
_sampleCache.bufReadPos = NULL;
_sampleCache.bufFill = 0;
_methodConvertBuffers = &FLACStream::convertBuffersGeneric;
bool success;
#ifdef LEGACY_FLAC
::FLAC__seekable_stream_decoder_set_read_callback(_decoder, &FLACStream::callWrapRead);
::FLAC__seekable_stream_decoder_set_seek_callback(_decoder, &FLACStream::callWrapSeek);
::FLAC__seekable_stream_decoder_set_tell_callback(_decoder, &FLACStream::callWrapTell);
::FLAC__seekable_stream_decoder_set_length_callback(_decoder, &FLACStream::callWrapLength);
::FLAC__seekable_stream_decoder_set_eof_callback(_decoder, &FLACStream::callWrapEOF);
::FLAC__seekable_stream_decoder_set_write_callback(_decoder, &FLACStream::callWrapWrite);
::FLAC__seekable_stream_decoder_set_metadata_callback(_decoder, &FLACStream::callWrapMetadata);
::FLAC__seekable_stream_decoder_set_error_callback(_decoder, &FLACStream::callWrapError);
::FLAC__seekable_stream_decoder_set_client_data(_decoder, (void*)this);
success = (::FLAC__seekable_stream_decoder_init(_decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK);
#else
success = (::FLAC__stream_decoder_init_stream(
_decoder,
&FLACStream::callWrapRead,
&FLACStream::callWrapSeek,
&FLACStream::callWrapTell,
&FLACStream::callWrapLength,
&FLACStream::callWrapEOF,
&FLACStream::callWrapWrite,
&FLACStream::callWrapMetadata,
&FLACStream::callWrapError,
(void*)this
) == FLAC__STREAM_DECODER_INIT_STATUS_OK);
#endif
if (success) {
if (processUntilEndOfMetadata() && _streaminfo.channels > 0) {
_lastSample = _streaminfo.total_samples + 1;
_length = Timestamp(0, _lastSample - 1, getRate());
return; // no error occured
}
}
warning("FLACStream: could not create audio stream");
}
FLACStream::~FLACStream() {
if (_decoder != NULL) {
#ifdef LEGACY_FLAC
(void) ::FLAC__seekable_stream_decoder_finish(_decoder);
::FLAC__seekable_stream_decoder_delete(_decoder);
#else
(void) ::FLAC__stream_decoder_finish(_decoder);
::FLAC__stream_decoder_delete(_decoder);
#endif
}
if (_disposeAfterUse)
delete _inStream;
}
inline FLAC__StreamDecoderState FLACStream::getStreamDecoderState() const {
assert(_decoder != NULL);
#ifdef LEGACY_FLAC
return ::FLAC__seekable_stream_decoder_get_stream_decoder_state(_decoder);
#else
return ::FLAC__stream_decoder_get_state(_decoder);
#endif
}
inline bool FLACStream::processSingleBlock() {
assert(_decoder != NULL);
#ifdef LEGACY_FLAC
return 0 != ::FLAC__seekable_stream_decoder_process_single(_decoder);
#else
return 0 != ::FLAC__stream_decoder_process_single(_decoder);
#endif
}
inline bool FLACStream::processUntilEndOfMetadata() {
assert(_decoder != NULL);
#ifdef LEGACY_FLAC
return 0 != ::FLAC__seekable_stream_decoder_process_until_end_of_metadata(_decoder);
#else
return 0 != ::FLAC__stream_decoder_process_until_end_of_metadata(_decoder);
#endif
}
bool FLACStream::seekAbsolute(FLAC__uint64 sample) {
assert(_decoder != NULL);
#ifdef LEGACY_FLAC
const bool result = (0 != ::FLAC__seekable_stream_decoder_seek_absolute(_decoder, sample));
#else
const bool result = (0 != ::FLAC__stream_decoder_seek_absolute(_decoder, sample));
#endif
if (result) {
_lastSampleWritten = (_lastSample != 0 && sample >= _lastSample); // only set if we are SURE
}
return result;
}
bool FLACStream::seek(const Timestamp &where) {
_sampleCache.bufFill = 0;
_sampleCache.bufReadPos = NULL;
// FLAC uses the sample pair number, thus we always use "false" for the isStereo parameter
// of the convertTimeToStreamPos helper.
return seekAbsolute((FLAC__uint64)convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
}
int FLACStream::readBuffer(int16 *buffer, const int numSamples) {
const uint numChannels = getChannels();
if (numChannels == 0) {
warning("FLACStream: Stream not sucessfully initialised, cant playback");
return -1; // streaminfo wasnt read!
}
assert(numSamples % numChannels == 0); // must be multiple of channels!
assert(buffer != NULL);
assert(_outBuffer == NULL);
assert(_requestedSamples == 0);
_outBuffer = buffer;
_requestedSamples = numSamples;
// If there is still data in our buffer from the last time around,
// copy that first.
if (_sampleCache.bufFill > 0) {
assert(_sampleCache.bufReadPos >= _sampleCache.bufData);
assert(_sampleCache.bufFill % numChannels == 0);
const uint copySamples = MIN((uint)numSamples, _sampleCache.bufFill);
memcpy(buffer, _sampleCache.bufReadPos, copySamples*sizeof(buffer[0]));
_outBuffer = buffer + copySamples;
_requestedSamples = numSamples - copySamples;
_sampleCache.bufReadPos += copySamples;
_sampleCache.bufFill -= copySamples;
}
bool decoderOk = true;
FLAC__StreamDecoderState state = getStreamDecoderState();
// Keep poking FLAC to process more samples until we completely satisfied the request
// respectively until we run out of data.
while (!_lastSampleWritten && _requestedSamples > 0 && state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) {
assert(_sampleCache.bufFill == 0);
assert(_requestedSamples % numChannels == 0);
processSingleBlock();
state = getStreamDecoderState();
if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
_lastSampleWritten = true;
}
// Error handling
switch (state) {
case FLAC__STREAM_DECODER_END_OF_STREAM:
_lastSampleWritten = true;
break;
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
break;
default:
decoderOk = false;
warning("FLACStream: An error occured while decoding. DecoderState is: %s",
FLAC__StreamDecoderStateString[getStreamDecoderState()]);
}
// Compute how many samples we actually produced
const int samples = (int)(_outBuffer - buffer);
assert(samples % numChannels == 0);
_outBuffer = NULL; // basically unnecessary, only for the purpose of the asserts
_requestedSamples = 0; // basically unnecessary, only for the purpose of the asserts
return decoderOk ? samples : -1;
}
inline ::FLAC__SeekableStreamDecoderReadStatus FLACStream::callbackRead(FLAC__byte buffer[], FLAC_size_t *bytes) {
if (*bytes == 0) {
#ifdef LEGACY_FLAC
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; /* abort to avoid a deadlock */
#else
return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
#endif
}
const uint32 bytesRead = _inStream->read(buffer, *bytes);
if (bytesRead == 0) {
#ifdef LEGACY_FLAC
return _inStream->eos() ? FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
#else
return _inStream->eos() ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_ABORT;
#endif
}
*bytes = static_cast<uint>(bytesRead);
#ifdef LEGACY_FLAC
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
#else
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
#endif
}
void FLACStream::setBestConvertBufferMethod() {
PFCONVERTBUFFERS tempMethod = &FLACStream::convertBuffersGeneric;
const uint numChannels = getChannels();
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
assert(numChannels >= 1);
assert(numBits >= 4 && numBits <=32);
if (numChannels == 1) {
if (numBits == 8)
tempMethod = &FLACStream::convertBuffersMono8Bit;
if (numBits == BUFTYPE_BITS)
tempMethod = &FLACStream::convertBuffersMonoNS;
} else if (numChannels == 2) {
if (numBits == 8)
tempMethod = &FLACStream::convertBuffersStereo8Bit;
if (numBits == BUFTYPE_BITS)
tempMethod = &FLACStream::convertBuffersStereoNS;
} /* else ... */
_methodConvertBuffers = tempMethod;
}
// 1 channel, no scaling
void FLACStream::convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 1);
assert(numBits == BUFTYPE_BITS);
FLAC__int32 const* inChannel1 = inChannels[0];
while (numSamples >= 4) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
bufDestination[1] = static_cast<SampleType>(inChannel1[1]);
bufDestination[2] = static_cast<SampleType>(inChannel1[2]);
bufDestination[3] = static_cast<SampleType>(inChannel1[3]);
bufDestination += 4;
inChannel1 += 4;
numSamples -= 4;
}
for (; numSamples > 0; --numSamples) {
*bufDestination++ = static_cast<SampleType>(*inChannel1++);
}
inChannels[0] = inChannel1;
assert(numSamples == 0); // dint copy too many samples
}
// 1 channel, scaling from 8Bit
void FLACStream::convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 1);
assert(numBits == 8);
assert(8 < BUFTYPE_BITS);
FLAC__int32 const* inChannel1 = inChannels[0];
while (numSamples >= 4) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
bufDestination[1] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
bufDestination[2] = static_cast<SampleType>(inChannel1[2]) << (BUFTYPE_BITS - 8);
bufDestination[3] = static_cast<SampleType>(inChannel1[3]) << (BUFTYPE_BITS - 8);
bufDestination += 4;
inChannel1 += 4;
numSamples -= 4;
}
for (; numSamples > 0; --numSamples) {
*bufDestination++ = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
}
inChannels[0] = inChannel1;
assert(numSamples == 0); // dint copy too many samples
}
// 2 channels, no scaling
void FLACStream::convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 2);
assert(numBits == BUFTYPE_BITS);
assert(numSamples % 2 == 0); // must be integral multiply of channels
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
while (numSamples >= 2*2) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
bufDestination[1] = static_cast<SampleType>(inChannel2[0]);
bufDestination[2] = static_cast<SampleType>(inChannel1[1]);
bufDestination[3] = static_cast<SampleType>(inChannel2[1]);
bufDestination += 2 * 2;
inChannel1 += 2;
inChannel2 += 2;
numSamples -= 2 * 2;
}
while (numSamples > 0) {
bufDestination[0] = static_cast<SampleType>(*inChannel1++);
bufDestination[1] = static_cast<SampleType>(*inChannel2++);
bufDestination += 2;
numSamples -= 2;
}
inChannels[0] = inChannel1;
inChannels[1] = inChannel2;
assert(numSamples == 0); // dint copy too many samples
}
// 2 channels, scaling from 8Bit
void FLACStream::convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numChannels == 2);
assert(numBits == 8);
assert(numSamples % 2 == 0); // must be integral multiply of channels
assert(8 < BUFTYPE_BITS);
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
while (numSamples >= 2*2) {
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
bufDestination[1] = static_cast<SampleType>(inChannel2[0]) << (BUFTYPE_BITS - 8);
bufDestination[2] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
bufDestination[3] = static_cast<SampleType>(inChannel2[1]) << (BUFTYPE_BITS - 8);
bufDestination += 2 * 2;
inChannel1 += 2;
inChannel2 += 2;
numSamples -= 2 * 2;
}
while (numSamples > 0) {
bufDestination[0] = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
bufDestination[1] = static_cast<SampleType>(*inChannel2++) << (BUFTYPE_BITS - 8);
bufDestination += 2;
numSamples -= 2;
}
inChannels[0] = inChannel1;
inChannels[1] = inChannel2;
assert(numSamples == 0); // dint copy too many samples
}
// all Purpose-conversion - slowest of em all
void FLACStream::convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
assert(numSamples % numChannels == 0); // must be integral multiply of channels
if (numBits < BUFTYPE_BITS) {
const uint8 kPower = (uint8)(BUFTYPE_BITS - numBits);
for (; numSamples > 0; numSamples -= numChannels) {
for (uint i = 0; i < numChannels; ++i)
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++)) << kPower;
}
} else if (numBits > BUFTYPE_BITS) {
const uint8 kPower = (uint8)(numBits - BUFTYPE_BITS);
for (; numSamples > 0; numSamples -= numChannels) {
for (uint i = 0; i < numChannels; ++i)
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++) >> kPower) ;
}
} else {
for (; numSamples > 0; numSamples -= numChannels) {
for (uint i = 0; i < numChannels; ++i)
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++));
}
}
assert(numSamples == 0); // dint copy too many samples
}
inline ::FLAC__StreamDecoderWriteStatus FLACStream::callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) {
assert(frame->header.channels == _streaminfo.channels);
assert(frame->header.sample_rate == _streaminfo.sample_rate);
assert(frame->header.bits_per_sample == _streaminfo.bits_per_sample);
assert(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER || _streaminfo.min_blocksize == _streaminfo.max_blocksize);
// We require that either the sample cache is empty, or that no samples were requested
assert(_sampleCache.bufFill == 0 || _requestedSamples == 0);
uint numSamples = frame->header.blocksize;
const uint numChannels = getChannels();
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
assert(_requestedSamples % numChannels == 0); // must be integral multiply of channels
const FLAC__uint64 firstSampleNumber = (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) ?
frame->header.number.sample_number : (static_cast<FLAC__uint64>(frame->header.number.frame_number)) * _streaminfo.max_blocksize;
// Check whether we are about to reach beyond the last sample we are supposed to play.
if (_lastSample != 0 && firstSampleNumber + numSamples >= _lastSample) {
numSamples = (uint)(firstSampleNumber >= _lastSample ? 0 : _lastSample - firstSampleNumber);
_lastSampleWritten = true;
}
// The value in _requestedSamples counts raw samples, so if there are more than one
// channel, we have to multiply the number of available sample "pairs" by numChannels
numSamples *= numChannels;
const FLAC__int32 *inChannels[MAX_OUTPUT_CHANNELS];
for (uint i = 0; i < numChannels; ++i)
inChannels[i] = buffer[i];
// write the incoming samples directly into the buffer provided to us by the mixer
if (_requestedSamples > 0) {
assert(_requestedSamples % numChannels == 0);
assert(_outBuffer != NULL);
// Copy & convert the available samples (limited both by how many we have available, and
// by how many are actually needed).
const uint copySamples = MIN(_requestedSamples, numSamples);
(*_methodConvertBuffers)(_outBuffer, inChannels, copySamples, numChannels, numBits);
_requestedSamples -= copySamples;
numSamples -= copySamples;
_outBuffer += copySamples;
}
// Write all remaining samples (i.e. those which didn't fit into the mixer buffer)
// into the sample cache.
if (_sampleCache.bufFill == 0)
_sampleCache.bufReadPos = _sampleCache.bufData;
const uint cacheSpace = (_sampleCache.bufData + BUFFER_SIZE) - (_sampleCache.bufReadPos + _sampleCache.bufFill);
assert(numSamples <= cacheSpace);
(*_methodConvertBuffers)(_sampleCache.bufReadPos + _sampleCache.bufFill, inChannels, numSamples, numChannels, numBits);
_sampleCache.bufFill += numSamples;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
inline ::FLAC__SeekableStreamDecoderSeekStatus FLACStream::callbackSeek(FLAC__uint64 absoluteByteOffset) {
_inStream->seek(absoluteByteOffset, SEEK_SET);
const bool result = (absoluteByteOffset == (FLAC__uint64)_inStream->pos());
#ifdef LEGACY_FLAC
return result ? FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
#else
return result ? FLAC__STREAM_DECODER_SEEK_STATUS_OK : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
#endif
}
inline ::FLAC__SeekableStreamDecoderTellStatus FLACStream::callbackTell(FLAC__uint64 *absoluteByteOffset) {
*absoluteByteOffset = static_cast<FLAC__uint64>(_inStream->pos());
#ifdef LEGACY_FLAC
return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
#else
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
#endif
}
inline ::FLAC__SeekableStreamDecoderLengthStatus FLACStream::callbackLength(FLAC__uint64 *streamLength) {
*streamLength = static_cast<FLAC__uint64>(_inStream->size());
#ifdef LEGACY_FLAC
return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
#else
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
#endif
}
inline bool FLACStream::callbackEOF() {
return _inStream->eos();
}
inline void FLACStream::callbackMetadata(const ::FLAC__StreamMetadata *metadata) {
assert(_decoder != NULL);
assert(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); // others arent really interesting
_streaminfo = metadata->data.stream_info;
setBestConvertBufferMethod(); // should be set after getting stream-information. FLAC always parses the info first
}
inline void FLACStream::callbackError(::FLAC__StreamDecoderErrorStatus status) {
// some of these are non-critical-Errors
debug(1, "FLACStream: An error occured while decoding. DecoderState is: %s",
FLAC__StreamDecoderErrorStatusString[status]);
}
/* Static Callback Wrappers */
::FLAC__SeekableStreamDecoderReadStatus FLACStream::callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], FLAC_size_t *bytes, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
return instance->callbackRead(buffer, bytes);
}
::FLAC__SeekableStreamDecoderSeekStatus FLACStream::callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
return instance->callbackSeek(absoluteByteOffset);
}
::FLAC__SeekableStreamDecoderTellStatus FLACStream::callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
return instance->callbackTell(absoluteByteOffset);
}
::FLAC__SeekableStreamDecoderLengthStatus FLACStream::callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
return instance->callbackLength(streamLength);
}
FLAC__bool FLACStream::callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
return instance->callbackEOF();
}
::FLAC__StreamDecoderWriteStatus FLACStream::callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
return instance->callbackWrite(frame, buffer);
}
void FLACStream::callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
instance->callbackMetadata(metadata);
}
void FLACStream::callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData) {
FLACStream *instance = (FLACStream *)clientData;
assert(0 != instance);
instance->callbackError(status);
}
#pragma mark -
#pragma mark --- FLAC factory functions ---
#pragma mark -
SeekableAudioStream *makeFLACStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new FLACStream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return 0;
} else {
return s;
}
}
} // End of namespace Audio
#endif // #ifdef USE_FLAC

View file

@ -23,29 +23,51 @@
*
*/
#ifndef BACKEND_PSP_SAVES_H
#define BACKEND_PSP_SAVES_H
/**
* @file
* Sound decoder used in engines:
* - agos
* - kyra
* - m4
* - queen
* - saga
* - scumm
* - sword1
* - sword2
* - touche
* - tucker
*/
#include "backends/saves/default/default-saves.h"
#ifndef SOUND_FLAC_H
#define SOUND_FLAC_H
#include "common/types.h"
#include "common/sys.h"
#ifdef USE_FLAC
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class AudioStream;
class SeekableAudioStream;
/**
* Customization of the DefaultSaveFileManager for the PSP platform.
* The only two differences are that the default constructor sets
* up a default savepath, and that checkPath tries to create the savedir,
* if missing, via the sceIoMkdir() call.
* Create a new SeekableAudioStream from the FLAC data in the given stream.
* Allows for seeking (which is why we require a SeekableReadStream).
*
* @param stream the SeekableReadStream from which to read the FLAC data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occured
*/
class PSPSaveFileManager : public DefaultSaveFileManager {
public:
PSPSaveFileManager();
// PSPSaveFileManager(const Common::String &defaultSavepath);
SeekableAudioStream *makeFLACStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
protected:
/**
* Checks the given path for read access, existence, etc.
* In addition, tries to create a missing savedir, if possible.
* Sets the internal error and error message accordingly.
*/
virtual void checkPath(const Common::FSNode &dir);
};
} // End of namespace Audio
#endif
#endif // #ifdef USE_FLAC
#endif // #ifndef SOUND_FLAC_H

352
sound/decoders/mp3.cpp Normal file
View file

@ -0,0 +1,352 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#include "sound/decoders/mp3.h"
#ifdef USE_MAD
#include "common/debug.h"
#include "common/stream.h"
#include "common/util.h"
#include "sound/audiocd.h"
#include "sound/audiostream.h"
#include <mad.h>
namespace Audio {
#pragma mark -
#pragma mark --- MP3 (MAD) stream ---
#pragma mark -
class MP3Stream : public SeekableAudioStream {
protected:
enum State {
MP3_STATE_INIT, // Need to init the decoder
MP3_STATE_READY, // ready for processing data
MP3_STATE_EOS // end of data reached (may need to loop)
};
Common::SeekableReadStream *_inStream;
DisposeAfterUse::Flag _disposeAfterUse;
uint _posInFrame;
State _state;
Timestamp _length;
mad_timer_t _totalTime;
mad_stream _stream;
mad_frame _frame;
mad_synth _synth;
enum {
BUFFER_SIZE = 5 * 8192
};
// This buffer contains a slab of input data
byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
public:
MP3Stream(Common::SeekableReadStream *inStream,
DisposeAfterUse::Flag dispose);
~MP3Stream();
int readBuffer(int16 *buffer, const int numSamples);
bool endOfData() const { return _state == MP3_STATE_EOS; }
bool isStereo() const { return MAD_NCHANNELS(&_frame.header) == 2; }
int getRate() const { return _frame.header.samplerate; }
bool seek(const Timestamp &where);
Timestamp getLength() const { return _length; }
protected:
void decodeMP3Data();
void readMP3Data();
void initStream();
void readHeader();
void deinitStream();
};
MP3Stream::MP3Stream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
_inStream(inStream),
_disposeAfterUse(dispose),
_posInFrame(0),
_state(MP3_STATE_INIT),
_length(0, 1000),
_totalTime(mad_timer_zero) {
// The MAD_BUFFER_GUARD must always contain zeros (the reason
// for this is that the Layer III Huffman decoder of libMAD
// may read a few bytes beyond the end of the input buffer).
memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD);
// Calculate the length of the stream
initStream();
while (_state != MP3_STATE_EOS)
readHeader();
_length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate());
deinitStream();
// Reinit stream
_state = MP3_STATE_INIT;
// Decode the first chunk of data. This is necessary so that _frame
// is setup and isStereo() and getRate() return correct results.
decodeMP3Data();
}
MP3Stream::~MP3Stream() {
deinitStream();
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _inStream;
}
void MP3Stream::decodeMP3Data() {
do {
if (_state == MP3_STATE_INIT)
initStream();
if (_state == MP3_STATE_EOS)
return;
// If necessary, load more data into the stream decoder
if (_stream.error == MAD_ERROR_BUFLEN)
readMP3Data();
while (_state == MP3_STATE_READY) {
// Decode the next frame
if (mad_frame_decode(&_frame, &_stream) == -1) {
if (_stream.error == MAD_ERROR_BUFLEN) {
break; // Read more data
} else if (MAD_RECOVERABLE(_stream.error)) {
// Note: we will occasionally see MAD_ERROR_BADDATAPTR errors here.
// These are normal and expected (caused by our frame skipping (i.e. "seeking")
// code above).
debug(6, "MP3Stream: Recoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
continue;
} else {
warning("MP3Stream: Unrecoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
break;
}
}
// Synthesize PCM data
mad_synth_frame(&_synth, &_frame);
_posInFrame = 0;
break;
}
} while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
if (_stream.error != MAD_ERROR_NONE)
_state = MP3_STATE_EOS;
}
void MP3Stream::readMP3Data() {
uint32 remaining = 0;
// Give up immediately if we already used up all data in the stream
if (_inStream->eos()) {
_state = MP3_STATE_EOS;
return;
}
if (_stream.next_frame) {
// If there is still data in the MAD stream, we need to preserve it.
// Note that we use memmove, as we are reusing the same buffer,
// and hence the data regions we copy from and to may overlap.
remaining = _stream.bufend - _stream.next_frame;
assert(remaining < BUFFER_SIZE); // Paranoia check
memmove(_buf, _stream.next_frame, remaining);
}
// Try to read the next block
uint32 size = _inStream->read(_buf + remaining, BUFFER_SIZE - remaining);
if (size <= 0) {
_state = MP3_STATE_EOS;
return;
}
// Feed the data we just read into the stream decoder
_stream.error = MAD_ERROR_NONE;
mad_stream_buffer(&_stream, _buf, size + remaining);
}
bool MP3Stream::seek(const Timestamp &where) {
if (where == _length) {
_state = MP3_STATE_EOS;
return true;
} else if (where > _length) {
return false;
}
const uint32 time = where.msecs();
mad_timer_t destination;
mad_timer_set(&destination, time / 1000, time % 1000, 1000);
if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0)
initStream();
while (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS)
readHeader();
return (_state != MP3_STATE_EOS);
}
void MP3Stream::initStream() {
if (_state != MP3_STATE_INIT)
deinitStream();
// Init MAD
mad_stream_init(&_stream);
mad_frame_init(&_frame);
mad_synth_init(&_synth);
// Reset the stream data
_inStream->seek(0, SEEK_SET);
_totalTime = mad_timer_zero;
_posInFrame = 0;
// Update state
_state = MP3_STATE_READY;
// Read the first few sample bytes
readMP3Data();
}
void MP3Stream::readHeader() {
if (_state != MP3_STATE_READY)
return;
// If necessary, load more data into the stream decoder
if (_stream.error == MAD_ERROR_BUFLEN)
readMP3Data();
while (_state != MP3_STATE_EOS) {
_stream.error = MAD_ERROR_NONE;
// Decode the next header. Note: mad_frame_decode would do this for us, too.
// However, for seeking we don't want to decode the full frame (else it would
// be far too slow). Hence we perform this explicitly in a separate step.
if (mad_header_decode(&_frame.header, &_stream) == -1) {
if (_stream.error == MAD_ERROR_BUFLEN) {
readMP3Data(); // Read more data
continue;
} else if (MAD_RECOVERABLE(_stream.error)) {
debug(6, "MP3Stream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
continue;
} else {
warning("MP3Stream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
break;
}
}
// Sum up the total playback time so far
mad_timer_add(&_totalTime, _frame.header.duration);
break;
}
if (_stream.error != MAD_ERROR_NONE)
_state = MP3_STATE_EOS;
}
void MP3Stream::deinitStream() {
if (_state == MP3_STATE_INIT)
return;
// Deinit MAD
mad_synth_finish(&_synth);
mad_frame_finish(&_frame);
mad_stream_finish(&_stream);
_state = MP3_STATE_EOS;
}
static inline int scale_sample(mad_fixed_t sample) {
// round
sample += (1L << (MAD_F_FRACBITS - 16));
// clip
if (sample > MAD_F_ONE - 1)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
// quantize and scale to not saturate when mixing a lot of channels
return sample >> (MAD_F_FRACBITS + 1 - 16);
}
int MP3Stream::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
// Keep going as long as we have input available
while (samples < numSamples && _state != MP3_STATE_EOS) {
const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * MAD_NCHANNELS(&_frame.header));
while (samples < len) {
*buffer++ = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
samples++;
if (MAD_NCHANNELS(&_frame.header) == 2) {
*buffer++ = (int16)scale_sample(_synth.pcm.samples[1][_posInFrame]);
samples++;
}
_posInFrame++;
}
if (_posInFrame >= _synth.pcm.length) {
// We used up all PCM data in the current frame -- read & decode more
decodeMP3Data();
}
}
return samples;
}
#pragma mark -
#pragma mark --- MP3 factory functions ---
#pragma mark -
SeekableAudioStream *makeMP3Stream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new MP3Stream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return 0;
} else {
return s;
}
}
} // End of namespace Audio
#endif // #ifdef USE_MAD

View file

@ -8,42 +8,66 @@
* 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.
*
* $URL$
* $Id$
*
*/
#ifndef PSP_FILESYSTEM_FACTORY_H
#define PSP_FILESYSTEM_FACTORY_H
#include "common/singleton.h"
#include "backends/fs/fs-factory.h"
/**
* Creates PSPFilesystemNode objects.
*
* Parts of this class are documented in the base interface class, FilesystemFactory.
* @file
* Sound decoder used in engines:
* - agos
* - kyra
* - m4
* - queen
* - saga
* - scumm
* - sword1
* - sword2
* - touche
* - tucker
*/
class PSPFilesystemFactory : public FilesystemFactory, public Common::Singleton<PSPFilesystemFactory> {
public:
virtual AbstractFSNode *makeRootFileNode() const;
virtual AbstractFSNode *makeCurrentDirectoryFileNode() const;
virtual AbstractFSNode *makeFileNodePath(const Common::String &path) const;
protected:
PSPFilesystemFactory() {};
#ifndef SOUND_MP3_H
#define SOUND_MP3_H
private:
friend class Common::Singleton<SingletonBaseType>;
};
#include "common/types.h"
#include "common/sys.h"
#endif /*PSP_FILESYSTEM_FACTORY_H*/
#ifdef USE_MAD
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class AudioStream;
class SeekableAudioStream;
/**
* Create a new SeekableAudioStream from the MP3 data in the given stream.
* Allows for seeking (which is why we require a SeekableReadStream).
*
* @param stream the SeekableReadStream from which to read the MP3 data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occured
*/
SeekableAudioStream *makeMP3Stream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif // #ifdef USE_MAD
#endif // #ifndef SOUND_MP3_H

305
sound/decoders/raw.cpp Normal file
View file

@ -0,0 +1,305 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#include "common/endian.h"
#include "common/stream.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "sound/decoders/raw.h"
namespace Audio {
// This used to be an inline template function, but
// buggy template function handling in MSVC6 forced
// us to go with the macro approach. So far this is
// the only template function that MSVC6 seemed to
// compile incorrectly. Knock on wood.
#define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \
((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
#pragma mark -
#pragma mark --- RawStream ---
#pragma mark -
/**
* This is a stream, which allows for playing raw PCM data from a stream.
* It also features playback of multiple blocks from a given stream.
*/
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
class RawStream : public SeekableAudioStream {
// Allow backends to override buffer size
#ifdef CUSTOM_AUDIO_BUFFER_SIZE
static const int32 BUFFER_SIZE = CUSTOM_AUDIO_BUFFER_SIZE;
#else
static const int32 BUFFER_SIZE = 16384;
#endif
protected:
byte *_buffer; ///< Streaming buffer
const byte *_ptr; ///< Pointer to current position in stream buffer
const int _rate; ///< Sample rate of stream
Timestamp _playtime; ///< Calculated total play time
Common::SeekableReadStream *_stream; ///< Stream to read data from
int32 _filePos; ///< Current position in stream
int32 _diskLeft; ///< Samples left in stream in current block not yet read to buffer
int32 _bufferLeft; ///< Samples left in buffer in current block
const DisposeAfterUse::Flag _disposeAfterUse; ///< Indicates whether the stream object should be deleted when this RawStream is destructed
const RawStreamBlockList _blocks; ///< Audio block list
RawStreamBlockList::const_iterator _curBlock; ///< Current audio block number
public:
RawStream(int rate, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream, const RawStreamBlockList &blocks)
: _rate(rate), _playtime(0, rate), _stream(stream), _disposeAfterUse(disposeStream), _blocks(blocks), _curBlock(_blocks.begin()) {
assert(_blocks.size() > 0);
// Allocate streaming buffer
if (is16Bit)
_buffer = (byte *)malloc(BUFFER_SIZE * sizeof(int16));
else
_buffer = (byte *)malloc(BUFFER_SIZE * sizeof(byte));
_ptr = _buffer;
_bufferLeft = 0;
// Set current buffer state, playing first block
_filePos = _curBlock->pos;
_diskLeft = _curBlock->len;
// Add up length of all blocks in order to caluclate total play time
int32 len = 0;
for (RawStreamBlockList::const_iterator i = _blocks.begin(); i != _blocks.end(); ++i) {
assert(i->len % (stereo ? 2 : 1) == 0);
len += i->len;
}
_playtime = Timestamp(0, len / (stereo ? 2 : 1), rate);
}
virtual ~RawStream() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _stream;
free(_buffer);
}
int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const { return stereo; }
bool endOfData() const { return (_curBlock == _blocks.end()) && (_diskLeft == 0) && (_bufferLeft == 0); }
int getRate() const { return _rate; }
Timestamp getLength() const { return _playtime; }
bool seek(const Timestamp &where);
};
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
int RawStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
int oldPos = _stream->pos();
bool restoreFilePosition = false;
int samples = numSamples;
while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || _curBlock != _blocks.end())) {
// Output samples in the buffer to the output
int len = MIN<int>(samples, _bufferLeft);
samples -= len;
_bufferLeft -= len;
while (len > 0) {
*buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE);
_ptr += (is16Bit ? 2 : 1);
len--;
}
// Have we now finished this block? If so, read the next block
if ((_bufferLeft == 0) && (_diskLeft == 0) && _curBlock != _blocks.end()) {
// Next block
++_curBlock;
if (_curBlock != _blocks.end()) {
_filePos = _curBlock->pos;
_diskLeft = _curBlock->len;
}
}
// Now read more data from disk if there is more to be read
if (_bufferLeft == 0 && _diskLeft > 0) {
int32 readAmount = MIN(_diskLeft, BUFFER_SIZE);
// TODO: We should check for both seek and read to success.
// If that is not the case, we should probably stop the
// stream playback.
_stream->seek(_filePos, SEEK_SET);
_stream->read(_buffer, readAmount * (is16Bit? 2: 1));
// Amount of data in buffer is now the amount read in, and
// the amount left to read on disk is decreased by the same amount
_bufferLeft = readAmount;
_diskLeft -= readAmount;
_ptr = (byte *)_buffer;
_filePos += readAmount * (is16Bit ? 2 : 1);
// Set this flag now we've used the file, it restores it's
// original position.
restoreFilePosition = true;
}
}
// In case calling code relies on the position of this stream staying
// constant, I restore the location if I've changed it. This is probably
// not necessary.
if (restoreFilePosition)
_stream->seek(oldPos, SEEK_SET);
return numSamples - samples;
}
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
bool RawStream<stereo, is16Bit, isUnsigned, isLE>::seek(const Timestamp &where) {
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
uint32 curSample = 0;
// Search for the disk block in which the specific sample is placed
for (_curBlock = _blocks.begin(); _curBlock != _blocks.end(); ++_curBlock) {
uint32 nextBlockSample = curSample + _curBlock->len;
if (nextBlockSample > seekSample)
break;
curSample = nextBlockSample;
}
_filePos = 0;
_diskLeft = 0;
_bufferLeft = 0;
if (_curBlock == _blocks.end()) {
return ((seekSample - curSample) == (uint32)_curBlock->len);
} else {
const uint32 offset = seekSample - curSample;
_filePos = _curBlock->pos + offset * (is16Bit ? 2 : 1);
_diskLeft = _curBlock->len - offset;
return true;
}
}
#pragma mark -
#pragma mark --- Raw stream factories ---
#pragma mark -
/* In the following, we use preprocessor / macro tricks to simplify the code
* which instantiates the input streams. We used to use template functions for
* this, but MSVC6 / EVC 3-4 (used for WinCE builds) are extremely buggy when it
* comes to this feature of C++... so as a compromise we use macros to cut down
* on the (source) code duplication a bit.
* So while normally macro tricks are said to make maintenance harder, in this
* particular case it should actually help it :-)
*/
#define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \
if (is16Bit) { \
if (isLE) \
return new RawStream<STEREO, true, UNSIGNED, true>(rate, disposeAfterUse, stream, blockList); \
else \
return new RawStream<STEREO, true, UNSIGNED, false>(rate, disposeAfterUse, stream, blockList); \
} else \
return new RawStream<STEREO, false, UNSIGNED, false>(rate, disposeAfterUse, stream, blockList)
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
const RawStreamBlockList &blockList,
int rate,
byte flags,
DisposeAfterUse::Flag disposeAfterUse) {
const bool isStereo = (flags & Audio::FLAG_STEREO) != 0;
const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0;
const bool isUnsigned = (flags & Audio::FLAG_UNSIGNED) != 0;
const bool isLE = (flags & Audio::FLAG_LITTLE_ENDIAN) != 0;
if (blockList.empty()) {
warning("Empty block list passed to makeRawStream");
if (disposeAfterUse == DisposeAfterUse::YES)
delete stream;
return 0;
}
if (isStereo) {
if (isUnsigned) {
MAKE_LINEAR_DISK(true, true);
} else {
MAKE_LINEAR_DISK(true, false);
}
} else {
if (isUnsigned) {
MAKE_LINEAR_DISK(false, true);
} else {
MAKE_LINEAR_DISK(false, false);
}
}
}
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse) {
RawStreamBlockList blocks;
RawStreamBlock block;
block.pos = 0;
const bool isStereo = (flags & Audio::FLAG_STEREO) != 0;
const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0;
assert(stream->size() % ((is16Bit ? 2 : 1) * (isStereo ? 2 : 1)) == 0);
block.len = stream->size() / (is16Bit ? 2 : 1);
blocks.push_back(block);
return makeRawStream(stream, blocks, rate, flags, disposeAfterUse);
}
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse) {
return makeRawStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, flags, DisposeAfterUse::YES);
}
SeekableAudioStream *makeRawDiskStream_OLD(Common::SeekableReadStream *stream, RawStreamBlock *block, int numBlocks,
int rate, byte flags, DisposeAfterUse::Flag disposeStream) {
assert(numBlocks > 0);
RawStreamBlockList blocks;
for (int i = 0; i < numBlocks; ++i)
blocks.push_back(block[i]);
return makeRawStream(stream, blocks, rate, flags, disposeStream);
}
} // End of namespace Audio

153
sound/decoders/raw.h Normal file
View file

@ -0,0 +1,153 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#ifndef SOUND_RAW_H
#define SOUND_RAW_H
#include "common/sys.h"
#include "common/types.h"
#include "common/list.h"
namespace Common { class SeekableReadStream; }
namespace Audio {
class AudioStream;
class SeekableAudioStream;
/**
* Various flags which can be bit-ORed and then passed to
* makeRawMemoryStream and some other AudioStream factories
* to control their behavior.
*
* Engine authors are advised not to rely on a certain value or
* order of these flags (in particular, do not store them verbatim
* in savestates).
*/
enum RawFlags {
/** unsigned samples (default: signed) */
FLAG_UNSIGNED = 1 << 0,
/** sound is 16 bits wide (default: 8bit) */
FLAG_16BITS = 1 << 1,
/** samples are little endian (default: big endian) */
FLAG_LITTLE_ENDIAN = 1 << 2,
/** sound is in stereo (default: mono) */
FLAG_STEREO = 1 << 3
};
/**
* Struct used to define the audio data to be played by a RawStream.
*/
struct RawStreamBlock {
int32 pos; ///< Position in stream of the block (in bytes of course!)
int32 len; ///< Length of the block (in raw samples, not sample pairs!)
};
/**
* List containing all blocks of a raw stream.
* @see RawStreamBlock
*/
typedef Common::List<RawStreamBlock> RawStreamBlockList;
/**
* Creates an audio stream, which plays from the given buffer.
*
* @param buffer Buffer to play from.
* @param size Size of the buffer in bytes.
* @param rate Rate of the sound data.
* @param flags Audio flags combination.
* @see RawFlags
* @param disposeAfterUse Whether to free the buffer after use (with free!).
* @return The new SeekableAudioStream (or 0 on failure).
*/
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
/**
* Creates an audio stream, which plays from the given stream.
*
* @param stream Stream object to play from.
* @param rate Rate of the sound data.
* @param flags Audio flags combination.
* @see RawFlags
* @param disposeAfterUse Whether to delete the stream after use.
* @return The new SeekableAudioStream (or 0 on failure).
*/
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
int rate, byte flags,
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
/**
* Creates an audio stream, which plays from the given stream.
*
* @param stream Stream object to play from.
* @param blockList List of blocks to play.
* @see RawDiskStreamAudioBlock
* @see RawStreamBlockList
* @param rate Rate of the sound data.
* @param flags Audio flags combination.
* @see RawFlags
* @param disposeAfterUse Whether to delete the stream after use.
* @return The new SeekableAudioStream (or 0 on failure).
*/
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
const RawStreamBlockList &blockList,
int rate,
byte flags,
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
/**
* NOTE:
* This API is considered deprecated.
*
* Creates a audio stream, which plays from given stream.
*
* @param stream Stream to play from
* @param block Pointer to an RawStreamBlock array
* @see RawStreamBlock
* @param numBlocks Number of blocks.
* @param rate The rate
* @param flags Flags combination.
* @see RawFlags
* @param disposeStream Whether the "stream" object should be destroyed after playback.
* @return The new SeekableAudioStream (or 0 on failure).
*/
SeekableAudioStream *makeRawDiskStream_OLD(Common::SeekableReadStream *stream,
RawStreamBlock *block, int numBlocks,
int rate, byte flags,
DisposeAfterUse::Flag disposeStream);
} // End of namespace Audio
#endif

258
sound/decoders/vorbis.cpp Normal file
View file

@ -0,0 +1,258 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
#include "sound/decoders/vorbis.h"
#ifdef USE_VORBIS
#include "common/debug.h"
#include "common/stream.h"
#include "common/util.h"
#include "sound/audiostream.h"
#include "sound/audiocd.h"
#ifdef USE_TREMOR
#ifdef __GP32__ // GP32 uses custom libtremor
#include <ivorbisfile.h>
#else
#include <tremor/ivorbisfile.h>
#endif
#else
#include <vorbis/vorbisfile.h>
#endif
namespace Audio {
// These are wrapper functions to allow using a SeekableReadStream object to
// provide data to the OggVorbis_File object.
static size_t read_stream_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
uint32 result = stream->read(ptr, size * nmemb);
return result / size;
}
static int seek_stream_wrap(void *datasource, ogg_int64_t offset, int whence) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
stream->seek((int32)offset, whence);
return stream->pos();
}
static int close_stream_wrap(void *datasource) {
// Do nothing -- we leave it up to the VorbisStream to free memory as appropriate.
return 0;
}
static long tell_stream_wrap(void *datasource) {
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
return stream->pos();
}
static ov_callbacks g_stream_wrap = {
read_stream_wrap, seek_stream_wrap, close_stream_wrap, tell_stream_wrap
};
#pragma mark -
#pragma mark --- Ogg Vorbis stream ---
#pragma mark -
class VorbisStream : public SeekableAudioStream {
protected:
Common::SeekableReadStream *_inStream;
DisposeAfterUse::Flag _disposeAfterUse;
bool _isStereo;
int _rate;
Timestamp _length;
OggVorbis_File _ovFile;
int16 _buffer[4096];
const int16 *_bufferEnd;
const int16 *_pos;
public:
// startTime / duration are in milliseconds
VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
~VorbisStream();
int readBuffer(int16 *buffer, const int numSamples);
bool endOfData() const { return _pos >= _bufferEnd; }
bool isStereo() const { return _isStereo; }
int getRate() const { return _rate; }
bool seek(const Timestamp &where);
Timestamp getLength() const { return _length; }
protected:
bool refill();
};
VorbisStream::VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
_inStream(inStream),
_disposeAfterUse(dispose),
_length(0, 1000),
_bufferEnd(_buffer + ARRAYSIZE(_buffer)) {
int res = ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap);
if (res < 0) {
warning("Could not create Vorbis stream (%d)", res);
_pos = _bufferEnd;
return;
}
// Read in initial data
if (!refill())
return;
// Setup some header information
_isStereo = ov_info(&_ovFile, -1)->channels >= 2;
_rate = ov_info(&_ovFile, -1)->rate;
#ifdef USE_TREMOR
_length = Timestamp(ov_time_total(&_ovFile, -1), getRate());
#else
_length = Timestamp(uint32(ov_time_total(&_ovFile, -1) * 1000.0), getRate());
#endif
}
VorbisStream::~VorbisStream() {
ov_clear(&_ovFile);
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _inStream;
}
int VorbisStream::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
while (samples < numSamples && _pos < _bufferEnd) {
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
memcpy(buffer, _pos, len * 2);
buffer += len;
_pos += len;
samples += len;
if (_pos >= _bufferEnd) {
if (!refill())
break;
}
}
return samples;
}
bool VorbisStream::seek(const Timestamp &where) {
// Vorbisfile uses the sample pair number, thus we always use "false" for the isStereo parameter
// of the convertTimeToStreamPos helper.
int res = ov_pcm_seek(&_ovFile, convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
if (res) {
warning("Error seeking in Vorbis stream (%d)", res);
_pos = _bufferEnd;
return false;
}
return refill();
}
bool VorbisStream::refill() {
// Read the samples
uint len_left = sizeof(_buffer);
char *read_pos = (char *)_buffer;
while (len_left > 0) {
long result;
#ifdef USE_TREMOR
// Tremor ov_read() always returns data as signed 16 bit interleaved PCM
// in host byte order. As such, it does not take arguments to request
// specific signedness, byte order or bit depth as in Vorbisfile.
result = ov_read(&_ovFile, read_pos, len_left,
NULL);
#else
#ifdef SCUMM_BIG_ENDIAN
result = ov_read(&_ovFile, read_pos, len_left,
1,
2, // 16 bit
1, // signed
NULL);
#else
result = ov_read(&_ovFile, read_pos, len_left,
0,
2, // 16 bit
1, // signed
NULL);
#endif
#endif
if (result == OV_HOLE) {
// Possibly recoverable, just warn about it
warning("Corrupted data in Vorbis file");
} else if (result == 0) {
//warning("End of file while reading from Vorbis file");
//_pos = _bufferEnd;
//return false;
break;
} else if (result < 0) {
warning("Error reading from Vorbis stream (%d)", int(result));
_pos = _bufferEnd;
// Don't delete it yet, that causes problems in
// the CD player emulation code.
return false;
} else {
len_left -= result;
read_pos += result;
}
}
_pos = _buffer;
_bufferEnd = (int16 *)read_pos;
return true;
}
#pragma mark -
#pragma mark --- Ogg Vorbis factory functions ---
#pragma mark -
SeekableAudioStream *makeVorbisStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new VorbisStream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return 0;
} else {
return s;
}
}
} // End of namespace Audio
#endif // #ifdef USE_VORBIS

73
sound/decoders/vorbis.h Normal file
View file

@ -0,0 +1,73 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
/**
* @file
* Sound decoder used in engines:
* - agos
* - kyra
* - m4
* - queen
* - saga
* - scumm
* - sword1
* - sword2
* - touche
* - tucker
*/
#ifndef SOUND_VORBIS_H
#define SOUND_VORBIS_H
#include "common/types.h"
#include "common/sys.h"
#ifdef USE_VORBIS
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class AudioStream;
class SeekableAudioStream;
/**
* Create a new SeekableAudioStream from the Ogg Vorbis data in the given stream.
* Allows for seeking (which is why we require a SeekableReadStream).
*
* @param stream the SeekableReadStream from which to read the Ogg Vorbis data
* @param disposeAfterUse whether to delete the stream after use
* @return a new SeekableAudioStream, or NULL, if an error occured
*/
SeekableAudioStream *makeVorbisStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Audio
#endif // #ifdef USE_VORBIS
#endif // #ifndef SOUND_VORBIS_H

469
sound/rate_arm.cpp Normal file
View file

@ -0,0 +1,469 @@
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
* $URL$
* $Id$
*
*/
/*
* The code in this file, together with the rate_arm_asm.s file offers
* an ARM optimised version of the code in rate.cpp. The operation of this
* code should be identical to that of rate.cpp, but faster. The heavy
* lifting is done in the assembler file.
*
* To be as portable as possible we implement the core routines with C
* linkage in assembly, and implement the C++ routines that call into
* the C here. The C++ symbol mangling varies wildly between compilers,
* so this is the simplest way to ensure that the C/C++ combination should
* work on as many ARM based platforms as possible.
*
* Essentially the algorithm herein is the same as that in rate.cpp, so
* anyone seeking to understand this should attempt to understand that
* first. That code was based in turn on code with Copyright 1998 Fabrice
* Bellard - part of SoX (http://sox.sourceforge.net).
* Max Horn adapted that code to the needs of ScummVM and partially rewrote
* it, in the process removing any use of floating point arithmetic. Various
* other improvments over the original code were made.
*/
#include "sound/audiostream.h"
#include "sound/rate.h"
#include "sound/mixer.h"
#include "common/util.h"
//#define DEBUG_RATECONV
namespace Audio {
/**
* The precision of the fractional computations used by the rate converter.
* Normally you should never have to modify this value.
* This stuff is defined in common/frac.h, but we redefine it here as the
* ARM routine we call doesn't respect those definitions.
*/
#define FRAC_BITS 16
#define FRAC_ONE (1<<FRAC_BITS)
/**
* The size of the intermediate input cache. Bigger values may increase
* performance, but only until some point (depends largely on cache size,
* target processor and various other factors), at which it will decrease
* again.
*/
#define INTERMEDIATE_BUFFER_SIZE 512
/**
* Audio rate converter based on simple resampling. Used when no
* interpolation is required.
*
* Limited to sampling frequency <= 65535 Hz.
*/
typedef struct {
const st_sample_t *inPtr;
int inLen;
/** position of how far output is ahead of input */
/** Holds what would have been opos-ipos */
long opos;
/** fractional position increment in the output stream */
long opos_inc;
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
} SimpleRateDetails;
template<bool stereo, bool reverseStereo>
class SimpleRateConverter : public RateConverter {
protected:
SimpleRateDetails sr;
public:
SimpleRateConverter(st_rate_t inrate, st_rate_t outrate);
int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return (ST_SUCCESS);
}
};
/*
* Prepare processing.
*/
template<bool stereo, bool reverseStereo>
SimpleRateConverter<stereo, reverseStereo>::SimpleRateConverter(st_rate_t inrate, st_rate_t outrate) {
if (inrate == outrate) {
error("Input and Output rates must be different to use rate effect");
}
if ((inrate % outrate) != 0) {
error("Input rate must be a multiple of Output rate to use rate effect");
}
if (inrate >= 65536 || outrate >= 65536) {
error("rate effect can only handle rates < 65536");
}
sr.opos = 1;
/* increment */
sr.opos_inc = inrate / outrate;
sr.inLen = 0;
}
extern "C" {
#ifndef IPHONE
#define ARM_SimpleRate_M _ARM_SimpleRate_M
#define ARM_SimpleRate_S _ARM_SimpleRate_S
#define ARM_SimpleRate_R _ARM_SimpleRate_R
#endif
}
extern "C" st_sample_t *ARM_SimpleRate_M(
AudioStream &input,
int (*fn)(Audio::AudioStream&,int16*,int),
SimpleRateDetails *sr,
st_sample_t *obuf,
st_size_t osamp,
st_volume_t vol_l,
st_volume_t vol_r);
extern "C" st_sample_t *ARM_SimpleRate_S(
AudioStream &input,
int (*fn)(Audio::AudioStream&,int16*,int),
SimpleRateDetails *sr,
st_sample_t *obuf,
st_size_t osamp,
st_volume_t vol_l,
st_volume_t vol_r);
extern "C" st_sample_t *ARM_SimpleRate_R(
AudioStream &input,
int (*fn)(Audio::AudioStream&,int16*,int),
SimpleRateDetails *sr,
st_sample_t *obuf,
st_size_t osamp,
st_volume_t vol_l,
st_volume_t vol_r);
extern "C" int SimpleRate_readFudge(Audio::AudioStream &input,
int16 *a, int b)
{
#ifdef DEBUG_RATECONV
fprintf(stderr, "Reading ptr=%x n%d\n", a, b);
fflush(stderr);
#endif
return input.readBuffer(a, b);
}
template<bool stereo, bool reverseStereo>
int SimpleRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
#ifdef DEBUG_RATECONV
fprintf(stderr, "Simple st=%d rev=%d\n", stereo, reverseStereo);
fflush(stderr);
#endif
st_sample_t *ostart = obuf;
if (!stereo) {
obuf = ARM_SimpleRate_M(input,
&SimpleRate_readFudge,
&sr,
obuf, osamp, vol_l, vol_r);
} else if (reverseStereo) {
obuf = ARM_SimpleRate_R(input,
&SimpleRate_readFudge,
&sr,
obuf, osamp, vol_l, vol_r);
} else {
obuf = ARM_SimpleRate_S(input,
&SimpleRate_readFudge,
&sr,
obuf, osamp, vol_l, vol_r);
}
return (obuf-ostart)/2;
}
/**
* Audio rate converter based on simple linear Interpolation.
*
* The use of fractional increment allows us to use no buffer. It
* avoid the problems at the end of the buffer we had with the old
* method which stored a possibly big buffer of size
* lcm(in_rate,out_rate).
*
* Limited to sampling frequency <= 65535 Hz.
*/
typedef struct {
const st_sample_t *inPtr;
int inLen;
/** position of how far output is ahead of input */
/** Holds what would have been opos-ipos<<16 + opos_frac */
long opos;
/** integer position increment in the output stream */
long opos_inc;
/** current sample(s) in the input stream (left/right channel) */
st_sample_t icur[2];
/** last sample(s) in the input stream (left/right channel) */
/** Note, these are deliberately ints, not st_sample_t's */
int32 ilast[2];
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
} LinearRateDetails;
extern "C" {
#ifndef IPHONE
#define ARM_LinearRate_M _ARM_LinearRate_M
#define ARM_LinearRate_S _ARM_LinearRate_S
#define ARM_LinearRate_R _ARM_LinearRate_R
#endif
}
extern "C" st_sample_t *ARM_LinearRate_M(
AudioStream &input,
int (*fn)(Audio::AudioStream&,int16*,int),
LinearRateDetails *lr,
st_sample_t *obuf,
st_size_t osamp,
st_volume_t vol_l,
st_volume_t vol_r);
extern "C" st_sample_t *ARM_LinearRate_S(
AudioStream &input,
int (*fn)(Audio::AudioStream&,int16*,int),
LinearRateDetails *lr,
st_sample_t *obuf,
st_size_t osamp,
st_volume_t vol_l,
st_volume_t vol_r);
extern "C" st_sample_t *ARM_LinearRate_R(
AudioStream &input,
int (*fn)(Audio::AudioStream&,int16*,int),
LinearRateDetails *lr,
st_sample_t *obuf,
st_size_t osamp,
st_volume_t vol_l,
st_volume_t vol_r);
template<bool stereo, bool reverseStereo>
class LinearRateConverter : public RateConverter {
protected:
LinearRateDetails lr;
public:
LinearRateConverter(st_rate_t inrate, st_rate_t outrate);
int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return (ST_SUCCESS);
}
};
/*
* Prepare processing.
*/
template<bool stereo, bool reverseStereo>
LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
unsigned long incr;
if (inrate == outrate) {
error("Input and Output rates must be different to use rate effect");
}
if (inrate >= 65536 || outrate >= 65536) {
error("rate effect can only handle rates < 65536");
}
lr.opos = FRAC_ONE;
/* increment */
incr = (inrate << FRAC_BITS) / outrate;
lr.opos_inc = incr;
lr.ilast[0] = lr.ilast[1] = 32768;
lr.icur[0] = lr.icur[1] = 0;
lr.inLen = 0;
}
/*
* Processed signed long samples from ibuf to obuf.
* Return number of sample pairs processed.
*/
template<bool stereo, bool reverseStereo>
int LinearRateConverter<stereo, reverseStereo>::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
#ifdef DEBUG_RATECONV
fprintf(stderr, "Linear st=%d rev=%d\n", stereo, reverseStereo);
fflush(stderr);
#endif
st_sample_t *ostart = obuf;
if (!stereo) {
obuf = ARM_LinearRate_M(input,
&SimpleRate_readFudge,
&lr,
obuf, osamp, vol_l, vol_r);
} else if (reverseStereo) {
obuf = ARM_LinearRate_R(input,
&SimpleRate_readFudge,
&lr,
obuf, osamp, vol_l, vol_r);
} else {
obuf = ARM_LinearRate_S(input,
&SimpleRate_readFudge,
&lr,
obuf, osamp, vol_l, vol_r);
}
return (obuf-ostart)/2;
}
#pragma mark -
/**
* Simple audio rate converter for the case that the inrate equals the outrate.
*/
extern "C" {
#ifndef IPHONE
#define ARM_CopyRate_M _ARM_CopyRate_M
#define ARM_CopyRate_S _ARM_CopyRate_S
#define ARM_CopyRate_R _ARM_CopyRate_R
#endif
}
extern "C" st_sample_t *ARM_CopyRate_M(
st_size_t len,
st_sample_t *obuf,
st_volume_t vol_l,
st_volume_t vol_r,
st_sample_t *_buffer);
extern "C" st_sample_t *ARM_CopyRate_S(
st_size_t len,
st_sample_t *obuf,
st_volume_t vol_l,
st_volume_t vol_r,
st_sample_t *_buffer);
extern "C" st_sample_t *ARM_CopyRate_R(
st_size_t len,
st_sample_t *obuf,
st_volume_t vol_l,
st_volume_t vol_r,
st_sample_t *_buffer);
template<bool stereo, bool reverseStereo>
class CopyRateConverter : public RateConverter {
st_sample_t *_buffer;
st_size_t _bufferSize;
public:
CopyRateConverter() : _buffer(0), _bufferSize(0) {}
~CopyRateConverter() {
free(_buffer);
}
virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
assert(input.isStereo() == stereo);
#ifdef DEBUG_RATECONV
fprintf(stderr, "Copy st=%d rev=%d\n", stereo, reverseStereo);
fflush(stderr);
#endif
st_size_t len;
st_sample_t *ostart = obuf;
if (stereo)
osamp *= 2;
// Reallocate temp buffer, if necessary
if (osamp > _bufferSize) {
free(_buffer);
_buffer = (st_sample_t *)malloc(osamp * 2);
_bufferSize = osamp;
}
// Read up to 'osamp' samples into our temporary buffer
len = input.readBuffer(_buffer, osamp);
if (len <= 0)
return 0;
// Mix the data into the output buffer
if (stereo && reverseStereo)
obuf = ARM_CopyRate_R(len, obuf, vol_l, vol_r, _buffer);
else if (stereo)
obuf = ARM_CopyRate_S(len, obuf, vol_l, vol_r, _buffer);
else
obuf = ARM_CopyRate_M(len, obuf, vol_l, vol_r, _buffer);
return (obuf-ostart)/2;
}
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
return (ST_SUCCESS);
}
};
#pragma mark -
/**
* Create and return a RateConverter object for the specified input and output rates.
*/
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) {
if (inrate != outrate) {
if ((inrate % outrate) == 0) {
if (stereo) {
if (reverseStereo)
return new SimpleRateConverter<true, true>(inrate, outrate);
else
return new SimpleRateConverter<true, false>(inrate, outrate);
} else
return new SimpleRateConverter<false, false>(inrate, outrate);
} else {
if (stereo) {
if (reverseStereo)
return new LinearRateConverter<true, true>(inrate, outrate);
else
return new LinearRateConverter<true, false>(inrate, outrate);
} else
return new LinearRateConverter<false, false>(inrate, outrate);
}
} else {
if (stereo) {
if (reverseStereo)
return new CopyRateConverter<true, true>();
else
return new CopyRateConverter<true, false>();
} else
return new CopyRateConverter<false, false>();
}
}
} // End of namespace Audio

717
sound/rate_arm_asm.s Normal file
View file

@ -0,0 +1,717 @@
@ Residual - A 3D game interpreter
@
@ Residual is the legal property of its developers, whose names
@ are too numerous to list here. Please refer to the AUTHORS
@ 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.
@
@ $URL$
@ $Id$
@
@ @author Robin Watts (robin@wss.co.uk)
@
@ This file, together with rate_arm.cpp, provides an ARM optimised version
@ of rate.cpp. The algorithm is essentially the same as that within rate.cpp
@ so to understand this file you should understand rate.cpp first.
.text
.global _ARM_CopyRate_M
.global _ARM_CopyRate_S
.global _ARM_CopyRate_R
.global _ARM_SimpleRate_M
.global _ARM_SimpleRate_S
.global _ARM_SimpleRate_R
.global _ARM_LinearRate_M
.global _ARM_LinearRate_S
.global _ARM_LinearRate_R
_ARM_CopyRate_M:
@ r0 = len
@ r1 = obuf
@ r2 = vol_l
@ r3 = vol_r
@ <> = ptr
LDR r12,[r13]
STMFD r13!,{r4-r7,r14}
MOV r14,#0 @ r14= 0
ORR r2, r2, r2, LSL #8 @ r2 = vol_l as 16 bits
ORR r3, r3, r3, LSL #8 @ r3 = vol_r as 16 bits
CopyRate_M_loop:
LDRSH r5, [r12], #2 @ r5 = tmp0 = tmp1 = *ptr++
LDRSH r6, [r1] @ r6 = obuf[0]
LDRSH r7, [r1, #2] @ r7 = obuf[1]
MUL r4, r2, r5 @ r4 = tmp0*vol_l
MUL r5, r3, r5 @ r5 = tmp1*vol_r
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
RSCVS r6, r14,#0x80000000 @ Clamp r6
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
RSCVS r7, r14,#0x80000000 @ Clamp r7
MOV r6, r6, LSR #16 @ Shift back to halfword
MOV r7, r7, LSR #16 @ Shift back to halfword
STRH r6, [r1], #2 @ Store output value
STRH r7, [r1], #2 @ Store output value
SUBS r0,r0,#1 @ len--
BGT CopyRate_M_loop @ and loop
MOV r0, r1 @ return obuf
LDMFD r13!,{r4-r7,PC}
_ARM_CopyRate_S:
@ r0 = len
@ r1 = obuf
@ r2 = vol_l
@ r3 = vol_r
@ <> = ptr
LDR r12,[r13]
STMFD r13!,{r4-r7,r14}
MOV r14,#0 @ r14= 0
ORR r2, r2, r2, LSL #8 @ r2 = vol_l as 16 bits
ORR r3, r3, r3, LSL #8 @ r3 = vol_r as 16 bits
CopyRate_S_loop:
LDRSH r4, [r12],#2 @ r4 = tmp0 = *ptr++
LDRSH r5, [r12],#2 @ r5 = tmp1 = *ptr++
LDRSH r6, [r1] @ r6 = obuf[0]
LDRSH r7, [r1,#2] @ r7 = obuf[1]
MUL r4, r2, r4 @ r5 = tmp0*vol_l
MUL r5, r3, r5 @ r6 = tmp1*vol_r
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
RSCVS r6, r14,#0x80000000 @ Clamp r6
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
RSCVS r7, r14,#0x80000000 @ Clamp r7
MOV r6, r6, LSR #16 @ Shift back to halfword
MOV r7, r7, LSR #16 @ Shift back to halfword
STRH r6, [r1],#2 @ Store output value
STRH r7, [r1],#2 @ Store output value
SUBS r0,r0,#2 @ len -= 2
BGT CopyRate_S_loop @ and loop
MOV r0, r1 @ return obuf
LDMFD r13!,{r4-r7,PC}
_ARM_CopyRate_R:
@ r0 = len
@ r1 = obuf
@ r2 = vol_l
@ r3 = vol_r
@ <> = ptr
LDR r12,[r13]
STMFD r13!,{r4-r7,r14}
MOV r14,#0 @ r14= 0
ORR r2, r2, r2, LSL #8 @ r2 = vol_l as 16 bits
ORR r3, r3, r3, LSL #8 @ r3 = vol_r as 16 bits
CopyRate_R_loop:
LDRSH r5, [r12],#2 @ r5 = tmp1 = *ptr++
LDRSH r4, [r12],#2 @ r4 = tmp0 = *ptr++
LDRSH r6, [r1] @ r6 = obuf[0]
LDRSH r7, [r1,#2] @ r7 = obuf[1]
MUL r4, r2, r4 @ r4 = tmp0*vol_l
MUL r5, r3, r5 @ r5 = tmp1*vol_r
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
RSCVS r6, r14,#0x80000000 @ Clamp r6
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
RSCVS r7, r14,#0x80000000 @ Clamp r7
MOV r6, r6, LSR #16 @ Shift back to halfword
MOV r7, r7, LSR #16 @ Shift back to halfword
STRH r6, [r1],#2 @ Store output value
STRH r7, [r1],#2 @ Store output value
SUBS r0,r0,#2 @ len -= 2
BGT CopyRate_R_loop @ and loop
MOV r0, r1 @ return obuf
LDMFD r13!,{r4-r7,PC}
_ARM_SimpleRate_M:
@ r0 = AudioStream &input
@ r1 = input.readBuffer
@ r2 = input->sr
@ r3 = obuf
@ <> = osamp
@ <> = vol_l
@ <> = vol_r
MOV r12,r13
STMFD r13!,{r0-r2,r4-r8,r10-r11,r14}
LDMFD r12,{r11,r12,r14} @ r11= osamp
@ r12= vol_l
@ r14= vol_r
LDMIA r2,{r0,r1,r2,r8} @ r0 = inPtr
@ r1 = inLen
@ r2 = opos
@ r8 = opos_inc
CMP r11,#0 @ if (osamp <= 0)
BLE SimpleRate_M_end @ bale
MOV r10,#0
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
SimpleRate_M_loop:
SUBS r1, r1, #1 @ r1 = inLen -= 1
BLT SimpleRate_M_read
SUBS r2, r2, #1 @ r2 = opos--
ADDGE r0, r0, #2 @ if (r2 >= 0) { sr.inPtr++
BGE SimpleRate_M_loop @ and loop }
SimpleRate_M_read_return:
LDRSH r5, [r0],#2 @ r5 = tmp1 = *inPtr++
LDRSH r6, [r3] @ r6 = obuf[0]
LDRSH r7, [r3,#2] @ r7 = obuf[1]
ADD r2, r2, r8 @ r2 = opos += opos_inc
MUL r4, r12,r5 @ r4 = tmp0*vol_l
MUL r5, r14,r5 @ r5 = tmp1*vol_r
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
RSCVS r6, r10,#0x80000000 @ Clamp r6
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
RSCVS r7, r10,#0x80000000 @ Clamp r7
MOV r6, r6, LSR #16 @ Shift back to halfword
MOV r7, r7, LSR #16 @ Shift back to halfword
STRH r6, [r3],#2 @ Store output value
STRH r7, [r3],#2 @ Store output value
SUBS r11,r11,#1 @ len--
BGT SimpleRate_M_loop @ and loop
SimpleRate_M_end:
LDR r14,[r13,#8] @ r14 = sr
ADD r13,r13,#12 @ Skip over r0-r2 on stack
STMIA r14,{r0,r1,r2} @ Store back updated values
MOV r0, r3 @ return obuf
LDMFD r13!,{r4-r8,r10-r11,PC}
SimpleRate_M_read:
LDR r0, [r13,#8] @ r0 = sr (8 = 4*2)
ADD r0, r0, #16 @ r0 = inPtr = inBuf
.ifdef PALMOS_MODE
LDR r10,[r13,#4*8] @ restore r10
.endif
STMFD r13!,{r0,r2-r3,r12,r14}
MOV r1, r0 @ r1 = inBuf
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
@ Calling back into C++ here. WinCE is fairly easy about such things
@ but other OS are more awkward. r9 is preserved for Symbian, and
@ we have 3+8+5 = 16 things on the stack (an even number).
MOV r14,PC
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
SUBS r1, r0, #1 @ r1 = inLen-1
LDMFD r13!,{r0,r2-r3,r12,r14}
BLT SimpleRate_M_end
.ifdef PALMOS_MODE
MOV r10,#0
.endif
SUBS r2, r2, #1 @ r2 = opos--
ADDGE r0, r0, #2 @ if (r2 >= 0) { sr.inPtr++
BGE SimpleRate_M_loop @ and loop }
B SimpleRate_M_read_return
_ARM_SimpleRate_S:
@ r0 = AudioStream &input
@ r1 = input.readBuffer
@ r2 = input->sr
@ r3 = obuf
@ <> = osamp
@ <> = vol_l
@ <> = vol_r
MOV r12,r13
STMFD r13!,{r0-r2,r4-r8,r10-r11,r14}
LDMFD r12,{r11,r12,r14} @ r11= osamp
@ r12= vol_l
@ r14= vol_r
LDMIA r2,{r0,r1,r2,r8} @ r0 = inPtr
@ r1 = inLen
@ r2 = opos
@ r8 = opos_inc
CMP r11,#0 @ if (osamp <= 0)
BLE SimpleRate_S_end @ bale
MOV r10,#0
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
SimpleRate_S_loop:
SUBS r1, r1, #2 @ r1 = inLen -= 2
BLT SimpleRate_S_read
SUBS r2, r2, #1 @ r2 = opos--
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
BGE SimpleRate_S_loop @ and loop }
SimpleRate_S_read_return:
LDRSH r4, [r0],#2 @ r4 = tmp0 = *inPtr++
LDRSH r5, [r0],#2 @ r5 = tmp1 = *inPtr++
LDRSH r6, [r3] @ r6 = obuf[0]
LDRSH r7, [r3,#2] @ r7 = obuf[1]
ADD r2, r2, r8 @ r2 = opos += opos_inc
MUL r4, r12,r4 @ r5 = tmp0*vol_l
MUL r5, r14,r5 @ r6 = tmp1*vol_r
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
RSCVS r6, r10,#0x80000000 @ Clamp r6
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
RSCVS r7, r10,#0x80000000 @ Clamp r7
MOV r6, r6, LSR #16 @ Shift back to halfword
MOV r7, r7, LSR #16 @ Shift back to halfword
STRH r6, [r3],#2 @ Store output value
STRH r7, [r3],#2 @ Store output value
SUBS r11,r11,#1 @ osamp--
BGT SimpleRate_S_loop @ and loop
SimpleRate_S_end:
LDR r14,[r13,#8] @ r14 = sr
ADD r13,r13,#12 @ skip over r0-r2 on stack
STMIA r14,{r0,r1,r2} @ store back updated values
MOV r0, r3 @ return obuf
LDMFD r13!,{r4-r8,r10-r11,PC}
SimpleRate_S_read:
LDR r0, [r13,#8] @ r0 = sr (8 = 4*2)
ADD r0, r0, #16 @ r0 = inPtr = inBuf
.ifdef PALMOS_MODE
LDR r10,[r13,#4*8] @ restore r10
.endif
STMFD r13!,{r0,r2-r3,r12,r14}
MOV r1, r0 @ r1 = inBuf
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
@ Calling back into C++ here. WinCE is fairly easy about such things
@ but other OS are more awkward. r9 is preserved for Symbian, and
@ we have 3+8+5 = 16 things on the stack (an even number).
MOV r14,PC
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
SUBS r1, r0, #2 @ r1 = inLen-2
LDMFD r13!,{r0,r2-r3,r12,r14}
BLT SimpleRate_S_end
.ifdef PALMOS_MODE
MOV r10,#0
.endif
SUBS r2, r2, #1 @ r2 = opos--
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
BGE SimpleRate_S_loop @ and loop }
B SimpleRate_S_read_return
_ARM_SimpleRate_R:
@ r0 = AudioStream &input
@ r1 = input.readBuffer
@ r2 = input->sr
@ r3 = obuf
@ <> = osamp
@ <> = vol_l
@ <> = vol_r
MOV r12,r13
STMFD r13!,{r0-r2,r4-r8,r10-r11,r14}
LDMFD r12,{r11,r12,r14} @ r11= osamp
@ r12= vol_l
@ r14= vol_r
LDMIA r2,{r0,r1,r2,r8} @ r0 = inPtr
@ r1 = inLen
@ r2 = opos
@ r8 = opos_inc
CMP r11,#0 @ if (osamp <= 0)
BLE SimpleRate_R_end @ bale
MOV r10,#0
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
SimpleRate_R_loop:
SUBS r1, r1, #2 @ r1 = inLen -= 2
BLT SimpleRate_R_read
SUBS r2, r2, #1 @ r2 = opos--
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
BGE SimpleRate_R_loop @ and loop }
SimpleRate_R_read_return:
LDRSH r5, [r0],#2 @ r5 = tmp0 = *inPtr++
LDRSH r4, [r0],#2 @ r4 = tmp1 = *inPtr++
LDRSH r6, [r3] @ r6 = obuf[0]
LDRSH r7, [r3,#2] @ r7 = obuf[1]
ADD r2, r2, r8 @ r2 = opos += opos_inc
MUL r4, r12,r4 @ r5 = tmp0*vol_l
MUL r5, r14,r5 @ r6 = tmp1*vol_r
ADDS r6, r4, r6, LSL #16 @ r6 = obuf[0]<<16 + tmp0*vol_l
RSCVS r6, r10,#0x80000000 @ Clamp r6
ADDS r7, r5, r7, LSL #16 @ r7 = obuf[1]<<16 + tmp1*vol_r
RSCVS r7, r10,#0x80000000 @ Clamp r7
MOV r6, r6, LSR #16 @ Shift back to halfword
MOV r7, r7, LSR #16 @ Shift back to halfword
STRH r6, [r3],#2 @ Store output value
STRH r7, [r3],#2 @ Store output value
SUBS r11,r11,#1 @ osamp--
BGT SimpleRate_R_loop @ and loop
SimpleRate_R_end:
LDR r14,[r13,#8] @ r14 = sr
ADD r13,r13,#12 @ Skip over r0-r2 on stack
STMIA r14,{r0,r1,r2} @ Store back updated values
MOV r0, r3 @ return obuf
LDMFD r13!,{r4-r8,r10-r11,PC}
SimpleRate_R_read:
LDR r0, [r13,#8] @ r0 = sr (8 = 4*2)
ADD r0, r0, #16 @ r0 = inPtr = inBuf
.ifdef PALMOS_MODE
LDR r10,[r13,#4*8] @ restore r10
.endif
STMFD r13!,{r0,r2-r3,r12,r14}
MOV r1, r0 @ r1 = inBuf
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
@ Calling back into C++ here. WinCE is fairly easy about such things
@ but other OS are more awkward. r9 is preserved for Symbian, and
@ we have 3+8+5 = 16 things on the stack (an even number).
MOV r14,PC
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
SUBS r1, r0, #2 @ r1 = inLen-2
LDMFD r13!,{r0,r2-r3,r12,r14}
BLT SimpleRate_R_end
.ifdef PALMOS_MODE
MOV r10,#0
.endif
SUBS r2, r2, #1 @ r2 = opos--
ADDGE r0, r0, #4 @ if (r2 >= 0) { sr.inPtr += 2
BGE SimpleRate_R_loop @ and loop }
B SimpleRate_R_read_return
_ARM_LinearRate_M:
@ r0 = AudioStream &input
@ r1 = input.readBuffer
@ r2 = input->sr
@ r3 = obuf
@ <> = osamp
@ <> = vol_l
@ <> = vol_r
MOV r12,r13
STMFD r13!,{r0-r1,r4-r11,r14}
LDMFD r12,{r11,r12,r14} @ r11= osamp
@ r12= vol_l
@ r14= vol_r
LDMIA r2,{r0,r1,r8} @ r0 = inPtr
@ r1 = inLen
@ r8 = opos
MOV r10,#0
CMP r11,#0 @ if (osamp <= 0)
BLE LinearRate_M_end @ bale
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
CMP r1,#0
BGT LinearRate_M_part2
@ part1 - read input samples
LinearRate_M_loop:
SUBS r1, r1, #1 @ r1 = inLen -= 1
BLT LinearRate_M_read
LinearRate_M_read_return:
LDRH r4, [r2, #16] @ r4 = icur[0]
LDRSH r5, [r0],#2 @ r5 = tmp1 = *inPtr++
SUBS r8, r8, #65536 @ r8 = opos--
STRH r4, [r2,#22] @ ilast[0] = icur[0]
STRH r5, [r2,#16] @ icur[0] = tmp1
BGE LinearRate_M_loop
@ part2 - form output samples
LinearRate_M_part2:
@ We are guaranteed that opos < 0 here
LDR r6, [r2,#20] @ r6 = ilast[0]<<16 + 32768
LDRSH r5, [r2,#16] @ r5 = icur[0]
MOV r4, r8, LSL #16
MOV r4, r4, LSR #16
SUB r5, r5, r6, ASR #16 @ r5 = icur[0] - ilast[0]
MLA r6, r4, r5, r6 @ r6 = (icur[0]-ilast[0])*opos_frac+ilast[0]
LDRSH r4, [r3] @ r4 = obuf[0]
LDRSH r5, [r3,#2] @ r5 = obuf[1]
MOV r6, r6, ASR #16 @ r6 = tmp0 = tmp1 >>= 16
MUL r7, r12,r6 @ r7 = tmp0*vol_l
MUL r6, r14,r6 @ r6 = tmp1*vol_r
ADDS r7, r7, r4, LSL #16 @ r7 = obuf[0]<<16 + tmp0*vol_l
RSCVS r7, r10, #0x80000000 @ Clamp r7
ADDS r6, r6, r5, LSL #16 @ r6 = obuf[1]<<16 + tmp1*vol_r
RSCVS r6, r10, #0x80000000 @ Clamp r6
MOV r7, r7, LSR #16 @ Shift back to halfword
MOV r6, r6, LSR #16 @ Shift back to halfword
LDR r5, [r2,#12] @ r5 = opos_inc
STRH r7, [r3],#2 @ Store output value
STRH r6, [r3],#2 @ Store output value
SUBS r11, r11,#1 @ osamp--
BLE LinearRate_M_end @ end if needed
ADDS r8, r8, r5 @ r8 = opos += opos_inc
BLT LinearRate_M_part2
B LinearRate_M_loop
LinearRate_M_end:
ADD r13,r13,#8
STMIA r2,{r0,r1,r8}
MOV r0, r3 @ return obuf
LDMFD r13!,{r4-r11,PC}
LinearRate_M_read:
ADD r0, r2, #28 @ r0 = inPtr = inBuf
.ifdef PALMOS_MODE
LDR r10,[r13,#4*8] @ restore r10
.endif
STMFD r13!,{r0,r2-r3,r12,r14}
MOV r1, r0 @ r1 = inBuf
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
@ Calling back into C++ here. WinCE is fairly easy about such things
@ but other OS are more awkward. r9 is preserved for Symbian, and
@ we have 2+9+5 = 16 things on the stack (an even number).
MOV r14,PC
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
SUBS r1, r0, #1 @ r1 = inLen-1
LDMFD r13!,{r0,r2-r3,r12,r14}
BLT LinearRate_M_end
.ifdef PALMOS_MODE
MOV r10,#0
.endif
B LinearRate_M_read_return
_ARM_LinearRate_S:
@ r0 = AudioStream &input
@ r1 = input.readBuffer
@ r2 = input->sr
@ r3 = obuf
@ <> = osamp
@ <> = vol_l
@ <> = vol_r
MOV r12,r13
STMFD r13!,{r0-r1,r4-r11,r14}
LDMFD r12,{r11,r12,r14} @ r11= osamp
@ r12= vol_l
@ r14= vol_r
LDMIA r2,{r0,r1,r8} @ r0 = inPtr
@ r1 = inLen
@ r8 = opos
CMP r11,#0 @ if (osamp <= 0)
BLE LinearRate_S_end @ bale
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
CMP r1,#0
BGT LinearRate_S_part2
@ part1 - read input samples
LinearRate_S_loop:
SUBS r1, r1, #2 @ r1 = inLen -= 2
BLT LinearRate_S_read
LinearRate_S_read_return:
LDR r10,[r2, #16] @ r10= icur[0,1]
LDRSH r5, [r0],#2 @ r5 = tmp0 = *inPtr++
LDRSH r6, [r0],#2 @ r5 = tmp1 = *inPtr++
SUBS r8, r8, #65536 @ r8 = opos--
STRH r10,[r2,#22] @ ilast[0] = icur[0]
MOV r10,r10,LSR #16
STRH r10,[r2,#26] @ ilast[1] = icur[1]
STRH r5, [r2,#16] @ icur[0] = tmp0
STRH r6, [r2,#18] @ icur[1] = tmp1
BGE LinearRate_S_loop
@ part2 - form output samples
LinearRate_S_part2:
@ We are guaranteed that opos < 0 here
LDR r6, [r2,#20] @ r6 = ilast[0]<<16 + 32768
LDRSH r5, [r2,#16] @ r5 = icur[0]
MOV r4, r8, LSL #16
MOV r4, r4, LSR #16
SUB r5, r5, r6, ASR #16 @ r5 = icur[0] - ilast[0]
MLA r6, r4, r5, r6 @ r6 = (icur[0]-ilast[0])*opos_frac+ilast[0]
LDR r7, [r2,#24] @ r7 = ilast[1]<<16 + 32768
LDRSH r5, [r2,#18] @ r5 = icur[1]
LDRSH r10,[r3] @ r10= obuf[0]
MOV r6, r6, ASR #16 @ r6 = tmp1 >>= 16
SUB r5, r5, r7, ASR #16 @ r5 = icur[1] - ilast[1]
MLA r7, r4, r5, r7 @ r7 = (icur[1]-ilast[1])*opos_frac+ilast[1]
LDRSH r5, [r3,#2] @ r5 = obuf[1]
MOV r7, r7, ASR #16 @ r7 = tmp0 >>= 16
MUL r7, r12,r7 @ r7 = tmp0*vol_l
MUL r6, r14,r6 @ r6 = tmp1*vol_r
ADDS r7, r7, r10, LSL #16 @ r7 = obuf[0]<<16 + tmp0*vol_l
MOV r4, #0
RSCVS r7, r4, #0x80000000 @ Clamp r7
ADDS r6, r6, r5, LSL #16 @ r6 = obuf[1]<<16 + tmp1*vol_r
RSCVS r6, r4, #0x80000000 @ Clamp r6
MOV r7, r7, LSR #16 @ Shift back to halfword
MOV r6, r6, LSR #16 @ Shift back to halfword
LDR r5, [r2,#12] @ r5 = opos_inc
STRH r7, [r3],#2 @ Store output value
STRH r6, [r3],#2 @ Store output value
SUBS r11, r11,#1 @ osamp--
BLE LinearRate_S_end @ and loop
ADDS r8, r8, r5 @ r8 = opos += opos_inc
BLT LinearRate_S_part2
B LinearRate_S_loop
LinearRate_S_end:
ADD r13,r13,#8
STMIA r2,{r0,r1,r8}
MOV r0, r3 @ return obuf
LDMFD r13!,{r4-r11,PC}
LinearRate_S_read:
ADD r0, r2, #28 @ r0 = inPtr = inBuf
.ifdef PALMOS_MODE
LDR r10,[r13,#4*8] @ restore r10
.endif
STMFD r13!,{r0,r2-r3,r12,r14}
MOV r1, r0 @ r1 = inBuf
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
@ Calling back into C++ here. WinCE is fairly easy about such things
@ but other OS are more awkward. r9 is preserved for Symbian, and
@ we have 2+9+5 = 16 things on the stack (an even number).
MOV r14,PC
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
SUBS r1, r0, #2 @ r1 = inLen-2
LDMFD r13!,{r0,r2-r3,r12,r14}
BLT LinearRate_S_end
B LinearRate_S_read_return
_ARM_LinearRate_R:
@ r0 = AudioStream &input
@ r1 = input.readBuffer
@ r2 = input->sr
@ r3 = obuf
@ <> = osamp
@ <> = vol_l
@ <> = vol_r
MOV r12,r13
STMFD r13!,{r0-r1,r4-r11,r14}
LDMFD r12,{r11,r12,r14} @ r11= osamp
@ r12= vol_l
@ r14= vol_r
LDMIA r2,{r0,r1,r8} @ r0 = inPtr
@ r1 = inLen
@ r8 = opos
CMP r11,#0 @ if (osamp <= 0)
BLE LinearRate_R_end @ bale
ORR r12,r12,r12,LSL #8 @ r12= vol_l as 16 bits
ORR r14,r14,r14,LSL #8 @ r14= vol_r as 16 bits
CMP r1,#0
BGT LinearRate_R_part2
@ part1 - read input samples
LinearRate_R_loop:
SUBS r1, r1, #2 @ r1 = inLen -= 2
BLT LinearRate_R_read
LinearRate_R_read_return:
LDR r10,[r2, #16] @ r10= icur[0,1]
LDRSH r5, [r0],#2 @ r5 = tmp0 = *inPtr++
LDRSH r6, [r0],#2 @ r5 = tmp1 = *inPtr++
SUBS r8, r8, #65536 @ r8 = opos--
STRH r10,[r2,#22] @ ilast[0] = icur[0]
MOV r10,r10,LSR #16
STRH r10,[r2,#22] @ ilast[1] = icur[1]
STRH r5, [r2,#16] @ icur[0] = tmp0
STRH r6, [r2,#18] @ icur[1] = tmp1
BGE LinearRate_R_loop
@ part2 - form output samples
LinearRate_R_part2:
@ We are guaranteed that opos < 0 here
LDR r6, [r2,#20] @ r6 = ilast[0]
LDRSH r5, [r2,#16] @ r5 = icur[0]
MOV r4, r8, LSL #16
MOV r4, r4, LSR #16
SUB r5, r5, r6, ASR #16 @ r5 = icur[0] - ilast[0]
MLA r6, r4, r5, r6 @ r6 = (icur[0]-ilast[0])*opos_frac+ilast[0]
LDR r7, [r2,#24] @ r7 = ilast[1]
LDRSH r5, [r2,#18] @ r5 = icur[1]
LDR r10,[r3] @ r10= obuf[0]
MOV r6, r6, ASR #16 @ r6 = tmp1 >>= 16
SUB r5, r5, r7, ASR #16 @ r5 = icur[1] - ilast[1]
MLA r7, r4, r5, r7 @ r7 = (icur[1]-ilast[1])*opos_frac+ilast[1]
LDRSH r5, [r3,#2] @ r5 = obuf[1]
MOV r7, r7, ASR #16 @ r7 = tmp0 >>= 16
MUL r7, r12,r7 @ r7 = tmp0*vol_l
MUL r6, r14,r6 @ r6 = tmp1*vol_r
ADDS r7, r7, r10, LSL #16 @ r7 = obuf[0]<<16 + tmp0*vol_l
MOV r4, #0
RSCVS r7, r4, #0x80000000 @ Clamp r7
ADDS r6, r6, r5, LSL #16 @ r6 = obuf[1]<<16 + tmp1*vol_r
RSCVS r6, r4, #0x80000000 @ Clamp r6
MOV r7, r7, LSR #16 @ Shift back to halfword
MOV r6, r6, LSR #16 @ Shift back to halfword
LDR r5, [r2,#12] @ r5 = opos_inc
STRH r6, [r3],#2 @ Store output value
STRH r7, [r3],#2 @ Store output value
SUBS r11, r11,#1 @ osamp--
BLE LinearRate_R_end @ and loop
ADDS r8, r8, r5 @ r8 = opos += opos_inc
BLT LinearRate_R_part2
B LinearRate_R_loop
LinearRate_R_end:
ADD r13,r13,#8
STMIA r2,{r0,r1,r8}
MOV r0, r3 @ return obuf
LDMFD r13!,{r4-r11,PC}
LinearRate_R_read:
ADD r0, r2, #28 @ r0 = inPtr = inBuf
.ifdef PALMOS_MODE
LDR r10,[r13,#4*8] @ restore r10
.endif
STMFD r13!,{r0,r2-r3,r12,r14}
MOV r1, r0 @ r1 = inBuf
LDR r0, [r13,#20] @ r0 = AudioStream & input (20 = 4*5)
MOV r2, #512 @ r2 = ARRAYSIZE(inBuf)
@ Calling back into C++ here. WinCE is fairly easy about such things
@ but other OS are more awkward. r9 is preserved for Symbian, and
@ we have 2+9+5 = 16 things on the stack (an even number).
MOV r14,PC
LDR PC,[r13,#24] @ inLen = input.readBuffer(inBuf,512) (24 = 4*6)
SUBS r1, r0, #2 @ r1 = inLen-2
LDMFD r13!,{r0,r2-r3,r12,r14}
BLT LinearRate_R_end
B LinearRate_R_read_return