diff --git a/common/frac.h b/common/frac.h new file mode 100644 index 00000000000..8fbdfb55d54 --- /dev/null +++ b/common/frac.h @@ -0,0 +1,52 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) + * + * 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_FRAC_H +#define COMMON_FRAC_H + +#include "common/sys.h" + +/** + * The precision of the fractional (fixed point) type we define below. + * Normally you should never have to modify this value. + */ +enum { + FRAC_BITS = 16, + FRAC_LO_MASK = ((1L << FRAC_BITS) - 1), + FRAC_HI_MASK = ((1L << FRAC_BITS) - 1) << FRAC_BITS, + + FRAC_ONE = (1L << FRAC_BITS), // 1.0 + FRAC_HALF = (1L << (FRAC_BITS-1)) // 0.5 +}; + +/** + * Fixed-point fractions, used by the sound rate converter and other code. + */ +typedef int32 frac_t; + +inline frac_t doubleToFrac(double value) { return (frac_t)(value * FRAC_ONE); } +inline double fracToDouble(frac_t value) { return ((double)value) / FRAC_ONE; } + +inline frac_t intToFrac(int16 value) { return value << FRAC_BITS; } +inline int16 fracToInt(frac_t value) { return value >> FRAC_BITS; } + +#endif diff --git a/common/list.h b/common/list.h new file mode 100644 index 00000000000..df744a71f85 --- /dev/null +++ b/common/list.h @@ -0,0 +1,264 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) + * + * 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_LIST_H +#define COMMON_LIST_H + +#include "common/sys.h" + +namespace Common { + +/** + * Simple double linked list, modeled after the list template of the standard + * C++ library. + */ +template +class List { +protected: +#if defined (_WIN32_WCE) || defined (_MSC_VER) +//FIXME evc4 and msvc7 doesn't like it as protected member +public: +#endif + struct NodeBase { + NodeBase *_prev; + NodeBase *_next; + }; + + template + struct Node : public NodeBase { + t_T2 _data; + + Node(const t_T2 &x) : _data(x) {} + }; + + template + class Iterator { + friend class List; + NodeBase *_node; + +#if !defined (__WINSCW__) + explicit Iterator(NodeBase *node) : _node(node) {} +#else + Iterator(NodeBase *node) : _node(node) {} +#endif + + public: + Iterator() : _node(0) {} + + // Prefix inc + Iterator &operator++() { + if (_node) + _node = _node->_next; + return *this; + } + // Postfix inc + Iterator operator++(int) { + Iterator tmp(_node); + ++(*this); + return tmp; + } + // Prefix dec + Iterator &operator--() { + if (_node) + _node = _node->_prev; + return *this; + } + // Postfix dec + Iterator operator--(int) { + Iterator tmp(_node); + --(*this); + return tmp; + } + t_T2& operator*() const { + assert(_node); +#if (__GNUC__ == 2) && (__GNUC_MINOR__ >= 95) + return static_cast::Node *>(_node)->_data; +#else + return static_cast*>(_node)->_data; +#endif + } + t_T2* operator->() const { + return &(operator*()); + } + + bool operator==(const Iterator& x) const { + return _node == x._node; + } + + bool operator!=(const Iterator& x) const { + return _node != x._node; + } + }; + + NodeBase *_anchor; + +public: + typedef Iterator iterator; + typedef Iterator const_iterator; + + typedef t_T value_type; + +public: + List() { + _anchor = new NodeBase; + _anchor->_prev = _anchor; + _anchor->_next = _anchor; + } + List(const List& list) { + _anchor = new NodeBase; + _anchor->_prev = _anchor; + _anchor->_next = _anchor; + + insert(begin(), list.begin(), list.end()); + } + + ~List() { + clear(); + delete _anchor; + } + + void push_front(const t_T& element) { + insert(begin(), element); + } + + void push_back(const t_T& element) { + insert(end(), element); + } + + void insert(iterator pos, const t_T& element) { + NodeBase *newNode = new Node(element); + + newNode->_next = pos._node; + newNode->_prev = pos._node->_prev; + newNode->_prev->_next = newNode; + newNode->_next->_prev = newNode; + } + + template + void insert(iterator pos, iterator2 first, iterator2 last) { + for (; first != last; ++first) + insert(pos, *first); + } + + iterator erase(iterator pos) { + assert(pos != end()); + + NodeBase *next = pos._node->_next; + NodeBase *prev = pos._node->_prev; + Node *node = static_cast *>(pos._node); + prev->_next = next; + next->_prev = prev; + delete node; + return iterator(next); + } + + iterator reverse_erase(iterator pos) { + assert(pos != end()); + + NodeBase *next = pos._node->_next; + NodeBase *prev = pos._node->_prev; + Node *node = static_cast *>(pos._node); + prev->_next = next; + next->_prev = prev; + delete node; + return iterator(prev); + } + + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); + + return last; + } + + void remove(const t_T &val) { + iterator i = begin(); + while (i != end()) + if (val == i.operator*()) + i = erase(i); + else + ++i; + } + + + List& operator =(const List& list) { + if (this != &list) { + iterator i; + const_iterator j; + + for (i = begin(), j = list.begin(); (i != end()) && (j != list.end()) ; ++i, ++j) { + static_cast *>(i._node)->_data = static_cast *>(j._node)->_data; + } + + if (i == end()) + insert(i, j, list.end()); + else + erase(i, end()); + } + + return *this; + } + + uint size() const { + int n = 0; + for (const_iterator i = begin(); i != end(); ++i) + ++n; + return n; + } + + void clear() { + erase(begin(), end()); + } + + bool empty() const { + return (_anchor == _anchor->_next); + } + + + iterator begin() { + return iterator(_anchor->_next); + } + + iterator reverse_begin() { + return iterator(_anchor->_prev); + } + + iterator end() { + return iterator(_anchor); + } + + const_iterator begin() const { + return const_iterator(_anchor->_next); + } + + const_iterator reverse_begin() const { + return const_iterator(_anchor->_prev); + } + + const_iterator end() const { + return const_iterator(_anchor); + } +}; + +} // End of namespace Common + +#endif diff --git a/common/mutex.cpp b/common/mutex.cpp new file mode 100644 index 00000000000..f310cb19df6 --- /dev/null +++ b/common/mutex.cpp @@ -0,0 +1,72 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) + * + * 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/mutex.h" +#include "common/debug.h" + +#include "engine/backend/driver.h" + +namespace Common { + +Mutex::Mutex() { + _mutex = g_driver->createMutex(); +} + +Mutex::~Mutex() { + g_driver->deleteMutex(_mutex); +} + +void Mutex::lock() { + g_driver->lockMutex(_mutex); +} + +void Mutex::unlock() { + g_driver->unlockMutex(_mutex); +} + + +#pragma mark - + + +StackLock::StackLock(MutexRef mutex, const char *mutexName) + : _mutex(mutex), _mutexName(mutexName) { + lock(); +} + +StackLock::StackLock(const Mutex &mutex, const char *mutexName) + : _mutex(mutex._mutex), _mutexName(mutexName) { + lock(); +} + +StackLock::~StackLock() { + unlock(); +} + +void StackLock::lock() { + g_driver->lockMutex(_mutex); +} + +void StackLock::unlock() { + g_driver->unlockMutex(_mutex); +} + +} // End of namespace Common diff --git a/common/mutex.h b/common/mutex.h new file mode 100644 index 00000000000..480abf6e734 --- /dev/null +++ b/common/mutex.h @@ -0,0 +1,73 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) + * + * 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_MUTEX_H +#define COMMON_MUTEX_H + +#include "common/sys.h" + +namespace Common { + +class Mutex; + +/** + * An pseudo-opaque mutex type. + */ +typedef struct OpaqueMutex *MutexRef; + + +/** + * Auxillary class to (un)lock a mutex on the stack. + */ +class StackLock { + MutexRef _mutex; + const char *_mutexName; + + void lock(); + void unlock(); +public: + StackLock(MutexRef mutex, const char *mutexName = NULL); + StackLock(const Mutex &mutex, const char *mutexName = NULL); + ~StackLock(); +}; + + +/** + * Wrapper class. + */ +class Mutex { + friend class StackLock; + + MutexRef _mutex; + +public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); +}; + + +} // End of namespace Common + +#endif diff --git a/common/noncopyable.h b/common/noncopyable.h new file mode 100644 index 00000000000..19a5d0c5e1c --- /dev/null +++ b/common/noncopyable.h @@ -0,0 +1,43 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) + * + * 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_NONCOPYABLE_H +#define COMMON_NONCOPYABLE_H + +namespace Common { + +/** + * Subclass of NonCopyable can not be copied due to the fact that + * we made the copy constructor and assigment operator private. + */ +class NonCopyable { +public: + NonCopyable() {} +private: + // Prevent copying instances by accident + NonCopyable(const NonCopyable&); + NonCopyable& operator= (const NonCopyable&); +}; + +} // End of namespace Common + +#endif diff --git a/common/sys.h b/common/sys.h index 16b4c4b3e94..188dc716c71 100644 --- a/common/sys.h +++ b/common/sys.h @@ -24,8 +24,6 @@ #define COMMON_SYS_H -typedef struct Mutex *MutexRef; - #ifndef _MSC_VER #include #endif diff --git a/common/timer.cpp b/common/timer.cpp deleted file mode 100644 index 9790700fcec..00000000000 --- a/common/timer.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* Residual - Virtual machine to run LucasArts' 3D adventure games - * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - * - * $URL$ - * $Id$ - * - */ - -#include "common/sys.h" -#include "common/platform.h" -#include "common/debug.h" -#include "common/timer.h" - -#include "engine/backend/driver.h" - -Timer *g_timer = NULL; - -Timer::Timer() : - _mutex(0), - _timerHandler(0), - _lastTime(0) { - - _mutex = g_driver->createMutex(); - - g_timer = this; - - for (int i = 0; i < MAX_TIMERS; i++) { - _timerSlots[i].procedure = NULL; - _timerSlots[i].interval = 0; - _timerSlots[i].counter = 0; - } - - _thisTime = g_driver->getMillis(); - - // Set the timer last, after everything has been initialised - g_driver->setTimerCallback(timer_handler, 10); -} - -Timer::~Timer() { - g_driver->setTimerCallback(NULL, 0); - - { - StackLock lock(_mutex); - for (int i = 0; i < MAX_TIMERS; i++) { - _timerSlots[i].procedure = NULL; - _timerSlots[i].interval = 0; - _timerSlots[i].counter = 0; - } - } - - g_driver->deleteMutex(_mutex); -} - -int Timer::timer_handler(int t) { - if (g_timer) - return g_timer->handler(t); - return 0; -} - -int Timer::handler(int t) { - StackLock lock(_mutex); - uint32 interval, l; - - _lastTime = _thisTime; - _thisTime = g_driver->getMillis(); - interval = 1000 * (_thisTime - _lastTime); - - // If the timer has been frozen for a long time, don't - // call the procedures a silly number of times, just resynchronize - if(interval > 1000000) { - interval = 0; - warning("Timer skipped forward"); - } - - for (l = 0; l < MAX_TIMERS; l++) { - if (_timerSlots[l].procedure && _timerSlots[l].interval > 0) { - _timerSlots[l].counter -= interval; - while (_timerSlots[l].counter <= 0) { - // A small paranoia check which catches the case where - // a timer removes itself (which it never should do). - assert(_timerSlots[l].procedure && _timerSlots[l].interval > 0); - _timerSlots[l].counter += _timerSlots[l].interval; - _timerSlots[l].procedure(_timerSlots[l].refCon); - } - } - } - - return t; -} - -bool Timer::installTimerProc(TimerProc procedure, int32 interval, void *refCon) { - assert(interval > 0); - StackLock lock(_mutex); - - for (int l = 0; l < MAX_TIMERS; l++) { - if (!_timerSlots[l].procedure) { - _timerSlots[l].procedure = procedure; - _timerSlots[l].interval = interval; - _timerSlots[l].counter = interval; - _timerSlots[l].refCon = refCon; - return true; - } - } - - if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) - warning("Couldn't find free timer slot!"); - return false; -} - -void Timer::removeTimerProc(TimerProc procedure) { - StackLock lock(_mutex); - - for (int l = 0; l < MAX_TIMERS; l++) { - if (_timerSlots[l].procedure == procedure) { - _timerSlots[l].procedure = 0; - _timerSlots[l].interval = 0; - _timerSlots[l].counter = 0; - _timerSlots[l].refCon = 0; - } - } -} diff --git a/common/timer.h b/common/timer.h index cc6e55b015c..45c8ed885f9 100644 --- a/common/timer.h +++ b/common/timer.h @@ -1,19 +1,19 @@ /* Residual - Virtual machine to run LucasArts' 3D adventure games - * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org) + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * This 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 library is distributed in the hope that it will be useful, + * 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 - * Lesser General Public License for more details. + * 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 Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * 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$ @@ -24,30 +24,15 @@ #define COMMON_TIMER_H #include "common/sys.h" -#include "common/platform.h" +#include "common/noncopyable.h" -#define MAX_TIMERS 3 - -typedef void (*TimerProc)(void *refCon); - -class Timer { - -private: - MutexRef _mutex; - void *_timerHandler; - int32 _thisTime; - int32 _lastTime; - - struct TimerSlots { - TimerProc procedure; - int32 interval; - int32 counter; - void *refCon; - } _timerSlots[MAX_TIMERS]; +namespace Common { +class TimerManager : NonCopyable { public: - Timer(); - ~Timer(); + typedef void (*TimerProc)(void *refCon); + + virtual ~TimerManager() {} /** * Install a new timer callback. It will from now be called every interval microseconds. @@ -61,19 +46,14 @@ public: * @param refCon an arbitrary void pointer; will be passed to the timer callback * @return true if the timer was installed successfully, false otherwise */ - bool installTimerProc(TimerProc proc, int32 interval, void *refCon); + virtual bool installTimerProc(TimerProc proc, int32 interval, void *refCon) = 0; /** * Remove the given timer callback. It will not be invoked anymore. */ - void removeTimerProc(TimerProc proc); - -protected: - static int timer_handler(int t); - int handler(int t); + virtual void removeTimerProc(TimerProc proc) = 0; }; -extern Timer *g_timer; +} // End of namespace Common #endif - diff --git a/dists/msvc9/residual.vcproj b/dists/msvc9/residual.vcproj index 490e8c5b828..5e247778e93 100644 --- a/dists/msvc9/residual.vcproj +++ b/dists/msvc9/residual.vcproj @@ -198,6 +198,14 @@ RelativePath="..\..\common\debug.h" > + + + + @@ -214,6 +222,18 @@ RelativePath="..\..\common\matrix4.h" > + + + + + + @@ -222,10 +242,6 @@ RelativePath="..\..\common\sys.h" > - - @@ -781,10 +797,18 @@ RelativePath="..\..\engine\imuse\imuse_tables.cpp" > + + + + + + + + diff --git a/engine/backend/default-timer.cpp b/engine/backend/default-timer.cpp new file mode 100644 index 00000000000..d5f003932d5 --- /dev/null +++ b/engine/backend/default-timer.cpp @@ -0,0 +1,144 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) + * + * 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/sys.h" + +#include "engine/backend/driver.h" +#include "engine/backend/default-timer.h" + +struct TimerSlot { + Common::TimerManager::TimerProc callback; + void *refCon; + uint32 interval; // in microseconds + + uint32 nextFireTime; // in milliseconds + uint32 nextFireTimeMicro; // mircoseconds part of nextFire + + TimerSlot *next; +}; + +void insertPrioQueue(TimerSlot *head, TimerSlot *newSlot) { + // The head points to a fake anchor TimerSlot; this common + // trick allows us to get rid of many special cases. + + const uint32 nextFireTime = newSlot->nextFireTime; + TimerSlot *slot = head; + newSlot->next = 0; + + // Insert the new slot into the sorted list of already scheduled + // timers in such a way that the list stays sorted... + while (true) { + assert(slot); + if (slot->next == 0 || nextFireTime < slot->next->nextFireTime) { + newSlot->next = slot->next; + slot->next = newSlot; + return; + } + slot = slot->next; + } +} + + +DefaultTimerManager::DefaultTimerManager() : + _timerHandler(0), + _head(0) { + + _head = new TimerSlot(); + memset(_head, 0, sizeof(TimerSlot)); +} + +DefaultTimerManager::~DefaultTimerManager() { + Common::StackLock lock(_mutex); + + TimerSlot *slot = _head; + while (slot) { + TimerSlot *next = slot->next; + delete slot; + slot = next; + } + _head = 0; +} + +void DefaultTimerManager::handler() { + Common::StackLock lock(_mutex); + + const uint32 curTime = g_driver->getMillis(); + + // Repeat as long as there is a TimerSlot that is scheduled to fire. + TimerSlot *slot = _head->next; + while (slot && slot->nextFireTime < curTime) { + // Remove the slot from the priority queue + _head->next = slot->next; + + // Update the fire time and reinsert the TimerSlot into the priority + // queue. Has to be done before the timer callback is invoked, in case + // the callback wants to remove itself. + assert(slot->interval > 0); + slot->nextFireTime += (slot->interval / 1000); + slot->nextFireTimeMicro += (slot->interval % 1000); + if (slot->nextFireTimeMicro > 1000) { + slot->nextFireTime += slot->nextFireTimeMicro / 1000; + slot->nextFireTimeMicro %= 1000; + } + insertPrioQueue(_head, slot); + + // Invoke the timer callback + assert(slot->callback); + slot->callback(slot->refCon); + + // Look at the next scheduled timer + slot = _head->next; + } +} + +bool DefaultTimerManager::installTimerProc(TimerProc callback, int32 interval, void *refCon) { + assert(interval > 0); + Common::StackLock lock(_mutex); + + TimerSlot *slot = new TimerSlot; + slot->callback = callback; + slot->refCon = refCon; + slot->interval = interval; + slot->nextFireTime = g_driver->getMillis() + interval / 1000; + slot->nextFireTimeMicro = interval % 1000; + slot->next = 0; + + insertPrioQueue(_head, slot); + + return true; +} + +void DefaultTimerManager::removeTimerProc(TimerProc callback) { + Common::StackLock lock(_mutex); + + TimerSlot *slot = _head; + + while (slot->next) { + if (slot->next->callback == callback) { + TimerSlot *next = slot->next->next; + delete slot->next; + slot->next = next; + } else { + slot = slot->next; + } + } +} diff --git a/engine/backend/default-timer.h b/engine/backend/default-timer.h new file mode 100644 index 00000000000..95bcd9a80db --- /dev/null +++ b/engine/backend/default-timer.h @@ -0,0 +1,48 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2008 The ScummVM-Residual Team (www.scummvm.org) + * + * 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 BACKENDS_TIMER_DEFAULT_H +#define BACKENDS_TIMER_DEFAULT_H + +#include "common/timer.h" +#include "common/mutex.h" + +struct TimerSlot; + +class DefaultTimerManager : public Common::TimerManager { +private: + Common::Mutex _mutex; + void *_timerHandler; + TimerSlot *_head; + + +public: + DefaultTimerManager(); + ~DefaultTimerManager(); + bool installTimerProc(TimerProc proc, int32 interval, void *refCon); + void removeTimerProc(TimerProc proc); + + // Timer callback, to be invoked at regular time intervals by the backend. + void handler(); +}; + +#endif diff --git a/engine/backend/driver.h b/engine/backend/driver.h index 0c2a57632bb..6bdbb75223e 100644 --- a/engine/backend/driver.h +++ b/engine/backend/driver.h @@ -25,6 +25,7 @@ #include "common/platform.h" #include "common/vector3d.h" +#include "common/mutex.h" #include "engine/color.h" #include "engine/model.h" @@ -33,9 +34,15 @@ #include "engine/font.h" #include "engine/primitives.h" #include "engine/actor.h" +#include "engine/backend/default-timer.h" class Material; class Bitmap; +class Timer; + +namespace Audio { + class Mixer; +} class Driver { public: @@ -116,7 +123,7 @@ public: /** @name Events and Time */ //@{ - typedef int (*TimerProc)(int interval); + typedef unsigned int (*TimerProc)(unsigned int interval, void *param); /** * The types of events backends may generate. @@ -239,25 +246,22 @@ public: * @param interval the interval (in milliseconds) between invocations * of the callback */ - virtual void setTimerCallback(TimerProc callback, int interval) = 0; + virtual void setTimerCallback() = 0; + + virtual void clearTimerCallback() = 0; //@} /** * @name Mutex handling - * Historically, the OSystem API used to have a method which allowed - * creating threads. Hence mutex support was needed for thread syncing. - * To ease portability, though, we decided to remove the threading API. - * Instead, we now use timers (see setTimerCallback() and Timer). - * But since those may be implemented using threads (and in fact, that's - * how our primary backend, the SDL one, does it on many systems), we - * still have to do mutex syncing in our timer callbacks. * * Hence backends which do not use threads to implement the timers simply * can use dummy implementations for these methods. */ //@{ + typedef Common::MutexRef MutexRef; + /** * Create a new mutex. * @return the newly created mutex, or 0 if an error occured. @@ -266,6 +270,12 @@ public: /** * Lock the given mutex. + * + * @note Code assumes that the mutex implementation supports + * recursive locking. That is, a thread may lock a mutex twice w/o + * deadlocking. In case of a multilock, the mutex has to be unlocked + * as many times as it was locked befored it really becomes unlocked. + * * @param mutex the mutex to lock. */ virtual void lockMutex(MutexRef mutex) = 0; @@ -329,16 +339,7 @@ protected: }; extern Driver *g_driver; - -class StackLock { - MutexRef _mutex; -public: - StackLock(MutexRef mutex) : _mutex(mutex) { - g_driver->lockMutex(_mutex); - } - ~StackLock() { - g_driver->unlockMutex(_mutex); - } -}; +extern DefaultTimerManager *g_timer; +extern Audio::Mixer *g_mixer; #endif diff --git a/engine/backend/sdl/driver_sdl.cpp b/engine/backend/sdl/driver_sdl.cpp index c54b363ec74..5b400a4bdd6 100644 --- a/engine/backend/sdl/driver_sdl.cpp +++ b/engine/backend/sdl/driver_sdl.cpp @@ -359,11 +359,22 @@ void DriverSDL::delayMillis(uint msecs) { SDL_Delay(msecs); } -void DriverSDL::setTimerCallback(TimerProc callback, int timer) { - SDL_SetTimer(timer, (SDL_TimerCallback) callback); +static SDL_TimerID _timerID = NULL; + +static Uint32 timer_handler(Uint32 interval, void *param) { + ((DefaultTimerManager *)param)->handler(); + return interval; } -MutexRef DriverSDL::createMutex() { +void DriverSDL::setTimerCallback() { + _timerID = SDL_AddTimer(10, &timer_handler, g_timer); +} + +void DriverSDL::clearTimerCallback() { + SDL_RemoveTimer(_timerID); +} + +Common::MutexRef DriverSDL::createMutex() { return (MutexRef)SDL_CreateMutex(); } @@ -382,12 +393,20 @@ void DriverSDL::deleteMutex(MutexRef mutex) { bool DriverSDL::setSoundCallback(SoundProc proc, void *param) { SDL_AudioSpec desired; + // Determine the sample buffer size. We want it to store enough data for + // about 1/10th 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. + int samples = 0x8000; + while (10 * samples >= _samplesPerSec) { + samples >>= 1; + } + memset(&desired, 0, sizeof(desired)); desired.freq = _samplesPerSec; desired.format = AUDIO_S16SYS; desired.channels = 2; - desired.samples = 2048; + desired.samples = (uint16)samples; desired.callback = proc; desired.userdata = param; diff --git a/engine/backend/sdl/driver_sdl.h b/engine/backend/sdl/driver_sdl.h index 1968bd10c3d..35f16f618c1 100644 --- a/engine/backend/sdl/driver_sdl.h +++ b/engine/backend/sdl/driver_sdl.h @@ -52,7 +52,8 @@ public: bool pollEvent(Event &event); uint32 getMillis(); void delayMillis(uint msecs); - void setTimerCallback(TimerProc callback, int interval); + void setTimerCallback(); + void clearTimerCallback(); MutexRef createMutex(); void lockMutex(MutexRef mutex); diff --git a/engine/font.cpp b/engine/font.cpp index 17f196dc993..fc98c30e646 100644 --- a/engine/font.cpp +++ b/engine/font.cpp @@ -42,7 +42,7 @@ Font::Font(const char *filename, const char *data, int /*len*/) : data += 32; // Read character indexes - are the key/value reversed? - _charIndex = (uint16 *)malloc(sizeof(_charIndex) * _numChars); + _charIndex = new uint16[_numChars]; if (!_charIndex) error("Could not load font %s. Out of memory\n", filename); for (uint i = 0; i < _numChars; ++i) { @@ -52,7 +52,7 @@ Font::Font(const char *filename, const char *data, int /*len*/) : data += _numChars * 2; // Read character headers - _charHeaders = (CharHeader *)malloc(sizeof(CharHeader) * _numChars); + _charHeaders = new CharHeader[_numChars]; if (!_charHeaders) error("Could not load font %s. Out of memory\n", filename); for (uint i = 0; i < _numChars; ++i) { @@ -65,7 +65,7 @@ Font::Font(const char *filename, const char *data, int /*len*/) : data += 16; } // Read font data - _fontData = (byte *)malloc(_dataSize); + _fontData = new byte[_dataSize]; if (!_fontData) error("Could not load font %s. Out of memory\n", filename); @@ -73,9 +73,9 @@ Font::Font(const char *filename, const char *data, int /*len*/) : } Font::~Font() { - free(_charIndex); - free(_charHeaders); - free(_fontData); + delete[] _charIndex; + delete[] _charHeaders; + delete[] _fontData; } uint16 Font::getCharIndex(unsigned char c) { diff --git a/engine/imuse/imuse.cpp b/engine/imuse/imuse.cpp index 09726344138..253949b58e6 100644 --- a/engine/imuse/imuse.cpp +++ b/engine/imuse/imuse.cpp @@ -24,6 +24,7 @@ #include "common/platform.h" #include "common/debug.h" #include "common/timer.h" +#include "common/mutex.h" #include "engine/engine.h" #include "engine/savegame.h" @@ -42,26 +43,20 @@ extern ImuseTable grimSeqMusicTable[]; extern ImuseTable grimDemoStateMusicTable[]; extern ImuseTable grimDemoSeqMusicTable[]; -Imuse::Track::Track() - : used(false), stream(NULL) { -} - void Imuse::timerHandler(void *refCon) { Imuse *imuse = (Imuse *)refCon; imuse->callback(); } Imuse::Imuse(int fps) { - _mutex = g_driver->createMutex(); _pause = false; _sound = new ImuseSndMgr(); - _volVoice = 0; - _volSfx = 0; - _volMusic = 0; + assert(_sound); _callbackFps = fps; resetState(); for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { _track[l] = new Track; + assert(_track[l]); _track[l]->trackId = l; _track[l]->used = false; strcpy(_track[l]->soundName, ""); @@ -73,13 +68,12 @@ Imuse::Imuse(int fps) { } Imuse::~Imuse() { - stopAllSounds(); g_timer->removeTimerProc(timerHandler); + stopAllSounds(); for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { delete _track[l]; } delete _sound; - g_driver->deleteMutex(_mutex); } void Imuse::resetState() { @@ -89,19 +83,18 @@ void Imuse::resetState() { } void Imuse::restoreState(SaveGame *savedState) { - StackLock lock(_mutex); + Common::StackLock lock(_mutex); printf("Imuse::restoreState() started.\n"); savedState->beginSection('IMUS'); - savedState->read(&_volVoice, sizeof(int32)); - savedState->read(&_volSfx, sizeof(int32)); - savedState->read(&_volMusic, sizeof(int32)); savedState->read(&_curMusicState, sizeof(int32)); savedState->read(&_curMusicSeq, sizeof(int32)); savedState->read(_attributes, sizeof(int32) * 185); for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { Track *track = _track[l]; + memset(track, 0, sizeof(Track)); + track->trackId = l; savedState->read(&track->pan, sizeof(int32)); savedState->read(&track->panFadeDest, sizeof(int32)); savedState->read(&track->panFadeDelay, sizeof(int32)); @@ -113,50 +106,51 @@ void Imuse::restoreState(SaveGame *savedState) { savedState->read(track->soundName, 32); savedState->read(&track->used, sizeof(bool)); savedState->read(&track->toBeRemoved, sizeof(bool)); - savedState->read(&track->readyToRemove, sizeof(bool)); - savedState->read(&track->started, sizeof(bool)); savedState->read(&track->priority, sizeof(int32)); savedState->read(&track->regionOffset, sizeof(int32)); savedState->read(&track->dataOffset, sizeof(int32)); savedState->read(&track->curRegion, sizeof(int32)); savedState->read(&track->curHookId, sizeof(int32)); savedState->read(&track->volGroupId, sizeof(int32)); - savedState->read(&track->iteration, sizeof(int32)); + savedState->read(&track->feedSize, sizeof(int32)); savedState->read(&track->mixerFlags, sizeof(int32)); - savedState->read(&track->mixerVol, sizeof(int32)); - savedState->read(&track->mixerPan, sizeof(int32)); if (!track->used) continue; - track->readyToRemove = false; - if (track->toBeRemoved) { - track->stream = NULL; + if (track->toBeRemoved || track->curRegion == -1) { track->used = false; continue; } - track->soundHandle = _sound->openSound(track->soundName, track->volGroupId); - assert(track->soundHandle); + track->soundDesc = _sound->openSound(track->soundName, track->volGroupId); + if (!track->soundDesc) { + warning("Imuse::restoreState: Can't open sound so will not be resumed"); + track->used = false; + continue; + } - int32 streamBufferSize = track->iteration; - int freq = _sound->getFreq(track->soundHandle); + int channels = _sound->getChannels(track->soundDesc); + int freq = _sound->getFreq(track->soundDesc); + track->mixerFlags = kFlag16Bits; + if (channels == 2) + track->mixerFlags |= kFlagStereo | kFlagReverseStereo; - track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize); - g_mixer->playInputStream(&track->handle, track->stream, false, -1, track->mixerVol, track->mixerPan, false); + track->stream = Audio::makeAppendableAudioStream(freq, makeMixerFlags(track->mixerFlags)); + g_mixer->playInputStream(track->getType(), &track->handle, track->stream, -1, track->getVol(), track->getPan()); + g_mixer->pauseHandle(track->handle, true); } savedState->endSection(); + g_mixer->pauseAll(false); + printf("Imuse::restoreState() finished.\n"); } void Imuse::saveState(SaveGame *savedState) { - StackLock lock(_mutex); + Common::StackLock lock(_mutex); printf("Imuse::saveState() started.\n"); savedState->beginSection('IMUS'); - savedState->write(&_volVoice, sizeof(int32)); - savedState->write(&_volSfx, sizeof(int32)); - savedState->write(&_volMusic, sizeof(int32)); savedState->write(&_curMusicState, sizeof(int32)); savedState->write(&_curMusicSeq, sizeof(int32)); savedState->write(_attributes, sizeof(int32) * 185); @@ -174,31 +168,43 @@ void Imuse::saveState(SaveGame *savedState) { savedState->write(track->soundName, 32); savedState->write(&track->used, sizeof(bool)); savedState->write(&track->toBeRemoved, sizeof(bool)); - savedState->write(&track->readyToRemove, sizeof(bool)); - savedState->write(&track->started, sizeof(bool)); savedState->write(&track->priority, sizeof(int32)); savedState->write(&track->regionOffset, sizeof(int32)); savedState->write(&track->dataOffset, sizeof(int32)); savedState->write(&track->curRegion, sizeof(int32)); savedState->write(&track->curHookId, sizeof(int32)); savedState->write(&track->volGroupId, sizeof(int32)); - savedState->write(&track->iteration, sizeof(int32)); + savedState->write(&track->feedSize, sizeof(int32)); savedState->write(&track->mixerFlags, sizeof(int32)); - savedState->write(&track->mixerVol, sizeof(int32)); - savedState->write(&track->mixerPan, sizeof(int32)); } savedState->endSection(); printf("Imuse::saveState() finished.\n"); } +int32 Imuse::makeMixerFlags(int32 flags) { + int32 mixerFlags = 0; + if (flags & kFlag16Bits) + mixerFlags |= Audio::Mixer::FLAG_16BITS; + if (flags & kFlagLittleEndian) + mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; + if (flags & kFlagStereo) + mixerFlags |= Audio::Mixer::FLAG_STEREO; + if (flags & kFlagReverseStereo) + mixerFlags |= Audio::Mixer::FLAG_REVERSE_STEREO; + return mixerFlags; +} + void Imuse::callback() { - StackLock lock(_mutex); + Common::StackLock lock(_mutex); for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { Track *track = _track[l]; - if (track->used && !track->readyToRemove) { - if (track->toBeRemoved) { - track->readyToRemove = true; + if (track->used) { + // Ignore tracks which are about to finish. Also, if it did finish in the meantime, + // mark it as unused. + if (!track->stream) { + if (!g_mixer->isSoundHandleActive(track->handle)) + memset(track, 0, sizeof(Track)); continue; } @@ -209,17 +215,21 @@ void Imuse::callback() { if (track->volFadeStep < 0) { if (track->vol > track->volFadeDest) { track->vol += track->volFadeStep; + //warning("fade: %d", track->vol); if (track->vol < track->volFadeDest) { track->vol = track->volFadeDest; track->volFadeUsed = false; } if (track->vol == 0) { - track->toBeRemoved = true; + // Fade out complete -> remove this track + flushTrack(track); + continue; } } } else if (track->volFadeStep > 0) { if (track->vol < track->volFadeDest) { track->vol += track->volFadeStep; + //warning("fade: %d", track->vol); if (track->vol > track->volFadeDest) { track->vol = track->volFadeDest; track->volFadeUsed = false; @@ -248,74 +258,60 @@ void Imuse::callback() { } } - int pan = track->pan / 1000; - pan = (pan != 64) ? 2 * pan - 127 : 0; - int vol = track->vol / 1000; + assert(track->stream); + byte *data = NULL; + int32 result = 0; - if (track->volGroupId == 1) - vol = (vol * _volVoice) / 128; - if (track->volGroupId == 2) - vol = (vol * _volSfx) / 128; - if (track->volGroupId == 3) - vol = (vol * _volMusic) / 128; - - track->mixerVol = vol; - track->mixerPan = pan; - - if (track->stream) { - byte *data = NULL; - int32 result = 0; - - if (track->curRegion == -1) { - switchToNextRegion(track); - if (track->toBeRemoved) - continue; - } - - int channels = _sound->getChannels(track->soundHandle); - - int32 mixer_size = track->iteration / _callbackFps; - - if (track->stream->endOfData()) { - mixer_size *= 2; - } - - if (channels == 1) - mixer_size &= ~1; - if (channels == 2) - mixer_size &= ~3; - - if (mixer_size == 0) + if (track->curRegion == -1) { + switchToNextRegion(track); + if (!track->stream) // Seems we reached the end of the stream continue; + } - do { - result = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &data, track->regionOffset, mixer_size); - if (channels == 1) { - result &= ~1; - } - if (channels == 2) { - result &= ~3; - } + int channels = _sound->getChannels(track->soundDesc); + int32 mixer_size = track->feedSize / _callbackFps; - if (result > mixer_size) - result = mixer_size; + if (track->stream->endOfData()) { + mixer_size *= 2; + } - if (g_mixer->isReady()) { - g_mixer->setChannelVolume(track->handle, vol); - g_mixer->setChannelBalance(track->handle, pan); - track->stream->append(data, result); - track->regionOffset += result; - free(data); - } + if (channels == 1) + mixer_size &= ~1; + if (channels == 2) + mixer_size &= ~3; - if (_sound->isEndOfRegion(track->soundHandle, track->curRegion)) { - switchToNextRegion(track); - if (track->toBeRemoved) - break; - } - mixer_size -= result; - assert(mixer_size >= 0); - } while (mixer_size != 0); + if (mixer_size == 0) + continue; + + do { + result = _sound->getDataFromRegion(track->soundDesc, track->curRegion, &data, track->regionOffset, mixer_size); + if (channels == 1) { + result &= ~1; + } + if (channels == 2) { + result &= ~3; + } + + if (result > mixer_size) + result = mixer_size; + + if (g_mixer->isReady()) { + track->stream->queueBuffer(data, result); + track->regionOffset += result; + } else + delete[] data; + + if (_sound->isEndOfRegion(track->soundDesc, track->curRegion)) { + switchToNextRegion(track); + if (!track->stream) + break; + } + mixer_size -= result; + assert(mixer_size >= 0); + } while (mixer_size); + if (g_mixer->isReady()) { + g_mixer->setChannelVolume(track->handle, track->getVol()); + g_mixer->setChannelBalance(track->handle, track->getPan()); } } } @@ -325,51 +321,51 @@ void Imuse::switchToNextRegion(Track *track) { assert(track); if (track->trackId >= MAX_IMUSE_TRACKS) { - track->toBeRemoved = true; + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::switchToNextRegion(): fadeTrack end: soundName:%s\n", track->soundName); + flushTrack(track); return; } - int numRegions = _sound->getNumRegions(track->soundHandle); + int numRegions = _sound->getNumRegions(track->soundDesc); if (++track->curRegion == numRegions) { - track->toBeRemoved = true; + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::switchToNextRegion(): end of tracks: soundName:%s\n", track->soundName); + flushTrack(track); return; } - ImuseSndMgr::SoundStruct *soundHandle = track->soundHandle; - int jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, track->curHookId); + ImuseSndMgr::SoundDesc *soundDesc = track->soundDesc; + int jumpId = _sound->getJumpIdByRegionAndHookId(soundDesc, track->curRegion, track->curHookId); if (jumpId == -1) - jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, 0); + jumpId = _sound->getJumpIdByRegionAndHookId(soundDesc, track->curRegion, 0); if (jumpId != -1) { - int region = _sound->getRegionIdByJumpId(soundHandle, jumpId); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::switchToNextRegion(): JUMP: soundName:%s\n", track->soundName); + int region = _sound->getRegionIdByJumpId(soundDesc, jumpId); assert(region != -1); - int sampleHookId = _sound->getJumpHookId(soundHandle, jumpId); + int sampleHookId = _sound->getJumpHookId(soundDesc, jumpId); assert(sampleHookId != -1); - int fadeDelay = (60 * _sound->getJumpFade(soundHandle, jumpId)) / 1000; - if (sampleHookId != 0) { - if (track->curHookId == sampleHookId) { - if (fadeDelay != 0) { - Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay); - fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion); - fadeTrack->regionOffset = 0; - fadeTrack->curHookId = 0; - } - track->curRegion = region; - track->curHookId = 0; - } - } else { - if (fadeDelay != 0) { - Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay); - fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion); + int fadeDelay = (60 * _sound->getJumpFade(soundDesc, jumpId)) / 1000; + if (fadeDelay) { + Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay); + if (fadeTrack) { + fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundDesc, fadeTrack->curRegion); fadeTrack->regionOffset = 0; - } - track->curRegion = region; - if (track->curHookId == 0x80) { - track->curHookId = 0; + fadeTrack->curHookId = 0; } } + track->curRegion = region; + if (track->curHookId == sampleHookId) + track->curHookId = 0; + else + if (track->curHookId == 0x80) + track->curHookId = 0; } - track->dataOffset = _sound->getRegionOffset(soundHandle, track->curRegion); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::switchToNextRegion(): REGION %d: soundName:%s\n", track->curRegion, track->soundName); + track->dataOffset = _sound->getRegionOffset(soundDesc, track->curRegion); track->regionOffset = 0; } diff --git a/engine/imuse/imuse.h b/engine/imuse/imuse.h index 2a01c9b9eb3..0db58ceb766 100644 --- a/engine/imuse/imuse.h +++ b/engine/imuse/imuse.h @@ -34,21 +34,12 @@ #include "engine/imuse/imuse_sndmgr.h" #include "engine/imuse/imuse_mcmp_mgr.h" +#include "engine/imuse/imuse_track.h" +#include "engine/imuse/imuse_tables.h" #define MAX_IMUSE_TRACKS 16 #define MAX_IMUSE_FADETRACKS 16 -struct ImuseTable { - byte opcode; - int16 soundId; - byte atribPos; - byte hookId; - int16 fadeOut60TicksDelay; - byte volume; - byte pan; - char filename[32]; -}; - class SaveGame; class Imuse { @@ -56,52 +47,11 @@ private: int _callbackFps; - struct Track { - int trackId; - - int32 pan; - int32 panFadeDest; - int32 panFadeStep; - int32 panFadeDelay; - bool panFadeUsed; - int32 vol; - int32 volFadeDest; - int32 volFadeStep; - int32 volFadeDelay; - bool volFadeUsed; - - char soundName[32]; - bool used; - bool toBeRemoved; - bool readyToRemove; - bool started; - int32 priority; - int32 regionOffset; - int32 dataOffset; - int32 curRegion; - int32 curHookId; - int32 volGroupId; - int32 iteration; - int32 mixerFlags; - int32 mixerVol; - int32 mixerPan; - - ImuseSndMgr::SoundStruct *soundHandle; - PlayingSoundHandle handle; - AppendableAudioStream *stream; - - Track(); - }; - Track *_track[MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS]; - MutexRef _mutex; + Common::Mutex _mutex; ImuseSndMgr *_sound; - int32 _volVoice; - int32 _volSfx; - int32 _volMusic; - bool _pause; int32 _attributes[185]; @@ -111,6 +61,7 @@ private: const ImuseTable *_stateMusicTable; const ImuseTable *_seqMusicTable; + int32 makeMixerFlags(int32 flags); static void timerHandler(void *refConf); void callback(); void switchToNextRegion(Track *track); @@ -118,30 +69,27 @@ private: void selectVolumeGroup(const char *soundName, int volGroupId); void fadeOutMusic(int fadeDelay); + void fadeOutMusicAndStartNew(int fadeDelay, const char *filename, int hookId, int vol, int pan); Track *cloneToFadeOutTrack(Track *track, int fadeDelay); void playMusic(const ImuseTable *table, int atribPos, bool sequence); + void flushTrack(Track *track); + public: Imuse(int fps); ~Imuse(); - bool startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority); + bool startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority, Track *otherTrack); void startVoice(const char *soundName, int volume = 127, int pan = 64); void startMusic(const char *soundName, int hookId, int volume, int pan); + void startMusicWithOtherPos(const char *soundName, int hookId, int volume, int pan, Track *otherTrack); void startSfx(const char *soundName, int priority = 127); void restoreState(SaveGame *savedState); void saveState(SaveGame *savedState); void resetState(); - void setGroupVoiceVolume(int volume) { _volVoice = volume; } - void setGroupSfxVolume(int volume) { _volSfx = volume; } - void setGroupMusicVolume(int volume) { _volMusic = volume; } - int getGroupVoiceVolume() { return _volVoice; } - int getGroupSfxVolume() { return _volSfx; } - int getGroupMusicVolume() { return _volMusic; } - Track *findTrack(const char *soundName); void setPriority(const char *soundName, int priority); void setVolume(const char *soundName, int volume); @@ -160,6 +108,8 @@ public: void flushTracks(); bool isVoicePlaying(); char *getCurMusicSoundName(); + int getCurMusicPan(); + int getCurMusicVol(); bool getSoundStatus(const char *soundName); int32 getPosIn60HzTicks(const char *soundName); }; diff --git a/engine/imuse/imuse_mcmp_mgr.cpp b/engine/imuse/imuse_mcmp_mgr.cpp index 69cd3d7953d..2b1739ef824 100644 --- a/engine/imuse/imuse_mcmp_mgr.cpp +++ b/engine/imuse/imuse_mcmp_mgr.cpp @@ -45,10 +45,8 @@ McmpMgr::McmpMgr() { McmpMgr::~McmpMgr() { if (_file) fclose(_file); - if (_compTable) - free(_compTable); - if (_compInput) - free(_compInput); + delete[] _compTable; + delete[] _compInput; } bool McmpMgr::openSound(const char *filename, byte **resPtr, int &offsetData) { @@ -72,7 +70,7 @@ bool McmpMgr::openSound(const char *filename, byte **resPtr, int &offsetData) { int offset = ftell(_file) + (_numCompItems * 9) + 2; _numCompItems--; - _compTable = (CompTable *)malloc(sizeof(CompTable) * _numCompItems); + _compTable = new CompTable[_numCompItems]; fseek(_file, 5, SEEK_CUR); fread(&_compTable[0].decompSize, 1, 4, _file); int headerSize = _compTable[0].decompSize = READ_BE_UINT32(&_compTable[0].decompSize); @@ -98,7 +96,7 @@ bool McmpMgr::openSound(const char *filename, byte **resPtr, int &offsetData) { _compTable[i].offset += sizeCodecs; } fseek(_file, sizeCodecs, SEEK_CUR); - _compInput = (byte *)malloc(maxSize); + _compInput = new byte[maxSize]; fread(_compInput, 1, headerSize, _file); *resPtr = _compInput; offsetData = headerSize; @@ -123,7 +121,7 @@ int32 McmpMgr::decompressSample(int32 offset, int32 size, byte **comp_final) { last_block = _numCompItems - 1; int32 blocks_final_size = 0x2000 * (1 + last_block - first_block); - *comp_final = (byte *)malloc(blocks_final_size); + *comp_final = new byte[blocks_final_size]; final_size = 0; for (i = first_block; i <= last_block; i++) { diff --git a/engine/imuse/imuse_music.cpp b/engine/imuse/imuse_music.cpp index 27b9a1ff7ef..81af597d0c1 100644 --- a/engine/imuse/imuse_music.cpp +++ b/engine/imuse/imuse_music.cpp @@ -40,10 +40,13 @@ void Imuse::setMusicState(int stateId) { } assert(num != -1); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::setMusicState(): SoundId %d, filename: %s\n", _stateMusicTable[l].soundId, _stateMusicTable[l].filename); + if (_curMusicState == num) return; - if (_curMusicSeq == 0) { + if (!_curMusicSeq) { playMusic(&_stateMusicTable[num], num, false); } @@ -68,10 +71,13 @@ int Imuse::setMusicSequence(int seqId) { assert(num != -1); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::setMusicSequence(): SoundId %d, filename: %s\n", _seqMusicTable[l].soundId, _seqMusicTable[l].filename); + if (_curMusicSeq == num) return _seqMusicTable[_curMusicSeq].soundId; - if (num != 0) { + if (num) { playMusic(&_seqMusicTable[num], 0, true); } else { playMusic(&_stateMusicTable[_curMusicState], _curMusicState, true); @@ -85,12 +91,12 @@ int Imuse::setMusicSequence(int seqId) { void Imuse::playMusic(const ImuseTable *table, int atribPos, bool sequence) { int hookId = 0; - if (atribPos != 0) { - if (table->atribPos != 0) + if (atribPos) { + if (table->atribPos) atribPos = table->atribPos; hookId = _attributes[atribPos]; - if (table->hookId != 0) { - if ((hookId == 0) && (table->hookId > 1)) { + if (table->hookId) { + if (hookId && table->hookId > 1) { _attributes[atribPos] = 2; } else { _attributes[atribPos] = hookId + 1; @@ -104,27 +110,51 @@ void Imuse::playMusic(const ImuseTable *table, int atribPos, bool sequence) { if (table->opcode == 0) { fadeOutMusic(120); - } else if ((table->opcode == 2) || (table->opcode == 3)) { + return; + } + + if (table->opcode == 2 || table->opcode == 3) { if (table->filename[0] == 0) { fadeOutMusic(60); - } else { - char *soundName = getCurMusicSoundName(); - int pan; + return; + } + char *soundName = getCurMusicSoundName(); + int pan; - if (table->pan == 0) - pan = 64; - else - pan = table->pan; - if (soundName != NULL && (table->opcode == 3) && (!sequence) - && (strcmp(soundName, table->filename) == 0) && (table->atribPos != 0) - && table->atribPos == _stateMusicTable[_curMusicState].atribPos) { - setFadeVolume(soundName, table->volume, table->fadeOut60TicksDelay); - setFadePan(soundName, pan, table->fadeOut60TicksDelay); - setHookId(soundName, hookId); - } else { - fadeOutMusic(table->fadeOut60TicksDelay); - startMusic(table->filename, hookId, table->volume, pan); - } + if (table->pan == 0) + pan = 64; + else + pan = table->pan; + if (!soundName) { + startMusic(table->filename, hookId, 0, pan); + setFadeVolume(table->filename, table->volume, table->fadeOut60TicksDelay); + return; + } + int old_pan = getCurMusicPan(); + int old_vol = getCurMusicVol(); + if (old_pan == -1) + old_pan = 64; + if (old_vol == -1) + old_vol = 127; + + if (table->opcode == 2) { + fadeOutMusic(table->fadeOut60TicksDelay); + startMusic(table->filename, hookId, table->volume, pan); + return; + } + if (strcmp(soundName, table->filename) == 0) { + setFadeVolume(soundName, table->volume, table->fadeOut60TicksDelay); + setFadePan(soundName, pan, table->fadeOut60TicksDelay); + return; + } + + if (!sequence && table->atribPos && table->atribPos == _stateMusicTable[_curMusicState].atribPos) { + fadeOutMusicAndStartNew(table->fadeOut60TicksDelay, table->filename, hookId, old_vol, old_pan); + setFadeVolume(table->filename, table->volume, table->fadeOut60TicksDelay); + setFadePan(table->filename, pan, table->fadeOut60TicksDelay); + } else { + fadeOutMusic(table->fadeOut60TicksDelay); + startMusic(table->filename, hookId, table->volume, pan); } } } diff --git a/engine/imuse/imuse_script.cpp b/engine/imuse/imuse_script.cpp index fb2aa333c62..3cd43fd8d88 100644 --- a/engine/imuse/imuse_script.cpp +++ b/engine/imuse/imuse_script.cpp @@ -24,6 +24,7 @@ #include "common/platform.h" #include "common/debug.h" #include "common/timer.h" +#include "common/mutex.h" #include "engine/backend/driver.h" @@ -33,34 +34,41 @@ #include "engine/imuse/imuse.h" #include "engine/imuse/imuse_sndmgr.h" +void Imuse::flushTrack(Track *track) { + track->toBeRemoved = true; + + if (track->stream) { + // Finalize the appendable stream, then remove our reference to it. + // Note that there might still be some data left in the buffers of the + // appendable stream. We play it nice and wait till all of it + // played. The audio mixer will take care of it afterwards (and dispose it). + track->stream->finish(); + track->stream = 0; + if (track->soundDesc) { + _sound->closeSound(track->soundDesc); + track->soundDesc = 0; + } + } + + if (!g_mixer->isSoundHandleActive(track->handle)) { + memset(track, 0, sizeof(Track)); + } +} + void Imuse::flushTracks() { - // flushTracks should not lock the stack since all the functions - // that call it already do (stopAllSounds, startSound) + Common::StackLock lock(_mutex); for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { Track *track = _track[l]; - if (track->used && track->readyToRemove) { - if (track->stream) { - if (!track->stream->endOfStream()) { - track->stream->finish(); - } - if (track->stream->endOfStream()) { - g_mixer->stopHandle(track->handle); - delete track->stream; - track->stream = NULL; - _sound->closeSound(track->soundHandle); - track->soundHandle = NULL; - track->used = false; - strcpy(track->soundName, ""); - } - } + if (track->used && track->toBeRemoved && !g_mixer->isSoundHandleActive(track->handle)) { + memset(track, 0, sizeof(Track)); } } } void Imuse::refreshScripts() { + Common::StackLock lock(_mutex); bool found = false; - - StackLock lock(_mutex); + for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *track = _track[l]; if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { @@ -68,52 +76,58 @@ void Imuse::refreshScripts() { } } - if (!found && (_curMusicSeq != 0)) { - setMusicSequence(2000); + if (!found && _curMusicState) { + setMusicSequence(0); } } void Imuse::startVoice(const char *soundName, int volume, int pan) { - startSound(soundName, IMUSE_VOLGRP_VOICE, 0, volume, pan, 127); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::startVoice(): SoundName %s, vol:%d, pan:%d\n", soundName, volume, pan); + startSound(soundName, IMUSE_VOLGRP_VOICE, 0, volume, pan, 127, NULL); } void Imuse::startMusic(const char *soundName, int hookId, int volume, int pan) { - startSound(soundName, IMUSE_VOLGRP_MUSIC, hookId, volume, pan, 126); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::startMusic(): SoundName %s, hookId:%d, vol:%d, pan:%d\n", soundName, hookId, volume, pan); + startSound(soundName, IMUSE_VOLGRP_MUSIC, hookId, volume, pan, 126, NULL); +} + +void Imuse::startMusicWithOtherPos(const char *soundName, int hookId, int volume, int pan, Track *otherTrack) { + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::startMusicWithOtherPos(): SoundName %s, hookId:%d, vol:%d, pan:%d\n", soundName, volume, pan); + startSound(soundName, IMUSE_VOLGRP_MUSIC, hookId, volume, pan, 126, otherTrack); } void Imuse::startSfx(const char *soundName, int priority) { - startSound(soundName, IMUSE_VOLGRP_SFX, 0, 127, 0, priority); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::startSfx(): SoundName %s, priority:%d\n", soundName, priority); + startSound(soundName, IMUSE_VOLGRP_SFX, 0, 127, 0, priority, NULL); } int32 Imuse::getPosIn60HzTicks(const char *soundName) { + Common::StackLock lock(_mutex); Track *getTrack = NULL; getTrack = findTrack(soundName); // Warn the user if the track was not found if (getTrack == NULL) { if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) - warning("Music track '%s' could not be found to get ticks!", soundName); + warning("Sound '%s' could not be found to get ticks!", soundName); return false; } - if (getTrack->handle.isActive()) { - int32 pos = (5 * (getTrack->dataOffset + getTrack->regionOffset)) / (getTrack->iteration / 12); - return pos; - } - return -1; + int32 pos = (5 * (getTrack->dataOffset + getTrack->regionOffset)) / (getTrack->feedSize / 200); + return pos; } bool Imuse::isVoicePlaying() { - StackLock lock(_mutex); + Common::StackLock lock(_mutex); for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *track = _track[l]; - // Make sure the track is in use before checking the group ID, - // otherwise volGroupId can be uninitialized or reference an - // old track. if (track->used && track->volGroupId == IMUSE_VOLGRP_VOICE) { - if (track->handle.isActive()) { + if (g_mixer->isSoundHandleActive(track->handle)) return true; - } } } @@ -121,51 +135,55 @@ bool Imuse::isVoicePlaying() { } bool Imuse::getSoundStatus(const char *soundName) { - Track *statusTrack = NULL; + Common::StackLock lock(_mutex); + Track *track = NULL; // If there's no name then don't try to get the status! if (strlen(soundName) == 0) return false; - statusTrack = findTrack(soundName); + track = findTrack(soundName); // Warn the user if the track was not found - if (statusTrack == NULL) { + if (track == NULL || !g_mixer->isSoundHandleActive(track->handle)) { // This debug warning should be "light" since this function gets called // on occassion to see if a sound has stopped yet if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL) - printf("Music track '%s' could not be found to get status, assume inactive.\n", soundName); + printf("Sound '%s' could not be found to get status, assume inactive.\n", soundName); return false; } - return statusTrack->handle.isActive(); + return true; } void Imuse::stopSound(const char *soundName) { + Common::StackLock lock(_mutex); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::stopSound(): SoundName %s\n", soundName); Track *removeTrack = NULL; removeTrack = findTrack(soundName); // Warn the user if the track was not found if (removeTrack == NULL) { if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) - warning("Music track '%s' could not be found to stop!", soundName); + warning("Sound track '%s' could not be found to stop!", soundName); return; } - removeTrack->toBeRemoved = true; + flushTrack(removeTrack); } void Imuse::stopAllSounds() { - for (;;) { - bool foundNotRemoved = false; - for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { - Track *track = _track[l]; - if (track->used) { - track->toBeRemoved = true; - foundNotRemoved = true; + Common::StackLock lock(_mutex); + if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_ALL) + printf("Imuse::stopAllSounds()\n"); + + for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { + Track *track = _track[l]; + if (track->used) { + g_mixer->stopHandle(track->handle); + if (track->soundDesc) { + _sound->closeSound(track->soundDesc); } + memset(track, 0, sizeof(Track)); } - if (!foundNotRemoved) - break; - flushTracks(); - g_driver->delayMillis(50); } } diff --git a/engine/imuse/imuse_sndmgr.cpp b/engine/imuse/imuse_sndmgr.cpp index ce55658e0a4..19e70dc6311 100644 --- a/engine/imuse/imuse_sndmgr.cpp +++ b/engine/imuse/imuse_sndmgr.cpp @@ -35,7 +35,7 @@ ImuseSndMgr::ImuseSndMgr() { for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { - memset(&_sounds[l], 0, sizeof(SoundStruct)); + memset(&_sounds[l], 0, sizeof(SoundDesc)); } } @@ -72,10 +72,10 @@ void ImuseSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps) { } while (tag != MKID_BE('DATA')); } -void ImuseSndMgr::parseSoundHeader(byte *ptr, SoundStruct *sound, int &headerSize) { +void ImuseSndMgr::parseSoundHeader(byte *ptr, SoundDesc *sound, int &headerSize) { if (READ_UINT32(ptr) == MKID('RIFF')) { - sound->region = (Region *)malloc(sizeof(Region)); - sound->jump = (Jump *)malloc(0); + sound->region = new Region[1]; + sound->jump = new Jump[0]; sound->numJumps = 0; sound->numRegions = 1; sound->region[0].offset = 0; @@ -96,8 +96,8 @@ void ImuseSndMgr::parseSoundHeader(byte *ptr, SoundStruct *sound, int &headerSiz sound->numRegions = 0; sound->numJumps = 0; countElements(ptr, sound->numRegions, sound->numJumps); - sound->region = (Region *)malloc(sizeof(Region) * sound->numRegions); - sound->jump = (Jump *)malloc(sizeof(Jump) * sound->numJumps); + sound->region = new Region [sound->numRegions]; + sound->jump = new Jump [sound->numJumps]; do { tag = READ_BE_UINT32(ptr); ptr += 4; @@ -147,7 +147,7 @@ void ImuseSndMgr::parseSoundHeader(byte *ptr, SoundStruct *sound, int &headerSiz } } -ImuseSndMgr::SoundStruct *ImuseSndMgr::allocSlot() { +ImuseSndMgr::SoundDesc *ImuseSndMgr::allocSlot() { for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { if (!_sounds[l].inUse) { _sounds[l].inUse = true; @@ -158,12 +158,12 @@ ImuseSndMgr::SoundStruct *ImuseSndMgr::allocSlot() { return NULL; } -ImuseSndMgr::SoundStruct *ImuseSndMgr::openSound(const char *soundName, int volGroupId) { +ImuseSndMgr::SoundDesc *ImuseSndMgr::openSound(const char *soundName, int volGroupId) { const char *extension = soundName + std::strlen(soundName) - 3; byte *ptr = NULL; int headerSize = 0; - SoundStruct *sound = allocSlot(); + SoundDesc *sound = allocSlot(); if (!sound) { error("ImuseSndMgr::openSound() Can't alloc free sound slot"); } @@ -197,94 +197,94 @@ ImuseSndMgr::SoundStruct *ImuseSndMgr::openSound(const char *soundName, int volG return sound; } -void ImuseSndMgr::closeSound(SoundStruct *soundHandle) { - assert(checkForProperHandle(soundHandle)); +void ImuseSndMgr::closeSound(SoundDesc *sound) { + assert(checkForProperHandle(sound)); - if (soundHandle->mcmpMgr) { - delete soundHandle->mcmpMgr; - soundHandle->mcmpMgr = NULL; + if (sound->mcmpMgr) { + delete sound->mcmpMgr; + sound->mcmpMgr = NULL; } - if (soundHandle->blockRes) { - delete soundHandle->blockRes; - soundHandle->blockRes = NULL; + if (sound->blockRes) { + delete sound->blockRes; + sound->blockRes = NULL; } - if (soundHandle->region) { - free(soundHandle->region); - soundHandle->region = NULL; + if (sound->region) { + delete[] sound->region; + sound->region = NULL; } - if (soundHandle->jump) { - free(soundHandle->jump); - soundHandle->jump = NULL; + if (sound->jump) { + delete[] sound->jump; + sound->jump = NULL; } - memset(soundHandle, 0, sizeof(SoundStruct)); + memset(sound, 0, sizeof(SoundDesc)); } -ImuseSndMgr::SoundStruct *ImuseSndMgr::cloneSound(SoundStruct *soundHandle) { - assert(checkForProperHandle(soundHandle)); +ImuseSndMgr::SoundDesc *ImuseSndMgr::cloneSound(SoundDesc *sound) { + assert(checkForProperHandle(sound)); - return openSound(soundHandle->name, soundHandle->volGroupId); + return openSound(sound->name, sound->volGroupId); } -bool ImuseSndMgr::checkForProperHandle(SoundStruct *soundHandle) { - if (!soundHandle) +bool ImuseSndMgr::checkForProperHandle(SoundDesc *sound) { + if (!sound) return false; for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { - if (soundHandle == &_sounds[l]) + if (sound == &_sounds[l]) return true; } return false; } -int ImuseSndMgr::getFreq(SoundStruct *soundHandle) { - assert(checkForProperHandle(soundHandle)); - return soundHandle->freq; +int ImuseSndMgr::getFreq(SoundDesc *sound) { + assert(checkForProperHandle(sound)); + return sound->freq; } -int ImuseSndMgr::getBits(SoundStruct *soundHandle) { - assert(checkForProperHandle(soundHandle)); - return soundHandle->bits; +int ImuseSndMgr::getBits(SoundDesc *sound) { + assert(checkForProperHandle(sound)); + return sound->bits; } -int ImuseSndMgr::getChannels(SoundStruct *soundHandle) { - assert(checkForProperHandle(soundHandle)); - return soundHandle->channels; +int ImuseSndMgr::getChannels(SoundDesc *sound) { + assert(checkForProperHandle(sound)); + return sound->channels; } -bool ImuseSndMgr::isEndOfRegion(SoundStruct *soundHandle, int region) { - assert(checkForProperHandle(soundHandle)); - assert(region >= 0 && region < soundHandle->numRegions); - return soundHandle->endFlag; +bool ImuseSndMgr::isEndOfRegion(SoundDesc *sound, int region) { + assert(checkForProperHandle(sound)); + assert(region >= 0 && region < sound->numRegions); + return sound->endFlag; } -int ImuseSndMgr::getNumRegions(SoundStruct *soundHandle) { - assert(checkForProperHandle(soundHandle)); - return soundHandle->numRegions; +int ImuseSndMgr::getNumRegions(SoundDesc *sound) { + assert(checkForProperHandle(sound)); + return sound->numRegions; } -int ImuseSndMgr::getNumJumps(SoundStruct *soundHandle) { - assert(checkForProperHandle(soundHandle)); - return soundHandle->numJumps; +int ImuseSndMgr::getNumJumps(SoundDesc *sound) { + assert(checkForProperHandle(sound)); + return sound->numJumps; } -int ImuseSndMgr::getRegionOffset(SoundStruct *soundHandle, int region) { - assert(checkForProperHandle(soundHandle)); - assert(region >= 0 && region < soundHandle->numRegions); - return soundHandle->region[region].offset; +int ImuseSndMgr::getRegionOffset(SoundDesc *sound, int region) { + assert(checkForProperHandle(sound)); + assert(region >= 0 && region < sound->numRegions); + return sound->region[region].offset; } -int ImuseSndMgr::getJumpIdByRegionAndHookId(SoundStruct *soundHandle, int region, int hookId) { - assert(checkForProperHandle(soundHandle)); - assert(region >= 0 && region < soundHandle->numRegions); - int32 offset = soundHandle->region[region].offset; - for (int l = 0; l < soundHandle->numJumps; l++) { - if (offset == soundHandle->jump[l].offset) { - if (soundHandle->jump[l].hookId == hookId) +int ImuseSndMgr::getJumpIdByRegionAndHookId(SoundDesc *sound, int region, int hookId) { + assert(checkForProperHandle(sound)); + assert(region >= 0 && region < sound->numRegions); + int32 offset = sound->region[region].offset; + for (int l = 0; l < sound->numJumps; l++) { + if (offset == sound->jump[l].offset) { + if (sound->jump[l].hookId == hookId) return l; } } @@ -292,12 +292,12 @@ int ImuseSndMgr::getJumpIdByRegionAndHookId(SoundStruct *soundHandle, int region return -1; } -int ImuseSndMgr::getRegionIdByJumpId(SoundStruct *soundHandle, int jumpId) { - assert(checkForProperHandle(soundHandle)); - assert(jumpId >= 0 && jumpId < soundHandle->numJumps); - int32 dest = soundHandle->jump[jumpId].dest; - for (int l = 0; l < soundHandle->numRegions; l++) { - if (dest == soundHandle->region[l].offset) { +int ImuseSndMgr::getRegionIdByJumpId(SoundDesc *sound, int jumpId) { + assert(checkForProperHandle(sound)); + assert(jumpId >= 0 && jumpId < sound->numJumps); + int32 dest = sound->jump[jumpId].dest; + for (int l = 0; l < sound->numRegions; l++) { + if (dest == sound->region[l].offset) { return l; } } @@ -305,38 +305,38 @@ int ImuseSndMgr::getRegionIdByJumpId(SoundStruct *soundHandle, int jumpId) { return -1; } -int ImuseSndMgr::getJumpHookId(SoundStruct *soundHandle, int number) { - assert(checkForProperHandle(soundHandle)); - assert(number >= 0 && number < soundHandle->numJumps); - return soundHandle->jump[number].hookId; +int ImuseSndMgr::getJumpHookId(SoundDesc *sound, int number) { + assert(checkForProperHandle(sound)); + assert(number >= 0 && number < sound->numJumps); + return sound->jump[number].hookId; } -int ImuseSndMgr::getJumpFade(SoundStruct *soundHandle, int number) { - assert(checkForProperHandle(soundHandle)); - assert(number >= 0 && number < soundHandle->numJumps); - return soundHandle->jump[number].fadeDelay; +int ImuseSndMgr::getJumpFade(SoundDesc *sound, int number) { + assert(checkForProperHandle(sound)); + assert(number >= 0 && number < sound->numJumps); + return sound->jump[number].fadeDelay; } -int32 ImuseSndMgr::getDataFromRegion(SoundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size) { - assert(checkForProperHandle(soundHandle)); +int32 ImuseSndMgr::getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size) { + assert(checkForProperHandle(sound)); assert(buf && offset >= 0 && size >= 0); - assert(region >= 0 && region < soundHandle->numRegions); + assert(region >= 0 && region < sound->numRegions); - int32 region_offset = soundHandle->region[region].offset; - int32 region_length = soundHandle->region[region].length; + int32 region_offset = sound->region[region].offset; + int32 region_length = sound->region[region].length; if (offset + size > region_length) { size = region_length - offset; - soundHandle->endFlag = true; + sound->endFlag = true; } else { - soundHandle->endFlag = false; + sound->endFlag = false; } - if (soundHandle->mcmpData) { - size = soundHandle->mcmpMgr->decompressSample(region_offset + offset, size, buf); + if (sound->mcmpData) { + size = sound->mcmpMgr->decompressSample(region_offset + offset, size, buf); } else { - *buf = (byte *)malloc(size); - memcpy(*buf, soundHandle->resPtr + region_offset + offset, size); + *buf = new byte[size]; + memcpy(*buf, sound->resPtr + region_offset + offset, size); } return size; diff --git a/engine/imuse/imuse_sndmgr.h b/engine/imuse/imuse_sndmgr.h index 69587a4f365..2ca070d07b7 100644 --- a/engine/imuse/imuse_sndmgr.h +++ b/engine/imuse/imuse_sndmgr.h @@ -64,7 +64,7 @@ private: public: - struct SoundStruct { + struct SoundDesc { uint16 freq; // frequency byte channels; // stereo or mono byte bits; // 8, 12, 16 @@ -77,6 +77,7 @@ public: char name[32]; McmpMgr *mcmpMgr; Block *blockRes; + int type; int volGroupId; byte *resPtr; bool mcmpData; @@ -84,11 +85,11 @@ public: private: - SoundStruct _sounds[MAX_IMUSE_SOUNDS]; + SoundDesc _sounds[MAX_IMUSE_SOUNDS]; - bool checkForProperHandle(SoundStruct *soundHandle); - SoundStruct *allocSlot(); - void parseSoundHeader(byte *ptr, SoundStruct *sound, int &headerSize); + bool checkForProperHandle(SoundDesc *soundDesc); + SoundDesc *allocSlot(); + void parseSoundHeader(byte *ptr, SoundDesc *sound, int &headerSize); void countElements(byte *ptr, int &numRegions, int &numJumps); public: @@ -96,23 +97,23 @@ public: ImuseSndMgr(); ~ImuseSndMgr(); - SoundStruct *openSound(const char *soundName, int volGroupId); - void closeSound(SoundStruct *soundHandle); - SoundStruct *cloneSound(SoundStruct *soundHandle); + SoundDesc *openSound(const char *soundName, int volGroupId); + void closeSound(SoundDesc *sound); + SoundDesc *cloneSound(SoundDesc *sound); - int getFreq(SoundStruct *soundHandle); - int getBits(SoundStruct *soundHandle); - int getChannels(SoundStruct *soundHandle); - bool isEndOfRegion(SoundStruct *soundHandle, int region); - int getNumRegions(SoundStruct *soundHandle); - int getNumJumps(SoundStruct *soundHandle); - int getRegionOffset(SoundStruct *soundHandle, int region); - int getJumpIdByRegionAndHookId(SoundStruct *soundHandle, int region, int hookId); - int getRegionIdByJumpId(SoundStruct *soundHandle, int jumpId); - int getJumpHookId(SoundStruct *soundHandle, int number); - int getJumpFade(SoundStruct *soundHandle, int number); + int getFreq(SoundDesc *sound); + int getBits(SoundDesc *sound); + int getChannels(SoundDesc *sound); + bool isEndOfRegion(SoundDesc *sound, int region); + int getNumRegions(SoundDesc *sound); + int getNumJumps(SoundDesc *sound); + int getRegionOffset(SoundDesc *sound, int region); + int getJumpIdByRegionAndHookId(SoundDesc *sound, int region, int hookId); + int getRegionIdByJumpId(SoundDesc *sound, int jumpId); + int getJumpHookId(SoundDesc *sound, int number); + int getJumpFade(SoundDesc *sound, int number); - int32 getDataFromRegion(SoundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size); + int32 getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size); }; #endif diff --git a/engine/imuse/imuse_tables.h b/engine/imuse/imuse_tables.h new file mode 100644 index 00000000000..9d06d6ee101 --- /dev/null +++ b/engine/imuse/imuse_tables.h @@ -0,0 +1,49 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + * $URL$ + * $Id$ + * + */ + +#ifndef IMUSE_TABLES_H +#define IMUSE_TABLES_H + +#include "common/sys.h" +#include "common/platform.h" +#include "common/debug.h" + +#include "engine/lua.h" + +#include "mixer/mixer.h" +#include "mixer/audiostream.h" + +#include "engine/imuse/imuse_sndmgr.h" +#include "engine/imuse/imuse_mcmp_mgr.h" + +struct ImuseTable { + byte opcode; + int16 soundId; + byte atribPos; + byte hookId; + int16 fadeOut60TicksDelay; + byte volume; + byte pan; + char filename[32]; +}; + +#endif diff --git a/engine/imuse/imuse_track.cpp b/engine/imuse/imuse_track.cpp index 71826454745..5006c016371 100644 --- a/engine/imuse/imuse_track.cpp +++ b/engine/imuse/imuse_track.cpp @@ -23,6 +23,7 @@ #include "common/sys.h" #include "common/platform.h" #include "common/debug.h" +#include "common/mutex.h" #include "engine/backend/driver.h" @@ -31,6 +32,7 @@ #include "engine/imuse/imuse.h" #include "engine/imuse/imuse_sndmgr.h" +#include "engine/imuse/imuse_track.h" int Imuse::allocSlot(int priority) { int l, lowest_priority = 127; @@ -39,35 +41,46 @@ int Imuse::allocSlot(int priority) { // allocSlot called by startSound so no locking is necessary for (l = 0; l < MAX_IMUSE_TRACKS; l++) { if (!_track[l]->used) { - return l; // Found an unused track + trackId = l; + break; } } - warning("Imuse::startSound(): All slots are full"); - for (l = 0; l < MAX_IMUSE_TRACKS; l++) { - Track *track = _track[l]; - if (track->used && !track->toBeRemoved && lowest_priority > track->priority) { - lowest_priority = track->priority; - trackId = l; + if (trackId == -1) { + warning("Imuse::startSound(): All slots are full"); + for (l = 0; l < MAX_IMUSE_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && + (lowest_priority > track->priority)) { + lowest_priority = track->priority; + trackId = l; + } + } + if (lowest_priority <= priority) { + assert(trackId != -1); + Track *track = _track[trackId]; + + // Stop the track immediately + g_mixer->stopHandle(track->handle); + if (track->soundDesc) { + _sound->closeSound(track->soundDesc); + } + + // Mark it as unused + memset(track, 0, sizeof(Track)); + } else { + return -1; } - } - if (lowest_priority <= priority) { - assert(trackId != -1); - _track[trackId]->toBeRemoved = true; - warning("Imuse::startSound(): Removed sound %s from track %d", _track[trackId]->soundName, trackId); - } else { - warning("Imuse::startSound(): Priority sound too low"); - return -1; } return trackId; } -bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority) { +bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority, Track *otherTrack) { + Common::StackLock lock(_mutex); Track *track = NULL; - int i, l = -1; - - StackLock lock(_mutex); + int i; + // If the track is already playing then there is absolutely no // reason to start it again, the existing track should be modified // instead of starting a new copy of the track @@ -79,111 +92,73 @@ bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int vo return true; } } + // Priority Level 127 appears to mean "load but don't play", so // within our paradigm this is a much lower priority than everything // else we're doing if (priority == 127) priority = -1; - l = allocSlot(priority); + + int l = allocSlot(priority); if (l == -1) { - warning("Imuse::startSound(): Can't start sound - no free slots"); - return false; - } - track = _track[l]; - i = 5; - // At this time it is inappropriate to assume that this will always - // succeed, so set a limit of 5 tries on running flushTracks - while (track->used && i > 0) { - // The designated track is not yet available. So, we call flushTracks() - // to get it processed (and thus made ready for us). Since the actual - // processing is done by another thread, we also call parseEvents to - // give it some time (and to avoid busy waiting/looping). - flushTracks(); - i--; - } - if (i == 0) { - if (debugLevel == DEBUG_IMUSE || debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL) - warning("Imuse::startSound(): flushTracks was unable to free up a track for %s!", soundName); + warning("Imuse::startSound() Can't start sound - no free slots"); return false; } + track = _track[l]; + // Reset the track + memset(track, 0, sizeof(Track)); + track->pan = pan * 1000; - track->panFadeDest = 0; - track->panFadeStep = 0; - track->panFadeDelay = 0; - track->panFadeUsed = false; track->vol = volume * 1000; - track->volFadeDest = 0; - track->volFadeStep = 0; - track->volFadeDelay = 0; - track->volFadeUsed = false; - track->started = false; track->volGroupId = volGroupId; track->curHookId = hookId; track->priority = priority; track->curRegion = -1; - track->dataOffset = 0; - track->regionOffset = 0; - track->mixerFlags = 0; - track->mixerPan = 0; - track->mixerVol = volume; - track->toBeRemoved = false; - track->readyToRemove = false; int bits = 0, freq = 0, channels = 0; strcpy(track->soundName, soundName); - track->soundHandle = _sound->openSound(soundName, volGroupId); + track->soundDesc = _sound->openSound(soundName, volGroupId); - if (track->soundHandle == NULL) + if (!track->soundDesc) return false; - bits = _sound->getBits(track->soundHandle); - channels = _sound->getChannels(track->soundHandle); - freq = _sound->getFreq(track->soundHandle); + bits = _sound->getBits(track->soundDesc); + channels = _sound->getChannels(track->soundDesc); + freq = _sound->getFreq(track->soundDesc); assert(bits == 8 || bits == 12 || bits == 16); assert(channels == 1 || channels == 2); assert(0 < freq && freq <= 65535); - track->iteration = freq * channels * 2; - track->mixerFlags = SoundMixer::FLAG_16BITS; + track->feedSize = freq * channels * 2; + track->mixerFlags = kFlag16Bits; if (channels == 2) - track->mixerFlags |= SoundMixer::FLAG_STEREO | SoundMixer::FLAG_REVERSE_STEREO; + track->mixerFlags |= kFlagStereo | kFlagReverseStereo; - pan = track->pan / 1000; - pan = (pan != 64) ? 2 * pan - 127 : 0; - volume = track->vol / 1000; + if (otherTrack && otherTrack->used && !otherTrack->toBeRemoved) { + track->curRegion = otherTrack->curRegion; + track->dataOffset = otherTrack->dataOffset; + track->regionOffset = otherTrack->regionOffset; + } - if (track->volGroupId == 1) - volume = (volume * _volVoice) / 128; - if (track->volGroupId == 2) - volume = (volume * _volSfx) / 128; - if (track->volGroupId == 3) - volume = (volume * _volMusic) / 128; - - track->mixerPan = pan; - track->mixerVol = volume; - - // setup 1 second stream wrapped buffer - int32 streamBufferSize = track->iteration; - track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize); - g_mixer->playInputStream(&track->handle, track->stream, false, -1, track->mixerVol, track->mixerPan, false); - track->started = true; + track->stream = Audio::makeAppendableAudioStream(freq, makeMixerFlags(track->mixerFlags)); + g_mixer->playInputStream(track->getType(), &track->handle, track->stream, -1, track->getVol(), track->getPan()); track->used = true; return true; } -Imuse::Track *Imuse::findTrack(const char *soundName) { - StackLock lock(_mutex); +Track *Imuse::findTrack(const char *soundName) { for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *track = _track[l]; // Since the audio (at least for Eva's keystrokes) can be referenced // two ways: keyboard.IMU and keyboard.imu, make a case insensitive // search for the track to make sure we can find it - if (strlen(track->soundName) != 0 && strcasecmp(track->soundName, soundName) == 0) { + if (track->used && !track->toBeRemoved + && strlen(track->soundName) != 0 && strcasecmp(track->soundName, soundName) == 0) { return track; } } @@ -191,6 +166,7 @@ Imuse::Track *Imuse::findTrack(const char *soundName) { } void Imuse::setPriority(const char *soundName, int priority) { + Common::StackLock lock(_mutex); Track *changeTrack = NULL; assert ((priority >= 0) && (priority <= 127)); @@ -204,6 +180,7 @@ void Imuse::setPriority(const char *soundName, int priority) { } void Imuse::setVolume(const char *soundName, int volume) { + Common::StackLock lock(_mutex); Track *changeTrack; changeTrack = findTrack(soundName); @@ -215,17 +192,19 @@ void Imuse::setVolume(const char *soundName, int volume) { } void Imuse::setPan(const char *soundName, int pan) { + Common::StackLock lock(_mutex); Track *changeTrack; changeTrack = findTrack(soundName); if (changeTrack == NULL) { - warning("Unable to find track '%s' to change volume!", soundName); + warning("Unable to find track '%s' to change pan!", soundName); return; } - changeTrack->pan = pan; + changeTrack->pan = pan * 1000; } int Imuse::getVolume(const char *soundName) { + Common::StackLock lock(_mutex); Track *getTrack; getTrack = findTrack(soundName); @@ -237,6 +216,7 @@ int Imuse::getVolume(const char *soundName) { } void Imuse::setHookId(const char *soundName, int hookId) { + Common::StackLock lock(_mutex); Track *changeTrack; changeTrack = findTrack(soundName); @@ -248,12 +228,12 @@ void Imuse::setHookId(const char *soundName, int hookId) { } int Imuse::getCountPlayedTracks(const char *soundName) { + Common::StackLock lock(_mutex); int count = 0; - StackLock lock(_mutex); for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *track = _track[l]; - if (track->used && !track->toBeRemoved && (strcmp(track->soundName, soundName) == 0)) { + if (track->used && !track->toBeRemoved && (strcasecmp(track->soundName, soundName) == 0)) { count++; } } @@ -262,6 +242,7 @@ int Imuse::getCountPlayedTracks(const char *soundName) { } void Imuse::selectVolumeGroup(const char *soundName, int volGroupId) { + Common::StackLock lock(_mutex); Track *changeTrack; assert((volGroupId >= 1) && (volGroupId <= 4)); @@ -277,6 +258,7 @@ void Imuse::selectVolumeGroup(const char *soundName, int volGroupId) { } void Imuse::setFadeVolume(const char *soundName, int destVolume, int duration) { + Common::StackLock lock(_mutex); Track *changeTrack; changeTrack = findTrack(soundName); @@ -291,6 +273,7 @@ void Imuse::setFadeVolume(const char *soundName, int destVolume, int duration) { } void Imuse::setFadePan(const char *soundName, int destPan, int duration) { + Common::StackLock lock(_mutex); Track *changeTrack; changeTrack = findTrack(soundName); @@ -305,7 +288,7 @@ void Imuse::setFadePan(const char *soundName, int destPan, int duration) { } char *Imuse::getCurMusicSoundName() { - StackLock lock(_mutex); + Common::StackLock lock(_mutex); for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *track = _track[l]; if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { @@ -315,65 +298,90 @@ char *Imuse::getCurMusicSoundName() { return NULL; } +int Imuse::getCurMusicPan() { + Common::StackLock lock(_mutex); + for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + return track->pan / 1000; + } + } + return NULL; +} + +int Imuse::getCurMusicVol() { + Common::StackLock lock(_mutex); + for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + return track->vol / 1000; + } + } + return NULL; +} + void Imuse::fadeOutMusic(int duration) { - StackLock lock(_mutex); + Common::StackLock lock(_mutex); for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { Track *track = _track[l]; if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { cloneToFadeOutTrack(track, duration); - track->toBeRemoved = true; + flushTrack(track); + return; } } } -Imuse::Track *Imuse::cloneToFadeOutTrack(Track *track, int fadeDelay) { - assert(track); - Track *fadeTrack = 0; +void Imuse::fadeOutMusicAndStartNew(int fadeDelay, const char *filename, int hookId, int vol, int pan) { + Common::StackLock lock(_mutex); - { - StackLock lock(_mutex); - for (int l = MAX_IMUSE_TRACKS; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) { - if (!_track[l]->used) { - fadeTrack = _track[l]; - break; - } + for (int l = 0; l < MAX_IMUSE_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + startMusicWithOtherPos(filename, 0, vol, pan, track); + cloneToFadeOutTrack(track, fadeDelay); + flushTrack(track); + break; } - if (fadeTrack == 0) - error("Imuse::cloneTofadeTrackId() Can't find free fade track"); + } +} - fadeTrack->pan = track->pan; - fadeTrack->vol = track->vol; - fadeTrack->volGroupId = track->volGroupId; - fadeTrack->priority = track->priority; - fadeTrack->dataOffset = track->dataOffset; - fadeTrack->regionOffset = track->regionOffset; - fadeTrack->curRegion = track->curRegion; - fadeTrack->curHookId = track->curHookId; - fadeTrack->iteration = track->iteration; - fadeTrack->mixerFlags = track->mixerFlags; - fadeTrack->mixerVol = track->mixerVol; - fadeTrack->mixerPan = track->mixerPan; - fadeTrack->toBeRemoved = track->toBeRemoved; - fadeTrack->readyToRemove = track->readyToRemove; - fadeTrack->started = track->started; - strcpy(fadeTrack->soundName, track->soundName); - fadeTrack->soundHandle = _sound->cloneSound(track->soundHandle); - assert(fadeTrack->soundHandle); - fadeTrack->volFadeDelay = fadeDelay; - fadeTrack->volFadeDest = 0; - fadeTrack->volFadeStep = (fadeTrack->volFadeDest - fadeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * fadeDelay); - fadeTrack->volFadeUsed = true; - fadeTrack->panFadeDelay = 0; - fadeTrack->panFadeDest = 0; - fadeTrack->panFadeStep = 0; - fadeTrack->panFadeUsed = false; +Track *Imuse::cloneToFadeOutTrack(Track *track, int fadeDelay) { + assert(track); + Track *fadeTrack; + + if (track->toBeRemoved) { + error("cloneToFadeOutTrack: Tried to clone a track to be removed, please bug report"); + return NULL; } - // setup 1 second stream wrapped buffer - int32 streamBufferSize = fadeTrack->iteration; - fadeTrack->stream = makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundHandle), fadeTrack->mixerFlags, streamBufferSize); - g_mixer->playInputStream(&fadeTrack->handle, fadeTrack->stream, false, -1, fadeTrack->vol / 1000, fadeTrack->pan, false); - fadeTrack->started = true; + assert(track->trackId < MAX_IMUSE_TRACKS); + fadeTrack = _track[track->trackId + MAX_IMUSE_TRACKS]; + + if (fadeTrack->used) { + flushTrack(fadeTrack); + g_mixer->stopHandle(fadeTrack->handle); + } + + // Clone the settings of the given track + memcpy(fadeTrack, track, sizeof(Track)); + fadeTrack->trackId = track->trackId + MAX_IMUSE_TRACKS; + + // Clone the sound. + // leaving bug number for now #1635361 + ImuseSndMgr::SoundDesc *soundDesc = _sound->cloneSound(track->soundDesc); + assert(soundDesc); + track->soundDesc = soundDesc; + + // Set the volume fading parameters to indicate a fade out + fadeTrack->volFadeDelay = fadeDelay; + fadeTrack->volFadeDest = 0; + fadeTrack->volFadeStep = (fadeTrack->volFadeDest - fadeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * fadeDelay); + fadeTrack->volFadeUsed = true; + + // Create an appendable output buffer + fadeTrack->stream = Audio::makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundDesc), makeMixerFlags(fadeTrack->mixerFlags)); + g_mixer->playInputStream(track->getType(), &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->getVol(), fadeTrack->getPan()); fadeTrack->used = true; return fadeTrack; diff --git a/engine/imuse/imuse_track.h b/engine/imuse/imuse_track.h new file mode 100644 index 00000000000..333db107f28 --- /dev/null +++ b/engine/imuse/imuse_track.h @@ -0,0 +1,98 @@ +/* Residual - Virtual machine to run LucasArts' 3D adventure games + * Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + * $URL$ + * $Id$ + * + */ + +#ifndef IMUSE_TRACK_H +#define IMUSE_TRACK_H + +#include "common/sys.h" +#include "common/platform.h" +#include "common/debug.h" + +#include "engine/lua.h" + +#include "mixer/mixer.h" +#include "mixer/audiostream.h" + +#include "engine/imuse/imuse_sndmgr.h" +#include "engine/imuse/imuse_mcmp_mgr.h" + +enum { + kFlagUnsigned = 1 << 0, + kFlag16Bits = 1 << 1, + kFlagLittleEndian = 1 << 2, + kFlagStereo = 1 << 3, + kFlagReverseStereo = 1 << 4 +}; + +struct Track { + int trackId; + + int32 pan; + int32 panFadeDest; + int32 panFadeStep; + int32 panFadeDelay; + bool panFadeUsed; + int32 vol; + int32 volFadeDest; + int32 volFadeStep; + int32 volFadeDelay; + bool volFadeUsed; + + char soundName[32]; + bool used; + bool toBeRemoved; + int32 priority; + int32 regionOffset; + int32 dataOffset; + int32 curRegion; + int32 curHookId; + int32 volGroupId; + int32 feedSize; + int32 mixerFlags; + + ImuseSndMgr::SoundDesc *soundDesc; + Audio::SoundHandle handle; + Audio::AppendableAudioStream *stream; + + Track() : used(false), stream(NULL) { + soundName[0] = 0; + } + + int getPan() const { return (pan != 6400) ? 2 * (pan / 1000) - 127 : 0; } + int getVol() const { return vol / 1000; } + Audio::Mixer::SoundType getType() const { + Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType; + if (volGroupId == IMUSE_VOLGRP_VOICE) + type = Audio::Mixer::kSpeechSoundType; + else if (volGroupId == IMUSE_VOLGRP_SFX) + type = Audio::Mixer::kSFXSoundType; + else if (volGroupId == IMUSE_VOLGRP_MUSIC) + type = Audio::Mixer::kMusicSoundType; + else if (volGroupId == IMUSE_VOLGRP_BGND) + type = Audio::Mixer::kPlainSoundType; + else if (volGroupId == IMUSE_VOLGRP_ACTION) + type = Audio::Mixer::kPlainSoundType; + return type; + } +}; + +#endif diff --git a/engine/lua.cpp b/engine/lua.cpp index a8db3a3d0a6..6cbb899e529 100644 --- a/engine/lua.cpp +++ b/engine/lua.cpp @@ -42,6 +42,8 @@ #include "engine/imuse/imuse.h" +#include "mixer/mixer.h" + #include #include #include @@ -1880,7 +1882,7 @@ static void ImStartSound() { group = check_int(3); // Start the sound with the appropriate settings - if (g_imuse->startSound(soundName, group, 0, 127, 0, priority)) { + if (g_imuse->startSound(soundName, group, 0, 127, 0, priority, NULL)) { lua_pushstring(soundName); } else { // Allow soft failing when loading sounds, hard failing when not @@ -1929,32 +1931,32 @@ static void ImSetVoiceEffect() { static void ImSetMusicVol() { DEBUG_FUNCTION(); - g_imuse->setGroupMusicVolume(check_int(1)); + g_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, check_int(1)); } static void ImGetMusicVol() { DEBUG_FUNCTION(); - lua_pushnumber(g_imuse->getGroupMusicVolume()); + lua_pushnumber(g_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType)); } static void ImSetVoiceVol() { DEBUG_FUNCTION(); - g_imuse->setGroupVoiceVolume(check_int(1)); + g_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, check_int(1)); } static void ImGetVoiceVol() { DEBUG_FUNCTION(); - lua_pushnumber(g_imuse->getGroupVoiceVolume()); + lua_pushnumber(g_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType)); } static void ImSetSfxVol() { DEBUG_FUNCTION(); - g_imuse->setGroupSfxVolume(check_int(1)); + g_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, check_int(1)); } static void ImGetSfxVol() { DEBUG_FUNCTION(); - lua_pushnumber(g_imuse->getGroupSfxVolume()); + lua_pushnumber(g_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType)); } static void ImSetParam() { @@ -2961,7 +2963,7 @@ static void GetSaveGameImage() { filename = luaL_check_string(1); SaveGame *savedState = new SaveGame(filename, false); dataSize = savedState->beginSection('SIMG'); - data = (char *)malloc(dataSize); + data = new char[dataSize]; savedState->read(data, dataSize); screenshot = new Bitmap(data, width, height, "screenshot"); if (screenshot) { diff --git a/engine/main.cpp b/engine/main.cpp index ad188ffa155..0fd0bb4e5dd 100644 --- a/engine/main.cpp +++ b/engine/main.cpp @@ -58,6 +58,8 @@ enDebugLevels debugLevel = DEBUG_NONE; static bool g_lua_initialized = false; Driver *g_driver = NULL; +DefaultTimerManager *g_timer = NULL; +Audio::Mixer *g_mixer = NULL; static bool parseBoolStr(const char *val) { if (val == NULL || val[0] == 0) @@ -168,12 +170,18 @@ needshelp: g_driver = new DriverTinyGL(640, 480, 16, fullscreen); else g_driver = new DriverGL(640, 480, 24, fullscreen); + g_timer = new DefaultTimerManager(); + g_driver->setTimerCallback(); + g_mixer = new Audio::Mixer(); + g_driver->setSoundCallback(Audio::Mixer::mixCallback, g_mixer); + g_mixer->setReady(true); + g_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, 127); + g_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, Audio::Mixer::kMaxMixerVolume); + g_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, Audio::Mixer::kMaxMixerVolume); + g_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume); g_engine = new Engine(); g_resourceloader = new ResourceLoader(); g_localizer = new Localizer(); - g_mixer = new SoundMixer(); - g_mixer->setVolume(255); - g_timer = new Timer(); g_smush = new Smush(); g_imuse = new Imuse(20); @@ -226,6 +234,8 @@ void quit() { delete g_registry; g_registry = NULL; } + if (g_driver) + g_driver->clearTimerCallback(); delete g_smush; g_smush = NULL; delete g_imuse; diff --git a/engine/savegame.cpp b/engine/savegame.cpp index ad517308fde..259f0d9667d 100644 --- a/engine/savegame.cpp +++ b/engine/savegame.cpp @@ -68,17 +68,17 @@ uint32 SaveGame::beginSection(uint32 sectionTag) { error("Tried to begin a new save game section with ending old section!"); _currentSection = sectionTag; _sectionSize = 0; - _sectionBuffer = (char *) malloc(_sectionSize); + _sectionBuffer = new char[_sectionSize]; if (!_saving) { uint32 tag = 0; while (tag != sectionTag) { - free(_sectionBuffer); + delete[] _sectionBuffer; gzread(_fileHandle, &tag, sizeof(uint32)); if (tag == SAVEGAME_FOOTERTAG) error("Unable to find requested section of savegame!"); gzread(_fileHandle, &_sectionSize, sizeof(uint32)); - _sectionBuffer = (char *)malloc(_sectionSize); + _sectionBuffer = new char[_sectionSize]; gzread(_fileHandle, _sectionBuffer, _sectionSize); } } @@ -94,8 +94,8 @@ void SaveGame::endSection() { gzwrite(_fileHandle, &_sectionSize, sizeof(uint32)); gzwrite(_fileHandle, _sectionBuffer, _sectionSize); } - free(_sectionBuffer); - _currentSection = 0; + delete[] _sectionBuffer; + _currentSection = NULL; } void SaveGame::read(void *data, int size) { diff --git a/engine/smush/blocky16.cpp b/engine/smush/blocky16.cpp index 00474c173b0..c40a18b6045 100644 --- a/engine/smush/blocky16.cpp +++ b/engine/smush/blocky16.cpp @@ -624,7 +624,7 @@ void Blocky16::init(int width, int height) { // lol, byeruba, crushed, eldepot, heltrain, hostage // but for tb_kitty.snm 5700 bytes is needed _deltaSize = _frameSize * 3 + 5700; - _deltaBuf = (byte *)malloc(_deltaSize); + _deltaBuf = new byte[_deltaSize]; memset(_deltaBuf, 0, _deltaSize); _deltaBufs[0] = _deltaBuf; _deltaBufs[1] = _deltaBuf + _frameSize; @@ -632,8 +632,8 @@ void Blocky16::init(int width, int height) { } Blocky16::Blocky16() { - _tableBig = (byte *)malloc(99328); - _tableSmall = (byte *)malloc(32768); + _tableBig = new byte[99328]; + _tableSmall = new byte[32768]; memset(_tableBig, 0, 99328); memset(_tableSmall, 0, 32768); _deltaBuf = NULL; @@ -642,7 +642,7 @@ Blocky16::Blocky16() { void Blocky16::deinit() { _lastTableWidth = -1; if (_deltaBuf) { - free(_deltaBuf); + delete[] _deltaBuf; _deltaSize = 0; _deltaBuf = NULL; _deltaBufs[0] = NULL; @@ -653,11 +653,11 @@ void Blocky16::deinit() { Blocky16::~Blocky16() { deinit(); if (_tableBig) { - free(_tableBig); + delete[] _tableBig; _tableBig = NULL; } if (_tableSmall) { - free(_tableSmall); + delete[] _tableSmall; _tableSmall = NULL; } } diff --git a/engine/smush/smush.cpp b/engine/smush/smush.cpp index ede88c08101..811e14cd599 100644 --- a/engine/smush/smush.cpp +++ b/engine/smush/smush.cpp @@ -29,6 +29,7 @@ #include "engine/resource.h" #include "engine/engine.h" #include "engine/backend/driver.h" +#include "engine/imuse/imuse_track.h" #include "mixer/mixer.h" @@ -83,8 +84,8 @@ void Smush::init() { assert(!_internalBuffer); assert(!_externalBuffer); - _internalBuffer = (byte *)malloc(_width * _height * 2); - _externalBuffer = (byte *)malloc(_width * _height * 2); + _internalBuffer = new byte[_width * _height * 2]; + _externalBuffer = new byte[_width * _height * 2]; vimaInit(smushDestTable); g_timer->installTimerProc(&timerCallback, _speed, NULL); @@ -94,16 +95,16 @@ void Smush::deinit() { g_timer->removeTimerProc(&timerCallback); if (_internalBuffer) { - free(_internalBuffer); + delete[] _internalBuffer; _internalBuffer = NULL; } if (_externalBuffer) { - free(_externalBuffer); + delete[] _externalBuffer; _externalBuffer = NULL; } if (_videoLooping && _startPos != NULL) { - free(_startPos->tmpBuf); - free(_startPos); + delete[] _startPos->tmpBuf; + delete[] _startPos; _startPos = NULL; } if (_stream) { @@ -118,20 +119,22 @@ void Smush::deinit() { } void Smush::handleWave(const byte *src, uint32 size) { - int16 *dst = (int16 *)malloc(size * _channels * 2); + int16 *dst = new int16[size * _channels]; decompressVima(src, dst, size * _channels * 2, smushDestTable); - int flags = SoundMixer::FLAG_16BITS; + int flags = Audio::Mixer::FLAG_16BITS; if (_channels == 2) - flags |= SoundMixer::FLAG_STEREO; + flags |= Audio::Mixer::FLAG_STEREO; if (!_stream) { - _stream = makeAppendableAudioStream(_freq, flags, 500000); - g_mixer->playInputStream(&_soundHandle, _stream, true); + _stream = Audio::makeAppendableAudioStream(_freq, flags); + g_mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_soundHandle, _stream); + } + if (g_mixer->isReady()) { + _stream->queueBuffer((byte *)dst, size * _channels * 2); + } else { + delete[] dst; } - if (_stream) - _stream->append((byte *)dst, size * _channels * 2); - free(dst); } void Smush::handleFrame() { @@ -162,7 +165,7 @@ void Smush::handleFrame() { byte *data; size = _file.readUint32BE(); - data = (byte *)malloc(size); + data = new byte[size]; _file.read(data, size); anno = (char *)data; if (strncmp(anno, ANNO_HEADER, sizeof(ANNO_HEADER)-1) == 0) { @@ -185,13 +188,13 @@ void Smush::handleFrame() { if (debugLevel == DEBUG_SMUSH || debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL) printf("Announcement header not understood: %s\n", anno); } - free(anno); + delete[] anno; tag = _file.readUint32BE(); } assert(tag == MKID_BE('FRME')); size = _file.readUint32BE(); - byte *frame = (byte *)malloc(size); + byte *frame = new byte[size]; _file.read(frame, size); do { @@ -209,7 +212,7 @@ void Smush::handleFrame() { error("Smush::handleFrame() unknown tag"); } } while (pos < size); - free(frame); + delete[] frame; memcpy(_externalBuffer, _internalBuffer, _width * _height * 2); _updateNeeded = true; @@ -234,7 +237,7 @@ void Smush::handleFramesHeader() { tag = _file.readUint32BE(); assert(tag == MKID_BE('FLHD')); size = _file.readUint32BE(); - byte *f_header = (byte*)malloc(size); + byte *f_header = new byte[size]; _file.read(f_header, size); do { @@ -248,7 +251,7 @@ void Smush::handleFramesHeader() { error("Smush::handleFramesHeader() unknown tag"); } } while (pos < size); - free(f_header); + delete[] f_header; } bool Smush::setupAnim(const char *file, int x, int y) { @@ -267,7 +270,7 @@ bool Smush::setupAnim(const char *file, int x, int y) { assert(tag == MKID_BE('SHDR')); size = _file.readUint32BE(); - byte *s_header = (byte *)malloc(size); + byte *s_header = new byte[size]; _file.read(s_header, size); _nbframes = READ_LE_UINT32(s_header + 2); int width = READ_LE_UINT16(s_header + 8); @@ -301,7 +304,7 @@ bool Smush::setupAnim(const char *file, int x, int y) { } _videoLooping = SMUSH_LOOPMOVIE(flags); _startPos = NULL; // Set later - free(s_header); + delete[] s_header; return true; } @@ -347,10 +350,10 @@ struct SavePos *zlibFile::getPos() { warning("zlibFile::open() unable to find start position! %m"); return NULL; } - pos = (struct SavePos *) malloc(sizeof(struct SavePos)); + pos = new SavePos; pos->filePos = position; inflateCopy(&pos->streamBuf, &_stream); - pos->tmpBuf = (char *)calloc(1, BUFFER_SIZE); + pos->tmpBuf = new char[BUFFER_SIZE]; memcpy(pos->tmpBuf, _inBuf, BUFFER_SIZE); return pos; } @@ -446,7 +449,7 @@ void zlibFile::close() { } if (_inBuf) { - free(_inBuf); + delete[] _inBuf; _inBuf = NULL; } } diff --git a/engine/smush/smush.h b/engine/smush/smush.h index 897a77b96f5..6f0ebc2d284 100644 --- a/engine/smush/smush.h +++ b/engine/smush/smush.h @@ -70,8 +70,8 @@ private: int32 _nbframes; Blocky16 _blocky16; zlibFile _file; - PlayingSoundHandle _soundHandle; - AppendableAudioStream *_stream; + Audio::SoundHandle _soundHandle; + Audio::AppendableAudioStream *_stream; int32 _frame; bool _updateNeeded; diff --git a/engine/textobject.cpp b/engine/textobject.cpp index 0f303339b42..5d94dcb1064 100644 --- a/engine/textobject.cpp +++ b/engine/textobject.cpp @@ -139,7 +139,7 @@ void TextObject::createBitmap() { message += msg[i]; } _textObjectHandle = (Driver::TextObjectHandle **)malloc(sizeof(long) * _numberLines); - _bitmapWidthPtr = (int *)malloc(sizeof(int) * _numberLines); + _bitmapWidthPtr = new int[_numberLines]; for (int j = 0; j < _numberLines; j++) { int nextLinePos = message.find_first_of('\n'); @@ -188,13 +188,13 @@ void TextObject::destroyBitmap() { if (_textObjectHandle) { for (int i = 0; i < _numberLines; i++) { g_driver->destroyTextBitmap(_textObjectHandle[i]); - delete _textObjectHandle[i]; + delete[] _textObjectHandle[i]; } free(_textObjectHandle); _textObjectHandle = NULL; } if (_bitmapWidthPtr) { - free(_bitmapWidthPtr); + delete[] _bitmapWidthPtr; _bitmapWidthPtr = NULL; } }