mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 22:28:10 +00:00
added mixer code, handle smush sound, handle smush filenames in lua
This commit is contained in:
parent
929b13790b
commit
03abd1bb4c
@ -20,7 +20,6 @@
|
||||
#include "engine.h"
|
||||
#include "costume.h"
|
||||
#include "sound.h"
|
||||
#include "mixer.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <SDL.h>
|
||||
|
4
bits.h
4
bits.h
@ -36,6 +36,10 @@
|
||||
|
||||
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
|
||||
|
||||
template<typename T> inline T ABS (T x) { return (x>=0) ? x : -x; }
|
||||
template<typename T> inline T MIN (T a, T b) { return (a<b) ? a : b; }
|
||||
template<typename T> inline T MAX (T a, T b) { return (a>b) ? a : b; }
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#define scumm_stricmp stricmp
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "model.h"
|
||||
#include "lua.h"
|
||||
#include "sound.h"
|
||||
#include "mixer.h"
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
|
7
lua.cpp
7
lua.cpp
@ -25,7 +25,6 @@
|
||||
#include "color.h"
|
||||
#include "costume.h"
|
||||
#include "engine.h"
|
||||
#include "mixer.h"
|
||||
#include "sound.h"
|
||||
#include "smush.h"
|
||||
#include "textobject.h"
|
||||
@ -1023,12 +1022,14 @@ static void GetTextObjectDimensions() {
|
||||
|
||||
static void StartMovie() {
|
||||
Smush player;
|
||||
player.play("intro.snm", "");
|
||||
char *filename = lua_getstring(lua_getparam(1));
|
||||
player.play(filename, "");
|
||||
}
|
||||
|
||||
static void StartFullscreenMovie() {
|
||||
Smush player;
|
||||
player.play("intro.snm", "");
|
||||
char *filename = lua_getstring(lua_getparam(1));
|
||||
player.play(filename, "");
|
||||
}
|
||||
|
||||
// Stub function for builtin functions not yet implemented
|
||||
|
8
main.cpp
8
main.cpp
@ -25,7 +25,8 @@
|
||||
#include "lua.h"
|
||||
#include "registry.h"
|
||||
#include "engine.h"
|
||||
#include "mixer.h"
|
||||
#include "sound.h"
|
||||
#include "mixer/mixer.h"
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@ -52,6 +53,8 @@ static void saveRegistry() {
|
||||
}
|
||||
#endif
|
||||
|
||||
extern SoundMixer *g_mixer;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char GLDriver[1024];
|
||||
int i;
|
||||
@ -102,6 +105,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
g_mixer = new SoundMixer();
|
||||
Mixer::instance()->start();
|
||||
|
||||
lua_open();
|
||||
@ -154,5 +158,7 @@ int main(int argc, char *argv[]) {
|
||||
#endif
|
||||
Engine::instance()->mainLoop();
|
||||
|
||||
delete g_mixer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
440
mixer.cpp
440
mixer.cpp
@ -1,440 +0,0 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "mixer.h"
|
||||
#include "sound.h"
|
||||
#include "debug.h"
|
||||
#include <cstdlib>
|
||||
#include <SDL_audio.h>
|
||||
|
||||
class AudioLock {
|
||||
public:
|
||||
AudioLock() { SDL_LockAudio(); }
|
||||
~AudioLock() { SDL_UnlockAudio(); }
|
||||
};
|
||||
|
||||
struct imuseTableEntry {
|
||||
int stateNum;
|
||||
const char *filename;
|
||||
};
|
||||
|
||||
static const imuseTableEntry grimMusicTable[] = {
|
||||
{ 1001, "1001 - Manny's Office.IMC" },
|
||||
{ 1002, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1003, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1004, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1005, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1006, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1007, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1008, "1008 - Domino's Office.IMC" },
|
||||
{ 1009, "1009 - Copal's Office.IMC" },
|
||||
{ 1010, "1010 - Ledge.IMC" },
|
||||
{ 1011, "1011 - Roof.IMC" },
|
||||
{ 1020, "1020 - Tube Room.IMC" },
|
||||
{ 1021, "1021 - Brennis.IMC" },
|
||||
{ 1022, "1022 - Lobby.IMC" },
|
||||
{ 1023, "1023 - Packing Room.IMC" },
|
||||
{ 1030, "1030 - Garage.IMC" },
|
||||
{ 1031, "1031 - Glottis' Shop.IMC" },
|
||||
{ 1032, "1030 - Garage.IMC" },
|
||||
{ 1040, "1040 - Festival Wet.IMC" },
|
||||
{ 1041, "1041 - Festival Dry.IMC" },
|
||||
{ 1042, "1041 - Festival Dry.IMC" },
|
||||
{ 1043, "1041 - Festival Dry.IMC" },
|
||||
{ 1044, "1040 - Festival Wet.IMC" },
|
||||
{ 1050, "1050 - Headquarters.IMC" },
|
||||
{ 1060, "1060 - Real World.IMC" },
|
||||
{ 1070, "1070 - Stump Room.IMC" },
|
||||
{ 1071, "1071 - Signpost Room.IMC" },
|
||||
{ 1072, "1072 - Navigation.IMC" },
|
||||
{ 1073, "1071 - Signpost Room.IMC" },
|
||||
{ 1074, "1074 - Bone Wagon.IMC" },
|
||||
{ 1075, "1075 - Spider's Eye.IMC" },
|
||||
{ 1076, "1076 - Spider Room.IMC" },
|
||||
{ 1077, "1077 - Tree Pump Amb.IMC" },
|
||||
{ 1078, "1078 - Tree Pump.IMC" },
|
||||
{ 1079, "1071 - Signpost Room.IMC" },
|
||||
{ 1080, "1080 - Beaver Room Lobby.IMC" },
|
||||
{ 1081, "1081 - Beaver Dam.IMC" },
|
||||
{ 1082, "1083 - Beaver Room.IMC" },
|
||||
{ 1083, "1083 - Beaver Room.IMC" },
|
||||
{ 1084, "1084 - Foggy Cactus.IMC" },
|
||||
{ 1085, "1085 - Rubamat Exterior.IMC" },
|
||||
{ 1086, "1086 - Blue Hector.IMC" },
|
||||
{ 1100, "1109 - Cafe Exterior.IMC" },
|
||||
{ 1101, "1101 - Cafe Office.IMC" },
|
||||
{ 1102, "1102 - Cafe Intercom.IMC" },
|
||||
{ 1103, "1103 - Coat Check.IMC" },
|
||||
{ 1104, "1104 - Lupe.IMC" },
|
||||
{ 1105, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1106, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1107, "1101 - Cafe Office.IMC" },
|
||||
{ 1108, "1108 - Casino Interior.IMC" },
|
||||
{ 1109, "1109 - Cafe Exterior.IMC" },
|
||||
{ 1110, "1110 - Cafe Ledge.IMC" },
|
||||
{ 1111, "1108 - Casino Interior.IMC" },
|
||||
{ 1112, "1112 - Rusty Sans Vox.IMC" },
|
||||
{ 1120, "1120 - Elevator Station.IMC" },
|
||||
{ 1121, "1122 - Blue Exterior.IMC" },
|
||||
{ 1122, "1122 - Blue Exterior.IMC" },
|
||||
{ 1123, "1123 - Blue Casket Ins.IMC" },
|
||||
{ 1124, "1124 - Blue Casket Amb.IMC" },
|
||||
{ 1125, "1125 - Smooth Hector.IMC" },
|
||||
{ 1126, "1122 - Blue Exterior.IMC" },
|
||||
{ 1127, "1127 - Limbo Dock.IMC" },
|
||||
{ 1128, "1128 - Limbo Talk.IMC" },
|
||||
{ 1129, "1129 - Limbo Poem.IMC" },
|
||||
{ 1130, "1130 - Dry Dock.IMC" },
|
||||
{ 1131, "1131 - Dry Dock Strike.IMC" },
|
||||
{ 1132, "1132 - Lighthouse Ext.IMC" },
|
||||
{ 1133, "1133 - Lola's Last.IMC" },
|
||||
{ 1140, "1140 - Police Station.IMC" },
|
||||
{ 1141, "1141 - Police Interior.IMC" },
|
||||
{ 1142, "1141 - Police Interior.IMC" },
|
||||
{ 1143, "1143 - Morgue.IMC" },
|
||||
{ 1144, "1140 - Police Station.IMC" },
|
||||
{ 1145, "1145 - Bridge Blimp.IMC" },
|
||||
{ 1146, "1146 - LOL Security Ext.IMC" },
|
||||
{ 1147, "1147 - LOL Security Int.IMC" },
|
||||
{ 1148, "1148 - Carla's Life.IMC" },
|
||||
{ 1149, "1149 - Bomb.IMC" },
|
||||
{ 1150, "1150 - Track Stairs.IMC" },
|
||||
{ 1151, "1151 - Track Stairs.IMC" },
|
||||
{ 1152, "1152 - Track Stairs.IMC" },
|
||||
{ 1153, "1153 - Track Base.IMC" },
|
||||
{ 1154, "1154 - Kitty Hall.IMC" },
|
||||
{ 1155, "1155 - Sanspoof.IMC" },
|
||||
{ 1156, "1156 - Kitty Stables.IMC" },
|
||||
{ 1160, "1160 - High Roller Hall.IMC" },
|
||||
{ 1161, "1161 - High Roller Lnge.IMC" },
|
||||
{ 1162, "1162 - Glottis Gambling.IMC" },
|
||||
{ 1163, "1163 - Max's Office.IMC" },
|
||||
{ 1164, "1125 - Hector Steps Out.IMC" },
|
||||
{ 1165, "1125 - Hector Steps Out.IMC" },
|
||||
{ 1166, "1125 - Hector Steps Out.IMC" },
|
||||
{ 1167, "1167 - Dillopede Elev.IMC" },
|
||||
{ 1168, "1168 - Dillopede Elev.IMC" },
|
||||
{ 1169, "1169 - Dillopede Elev.IMC" },
|
||||
{ 1170, "1170 - Extendo Bridge.IMC" },
|
||||
{ 1171, "1170 - Extendo Bridge.IMC" },
|
||||
{ 1172, "1170 - Extendo Bridge.IMC" },
|
||||
{ 1173, "1173 - Scrimshaw Int.IMC" },
|
||||
{ 1174, "1174 - Scrim Sleep.IMC" },
|
||||
{ 1180, "1180 - Note to Manny.IMC" },
|
||||
{ 1181, "1155 - Sanspoof.IMC" },
|
||||
{ 1190, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1191, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1201, "1201 - Lola Zapata.IMC" },
|
||||
{ 1202, "1202 - Inside the Lola.IMC" },
|
||||
{ 1203, "1203 - Engine Room.IMC" },
|
||||
{ 1204, "1204 - Porthole.IMC" },
|
||||
{ 1205, "1204 - Porthole.IMC" },
|
||||
{ 1210, "1210 - Sunken Lola.IMC" },
|
||||
{ 1211, "1211 - Pearl Crater Sub.IMC" },
|
||||
{ 1220, "1220 - Miner's Room.IMC" },
|
||||
{ 1221, "1221 - Miner's Room.IMC" },
|
||||
{ 1222, "1222 - Exterior Airlock.IMC" },
|
||||
{ 1223, "1223 - Factory Hub.IMC" },
|
||||
{ 1224, "1224 - Foreman's Office.IMC" },
|
||||
{ 1230, "1230 - Vault Door.IMC" },
|
||||
{ 1231, "1231 - Outer Vault.IMC" },
|
||||
{ 1232, "1232 - Inner Vault.IMC" },
|
||||
{ 1233, "1233 - Ashtray Room.IMC" },
|
||||
{ 1234, "1234 - Ashtray Scary.IMC" },
|
||||
{ 1235, "1235 - Ashtray Pensive.IMC" },
|
||||
{ 1236, "1236 - Domino's Room.IMC" },
|
||||
{ 1240, "1240 - Conveyor Under.IMC" },
|
||||
{ 1241, "1240 - Conveyor Under.IMC" },
|
||||
{ 1242, "1241 - Crane Intro.IMC" },
|
||||
{ 1243, "1243 - Anchor Room.IMC" },
|
||||
{ 1244, "1244 - Glottis Hanging.IMC" },
|
||||
{ 1245, "1245 - End of the World.IMC" },
|
||||
{ 1246, "1246 - End World Later.IMC" },
|
||||
{ 1247, "1241 - Crane Intro.IMC" },
|
||||
{ 1250, "1250 - Upper Beach.IMC" },
|
||||
{ 1251, "1250 - Upper Beach.IMC" },
|
||||
{ 1252, "1252 - Lower Beach Boat.IMC" },
|
||||
{ 1253, "1253 - Lamancha Sub.IMC" },
|
||||
{ 1254, "1254 - Crane Later.IMC" },
|
||||
{ 1301, "1301 - Temple Gate.IMC" },
|
||||
{ 1302, "1301 - Temple Gate.IMC" },
|
||||
{ 1303, "1303 - Truck Depot.IMC" },
|
||||
{ 1304, "1304 - Mayan Train Sta.IMC" },
|
||||
{ 1305, "1305 - Mayan Workshop.IMC" },
|
||||
{ 1306, "1306 - Mayan Train Pad.IMC" },
|
||||
{ 1307, "1307 - Mechanic's Kitch.IMC" },
|
||||
{ 1310, "1310 - Jello Bomb.IMC" },
|
||||
{ 1311, "1310 - Jello Bomb.IMC" },
|
||||
{ 1312, "1125 - Smooth Hector.IMC" },
|
||||
{ 1313, "1125 - Smooth Hector.IMC" },
|
||||
{ 1314, "1125 - Smooth Hector.IMC" },
|
||||
{ 1315, "1122 - Blue Exterior.IMC" },
|
||||
{ 1316, "1122 - Blue Exterior.IMC" },
|
||||
{ 1317, "1122 - Blue Exterior.IMC" },
|
||||
{ 1318, "1332 - Hector's Foyer.IMC" },
|
||||
{ 1319, "1319 - Florist Video.IMC" },
|
||||
{ 1320, "1320 - New LSA HQ.IMC" },
|
||||
{ 1321, "1321 - LSA Sewer.IMC" },
|
||||
{ 1322, "1321 - LSA Sewer.IMC" },
|
||||
{ 1323, "1323 - Sewer Maze.IMC" },
|
||||
{ 1324, "1324 - Albinozod.IMC" },
|
||||
{ 1325, "1325 - Florist Shop.IMC" },
|
||||
{ 1326, "1326 - Florist Shop Int.IMC" },
|
||||
{ 1327, "1327 - Florist OK.IMC" },
|
||||
{ 1328, "1323 - Sewer Maze.IMC" },
|
||||
{ 1329, "1329 - Theater Backstag.IMC" },
|
||||
{ 1330, "1330 - Lemans Lobby.IMC" },
|
||||
{ 1331, "1330 - Lemans Lobby.IMC" },
|
||||
{ 1332, "1332 - Hector's Foyer.IMC" },
|
||||
{ 1333, "1333 - Brennis Talk.IMC" },
|
||||
{ 1334, "1334 - Albino Trap.IMC" },
|
||||
{ 1340, "1342 - Neon Ledge.IMC" },
|
||||
{ 1350, "1350 - Meadow Flowers.IMC" },
|
||||
{ 1351, "1351 - Meadow.IMC" },
|
||||
{ 1352, "1352 - Car Head.IMC" },
|
||||
{ 1353, "1353 - Greenhouse Appr.IMC" },
|
||||
{ 1354, "1354 - Game Ending.IMC" },
|
||||
{ 1355, "1355 - Shootout.IMC" },
|
||||
{ 1400, "1400 - Start Credits.IMC" },
|
||||
{ 1401, "1401 - Smooth Hector.IMC" },
|
||||
{ 2001, "2001 - Climb Rope.IMC" },
|
||||
{ 2010, "2010 - Glottis OK.IMC" },
|
||||
{ 2020, "2020 - Reap Bruno.IMC" },
|
||||
{ 2030, "2030 - Ledgepeckers.IMC" },
|
||||
{ 2050, "2050 - Glottis Heart.IMC" },
|
||||
{ 2055, "2055 - Slingshot Bone.IMC" },
|
||||
{ 2060, "2060 - Glott Tree Fall.IMC" },
|
||||
{ 2070, "2070 - Beaver Fly.IMC" },
|
||||
{ 2071, "2071 - Beaver Sink.IMC" },
|
||||
{ 2080, "2080 - Meet Velasco.IMC" },
|
||||
{ 2140, "2140 - Ooo Bonewagon.IMC" },
|
||||
{ 2141, "2141 - Ooo Meche.IMC" },
|
||||
{ 2155, "2155 - Find Detector.IMC" },
|
||||
{ 2156, "2156 - Glott Drink Wine.IMC" },
|
||||
{ 2157, "2157 - Glott No Wine.IMC" },
|
||||
{ 2161, "2161 - Raoul Appears.IMC" },
|
||||
{ 2162, "2162 - Raoul KO.IMC" },
|
||||
{ 2163, "2163 - Raoul Dissed.IMC" },
|
||||
{ 2165, "2165 - Fake Tix.IMC" },
|
||||
{ 2180, "2180 - Befriend Commies.IMC" },
|
||||
{ 2186, "2186 - Nick Punchout.IMC" },
|
||||
{ 2200, "2200 - Year 3 Iris.IMC" },
|
||||
{ 2210, "2210 - Hit Men.IMC" },
|
||||
{ 2230, "2230 - Open Vault.IMC" },
|
||||
{ 2235, "2235 - Dead Tix.IMC" },
|
||||
{ 2240, "2240 - Sprinkler.IMC" },
|
||||
{ 2250, "2250 - Crane Track.IMC" },
|
||||
{ 2255, "2255 - Crane Fall.IMC" },
|
||||
{ 2300, "2300 - Yr 4 Iris.IMC" },
|
||||
{ 2301, "2301 - Pop Bruno Casket.IMC" },
|
||||
{ 2310, "2310 - Rocket Idea.IMC" },
|
||||
{ 2320, "2320 - Jello Suspense.IMC" },
|
||||
{ 2325, "2325 - Lumbago Lemo.IMC" },
|
||||
{ 2327, "2327 - Breath Mint.IMC" },
|
||||
{ 2330, "2330 - Pigeon Fly.IMC" },
|
||||
{ 2340, "2340 - Coffee On Boys.IMC" },
|
||||
{ 2350, "2350 - Sprout Aha.IMC" },
|
||||
{ 2360, "2360 - Chowchilla Bye.IMC" },
|
||||
{ 2370, "2370 - Salvador Death.IMC" },
|
||||
{ 2399, "2399 - End Credits.IMC" }
|
||||
};
|
||||
|
||||
Mixer *Mixer::instance_ = NULL;
|
||||
|
||||
Mixer *Mixer::instance() {
|
||||
if (instance_ == NULL)
|
||||
instance_ = new Mixer;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void mixerCallback(void *userdata, Uint8 *stream, int len) {
|
||||
Mixer *m = static_cast<Mixer *>(userdata);
|
||||
int16 *samples = reinterpret_cast<int16 *>(stream);
|
||||
m->getAudio(samples, len / 2);
|
||||
}
|
||||
|
||||
Mixer::Mixer() :
|
||||
musicSound_(NULL), seqSound_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void Mixer::start() {
|
||||
Sound::init();
|
||||
|
||||
SDL_AudioSpec desired;
|
||||
memset(&desired, 0, sizeof(desired)); // for valgrind cleanness
|
||||
desired.freq = 22050;
|
||||
desired.format = AUDIO_S16SYS;
|
||||
desired.channels = 2;
|
||||
desired.samples = 2048;
|
||||
desired.callback = mixerCallback;
|
||||
desired.userdata = this;
|
||||
SDL_OpenAudio(&desired, NULL);
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
void Mixer::playVoice(Sound *s) {
|
||||
AudioLock l;
|
||||
|
||||
s->reset();
|
||||
voiceSounds_.push_back(s);
|
||||
}
|
||||
|
||||
void Mixer::playSfx(Sound *s) {
|
||||
AudioLock l;
|
||||
|
||||
s->reset();
|
||||
sfxSounds_.push_back(s);
|
||||
}
|
||||
|
||||
void Mixer::stopSfx(Sound *s) {
|
||||
AudioLock l;
|
||||
|
||||
for (sound_list::iterator i = sfxSounds_.begin();
|
||||
i != sfxSounds_.end(); ) {
|
||||
if (*i == s)
|
||||
i = sfxSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::stopVoice(Sound *s) {
|
||||
AudioLock l;
|
||||
|
||||
for (sound_list::iterator i = voiceSounds_.begin();
|
||||
i != voiceSounds_.end(); ) {
|
||||
if (*i == s)
|
||||
i = voiceSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static int compareStates(const void *p1, const void *p2) {
|
||||
const imuseTableEntry *e1 = static_cast<const imuseTableEntry *>(p1);
|
||||
const imuseTableEntry *e2 = static_cast<const imuseTableEntry *>(p2);
|
||||
return e1->stateNum - e2->stateNum;
|
||||
}
|
||||
|
||||
void Mixer::setImuseState(int state) {
|
||||
Sound *newSound = NULL;
|
||||
|
||||
if (state != 1000) {
|
||||
imuseTableEntry key;
|
||||
key.stateNum = state;
|
||||
const imuseTableEntry *e = static_cast<imuseTableEntry *>
|
||||
(std::bsearch(&key, grimMusicTable,
|
||||
sizeof(grimMusicTable) / sizeof(grimMusicTable[0]),
|
||||
sizeof(grimMusicTable[0]), compareStates));
|
||||
if (e == NULL) {
|
||||
warning("Unknown IMuse state %d\n", state);
|
||||
return;
|
||||
}
|
||||
|
||||
newSound = ResourceLoader::instance()->loadSound(e->filename);
|
||||
if (newSound == NULL) {
|
||||
warning("Could not find music file %s\n", e->filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AudioLock l;
|
||||
if (newSound != musicSound_) {
|
||||
if (newSound != NULL)
|
||||
newSound->reset();
|
||||
musicSound_ = newSound;
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::setImuseSeq(int state) {
|
||||
Sound *newSound = NULL;
|
||||
|
||||
if (state != 2000) {
|
||||
imuseTableEntry key;
|
||||
key.stateNum = state;
|
||||
const imuseTableEntry *e = static_cast<imuseTableEntry *>
|
||||
(std::bsearch(&key, grimMusicTable,
|
||||
sizeof(grimMusicTable) / sizeof(grimMusicTable[0]),
|
||||
sizeof(grimMusicTable[0]), compareStates));
|
||||
if (e == NULL) {
|
||||
warning("Unknown IMuse state %d\n", state);
|
||||
return;
|
||||
}
|
||||
|
||||
Sound *newSound = ResourceLoader::instance()->loadSound(e->filename);
|
||||
if (newSound == NULL) {
|
||||
warning("Could not find music file %s\n", e->filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AudioLock l;
|
||||
if (newSound != seqSound_) {
|
||||
if (newSound != NULL)
|
||||
newSound->reset();
|
||||
seqSound_ = newSound;
|
||||
}
|
||||
}
|
||||
|
||||
Sound *Mixer::findSfx(const char *filename) {
|
||||
AudioLock l;
|
||||
|
||||
for (sound_list::iterator i = sfxSounds_.begin();
|
||||
i != sfxSounds_.end(); i++) {
|
||||
if (std::strcmp((*i)->filename(), filename) == 0)
|
||||
return *i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Mixer::voicePlaying() const {
|
||||
AudioLock l;
|
||||
|
||||
return ! voiceSounds_.empty();
|
||||
}
|
||||
|
||||
void Mixer::getAudio(int16 *data, int numSamples) {
|
||||
memset(data, 0, numSamples * 2);
|
||||
for (sound_list::iterator i = voiceSounds_.begin();
|
||||
i != voiceSounds_.end(); ) {
|
||||
(*i)->mix(data, numSamples);
|
||||
if ((*i)->done())
|
||||
i = voiceSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
for (sound_list::iterator i = sfxSounds_.begin();
|
||||
i != sfxSounds_.end(); ) {
|
||||
(*i)->mix(data, numSamples);
|
||||
if ((*i)->done())
|
||||
i = sfxSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
if (seqSound_ != NULL) {
|
||||
seqSound_->mix(data, numSamples);
|
||||
if (seqSound_->done())
|
||||
seqSound_ = NULL;
|
||||
}
|
||||
else if (musicSound_ != NULL) {
|
||||
musicSound_->mix(data, numSamples);
|
||||
if (musicSound_->done())
|
||||
musicSound_->reset();
|
||||
}
|
||||
}
|
56
mixer.h
56
mixer.h
@ -1,56 +0,0 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#ifndef MIXER_H
|
||||
#define MIXER_H
|
||||
|
||||
#include "bits.h"
|
||||
#include "resource.h"
|
||||
#include <list>
|
||||
#include <SDL_audio.h>
|
||||
|
||||
class Sound;
|
||||
|
||||
class Mixer {
|
||||
public:
|
||||
static Mixer *instance();
|
||||
|
||||
void start();
|
||||
|
||||
void playVoice(Sound *s);
|
||||
void playSfx(Sound *s);
|
||||
void stopSfx(Sound *s);
|
||||
void stopVoice(Sound *s);
|
||||
void setImuseState(int state);
|
||||
void setImuseSeq(int seq);
|
||||
|
||||
Sound *findSfx(const char *filename);
|
||||
bool voicePlaying() const;
|
||||
|
||||
private:
|
||||
Mixer();
|
||||
void getAudio(int16 *data, int numSamples);
|
||||
|
||||
static Mixer *instance_;
|
||||
typedef std::list<ResPtr<Sound> > sound_list;
|
||||
sound_list voiceSounds_, sfxSounds_;
|
||||
ResPtr<Sound> musicSound_, seqSound_;
|
||||
|
||||
friend void mixerCallback(void *userdata, uint8 *stream, int len);
|
||||
};
|
||||
|
||||
#endif
|
241
mixer/audiostream.cpp
Normal file
241
mixer/audiostream.cpp
Normal file
@ -0,0 +1,241 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../debug.h"
|
||||
#include "mixer.h"
|
||||
#include "audiostream.h"
|
||||
|
||||
|
||||
// This used to be an inline template function, but
|
||||
// buggy template function handling in MSVC6 forced
|
||||
// us to go with the macro approach. So far this is
|
||||
// the only template function that MSVC6 seemed to
|
||||
// compile incorrectly. Knock on wood.
|
||||
#define READSAMPLE(is16Bit, isUnsigned, ptr) \
|
||||
((is16Bit ? READ_BE_UINT16(ptr) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0))
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
class LinearMemoryStream : public AudioInputStream {
|
||||
protected:
|
||||
const byte *_ptr;
|
||||
const byte *_end;
|
||||
const byte *_loopPtr;
|
||||
const byte *_loopEnd;
|
||||
|
||||
inline int16 readIntern() {
|
||||
//assert(_ptr < _end);
|
||||
int16 val = READSAMPLE(is16Bit, isUnsigned, _ptr);
|
||||
_ptr += (is16Bit ? 2 : 1);
|
||||
if (_loopPtr && eosIntern()) {
|
||||
_ptr = _loopPtr;
|
||||
_end = _loopEnd;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
inline bool eosIntern() const { return _ptr >= _end; };
|
||||
public:
|
||||
LinearMemoryStream(const byte *ptr, uint len, uint loopOffset, uint loopLen)
|
||||
: _ptr(ptr), _end(ptr+len), _loopPtr(0), _loopEnd(0) {
|
||||
|
||||
// Verify the buffer sizes are sane
|
||||
if (is16Bit && stereo)
|
||||
assert((len & 3) == 0 && (loopLen & 3) == 0);
|
||||
else if (is16Bit || stereo)
|
||||
assert((len & 1) == 0 && (loopLen & 1) == 0);
|
||||
|
||||
if (loopLen) {
|
||||
_loopPtr = _ptr + loopOffset;
|
||||
_loopEnd = _loopPtr + loopLen;
|
||||
}
|
||||
if (stereo) // Stereo requires even sized data
|
||||
assert(len % 2 == 0);
|
||||
}
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
int16 read() { return readIntern(); }
|
||||
bool eos() const { return eosIntern(); }
|
||||
bool isStereo() const { return stereo; }
|
||||
};
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
int LinearMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
while (samples < numSamples && !eosIntern()) {
|
||||
const int len = MIN(numSamples, samples + (int)(_end - _ptr) / (is16Bit ? 2 : 1));
|
||||
while (samples < len) {
|
||||
*buffer++ = READSAMPLE(is16Bit, isUnsigned, _ptr);
|
||||
_ptr += (is16Bit ? 2 : 1);
|
||||
samples++;
|
||||
}
|
||||
// Loop, if looping was specified
|
||||
if (_loopPtr && eosIntern()) {
|
||||
_ptr = _loopPtr;
|
||||
_end = _loopEnd;
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
// Wrapped memory stream, to be used by the ChannelStream class (and possibly others?)
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
class WrappedMemoryStream : public WrappedAudioInputStream {
|
||||
protected:
|
||||
byte *_bufferStart;
|
||||
byte *_bufferEnd;
|
||||
byte *_pos;
|
||||
byte *_end;
|
||||
|
||||
inline int16 readIntern();
|
||||
inline bool eosIntern() const { return _end == _pos; };
|
||||
public:
|
||||
WrappedMemoryStream(uint bufferSize);
|
||||
~WrappedMemoryStream() { free(_bufferStart); }
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
int16 read() { return readIntern(); }
|
||||
bool eos() const { return eosIntern(); }
|
||||
bool isStereo() const { return stereo; }
|
||||
|
||||
void append(const byte *data, uint32 len);
|
||||
};
|
||||
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
WrappedMemoryStream<stereo, is16Bit, isUnsigned>::WrappedMemoryStream(uint bufferSize) {
|
||||
|
||||
// Verify the buffer size is sane
|
||||
if (is16Bit && stereo)
|
||||
assert((bufferSize & 3) == 0);
|
||||
else if (is16Bit || stereo)
|
||||
assert((bufferSize & 1) == 0);
|
||||
|
||||
_bufferStart = (byte *)malloc(bufferSize);
|
||||
_pos = _end = _bufferStart;
|
||||
_bufferEnd = _bufferStart + bufferSize;
|
||||
}
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
inline int16 WrappedMemoryStream<stereo, is16Bit, isUnsigned>::readIntern() {
|
||||
//assert(_pos != _end);
|
||||
int16 val = READSAMPLE(is16Bit, isUnsigned, _pos);
|
||||
_pos += (is16Bit ? 2 : 1);
|
||||
|
||||
// Wrap around?
|
||||
if (_pos >= _bufferEnd)
|
||||
_pos = _pos - (_bufferEnd - _bufferStart);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
int WrappedMemoryStream<stereo, is16Bit, isUnsigned>::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
while (samples < numSamples && !eosIntern()) {
|
||||
const byte *endMarker = (_pos > _end) ? _bufferEnd : _end;
|
||||
const int len = MIN(numSamples, samples + (int)(endMarker - _pos) / (is16Bit ? 2 : 1));
|
||||
while (samples < len) {
|
||||
*buffer++ = READSAMPLE(is16Bit, isUnsigned, _pos);
|
||||
_pos += (is16Bit ? 2 : 1);
|
||||
samples++;
|
||||
}
|
||||
// Wrap around?
|
||||
if (_pos >= _bufferEnd)
|
||||
_pos = _pos - (_bufferEnd - _bufferStart);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
template<bool stereo, bool is16Bit, bool isUnsigned>
|
||||
void WrappedMemoryStream<stereo, is16Bit, isUnsigned>::append(const byte *data, uint32 len) {
|
||||
|
||||
// Verify the buffer size is sane
|
||||
if (is16Bit && stereo)
|
||||
assert((len & 3) == 0);
|
||||
else if (is16Bit || stereo)
|
||||
assert((len & 1) == 0);
|
||||
|
||||
if (_end + len > _bufferEnd) {
|
||||
// Wrap-around case
|
||||
uint32 size_to_end_of_buffer = _bufferEnd - _end;
|
||||
len -= size_to_end_of_buffer;
|
||||
if ((_end < _pos) || (_bufferStart + len >= _pos)) {
|
||||
warning("WrappedMemoryStream: buffer overflow (A)");
|
||||
return;
|
||||
}
|
||||
memcpy(_end, data, size_to_end_of_buffer);
|
||||
memcpy(_bufferStart, data + size_to_end_of_buffer, len);
|
||||
_end = _bufferStart + len;
|
||||
} else {
|
||||
if ((_end < _pos) && (_end + len >= _pos)) {
|
||||
warning("WrappedMemoryStream: buffer overflow (B)");
|
||||
return;
|
||||
}
|
||||
memcpy(_end, data, len);
|
||||
_end += len;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool stereo>
|
||||
static AudioInputStream *makeLinearInputStream(const byte *ptr, uint32 len, bool is16Bit, bool isUnsigned, uint loopOffset, uint loopLen) {
|
||||
if (isUnsigned) {
|
||||
if (is16Bit)
|
||||
return new LinearMemoryStream<stereo, true, true>(ptr, len, loopOffset, loopLen);
|
||||
else
|
||||
return new LinearMemoryStream<stereo, false, true>(ptr, len, loopOffset, loopLen);
|
||||
} else {
|
||||
if (is16Bit)
|
||||
return new LinearMemoryStream<stereo, true, false>(ptr, len, loopOffset, loopLen);
|
||||
else
|
||||
return new LinearMemoryStream<stereo, false, false>(ptr, len, loopOffset, loopLen);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool stereo>
|
||||
static WrappedAudioInputStream *makeWrappedInputStream(uint32 len, bool is16Bit, bool isUnsigned) {
|
||||
if (isUnsigned) {
|
||||
if (is16Bit)
|
||||
return new WrappedMemoryStream<stereo, true, true>(len);
|
||||
else
|
||||
return new WrappedMemoryStream<stereo, false, true>(len);
|
||||
} else {
|
||||
if (is16Bit)
|
||||
return new WrappedMemoryStream<stereo, true, false>(len);
|
||||
else
|
||||
return new WrappedMemoryStream<stereo, false, false>(len);
|
||||
}
|
||||
}
|
||||
|
||||
AudioInputStream *makeLinearInputStream(byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen) {
|
||||
const bool is16Bit = (_flags & SoundMixer::FLAG_16BITS) != 0;
|
||||
const bool isUnsigned = (_flags & SoundMixer::FLAG_UNSIGNED) != 0;
|
||||
if (_flags & SoundMixer::FLAG_STEREO) {
|
||||
return makeLinearInputStream<true>(ptr, len, is16Bit, isUnsigned, loopOffset, loopLen);
|
||||
} else {
|
||||
return makeLinearInputStream<false>(ptr, len, is16Bit, isUnsigned, loopOffset, loopLen);
|
||||
}
|
||||
}
|
||||
|
||||
WrappedAudioInputStream *makeWrappedInputStream(byte _flags, uint32 len) {
|
||||
const bool is16Bit = (_flags & SoundMixer::FLAG_16BITS) != 0;
|
||||
const bool isUnsigned = (_flags & SoundMixer::FLAG_UNSIGNED) != 0;
|
||||
if (_flags & SoundMixer::FLAG_STEREO) {
|
||||
return makeWrappedInputStream<true>(len, is16Bit, isUnsigned);
|
||||
} else {
|
||||
return makeWrappedInputStream<false>(len, is16Bit, isUnsigned);
|
||||
}
|
||||
}
|
94
mixer/audiostream.h
Normal file
94
mixer/audiostream.h
Normal file
@ -0,0 +1,94 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#ifndef AUDIOSTREAM_H
|
||||
#define AUDIOSTREAM_H
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../bits.h"
|
||||
|
||||
/**
|
||||
* Generic input stream for the resampling code.
|
||||
*/
|
||||
class AudioInputStream {
|
||||
public:
|
||||
virtual ~AudioInputStream() {}
|
||||
|
||||
/**
|
||||
* Fill the given buffer with up to numSamples samples.
|
||||
* Returns the actual number of samples read, or -1 if
|
||||
* a critical error occured (note: you *must* check if
|
||||
* this value is less than what you requested, this can
|
||||
* happen when the stream is fully used up).
|
||||
* For stereo stream, buffer will be filled with interleaved
|
||||
* left and right channel samples.
|
||||
*
|
||||
* For maximum efficency, subclasses should always override
|
||||
* the default implementation!
|
||||
*/
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
for (samples = 0; samples < numSamples && !eos(); samples++) {
|
||||
*buffer++ = read();
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
/** Read a singel (16 bit signed) sample from the stream. */
|
||||
virtual int16 read() = 0;
|
||||
|
||||
/** Is this a stereo stream? */
|
||||
virtual bool isStereo() const = 0;
|
||||
|
||||
/* End of stream reached? */
|
||||
virtual bool eos() const = 0;
|
||||
|
||||
virtual int getRate() const { return -1; }
|
||||
};
|
||||
|
||||
class WrappedAudioInputStream : public AudioInputStream {
|
||||
public:
|
||||
virtual void append(const byte *data, uint32 len) = 0;
|
||||
};
|
||||
|
||||
class ZeroInputStream : public AudioInputStream {
|
||||
protected:
|
||||
int _len;
|
||||
public:
|
||||
ZeroInputStream(uint len) : _len(len) { }
|
||||
int readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = MIN(_len, numSamples);
|
||||
memset(buffer, 0, samples * 2);
|
||||
_len -= samples;
|
||||
return samples;
|
||||
}
|
||||
int16 read() { assert(_len > 0); _len--; return 0; }
|
||||
int size() const { return _len; }
|
||||
bool isStereo() const { return false; }
|
||||
bool eos() const { return _len <= 0; }
|
||||
};
|
||||
|
||||
class MusicStream : public AudioInputStream {
|
||||
public:
|
||||
virtual int getRate() const = 0;
|
||||
};
|
||||
|
||||
|
||||
AudioInputStream *makeLinearInputStream(byte _flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen);
|
||||
WrappedAudioInputStream *makeWrappedInputStream(byte _flags, uint32 len);
|
||||
|
||||
#endif
|
525
mixer/mixer.cpp
Normal file
525
mixer/mixer.cpp
Normal file
@ -0,0 +1,525 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../bits.h"
|
||||
#include "../debug.h"
|
||||
|
||||
#include "mixer.h"
|
||||
#include "rate.h"
|
||||
#include "audiostream.h"
|
||||
|
||||
SoundMixer *g_mixer = NULL;
|
||||
|
||||
StackLock::StackLock(MutexRef mutex)
|
||||
: _mutex(mutex) {
|
||||
lock_mutex(_mutex);
|
||||
}
|
||||
|
||||
StackLock::~StackLock() {
|
||||
unlock_mutex(_mutex);
|
||||
}
|
||||
|
||||
MutexRef create_mutex() {
|
||||
return (MutexRef) SDL_CreateMutex();
|
||||
}
|
||||
|
||||
void lock_mutex(MutexRef mutex) {
|
||||
SDL_mutexP((SDL_mutex *) mutex);
|
||||
}
|
||||
|
||||
void unlock_mutex(MutexRef mutex) {
|
||||
SDL_mutexV((SDL_mutex *) mutex);
|
||||
}
|
||||
|
||||
void delete_mutex(MutexRef mutex) {
|
||||
SDL_DestroyMutex((SDL_mutex *) mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channels used by the sound mixer.
|
||||
*/
|
||||
class Channel {
|
||||
protected:
|
||||
SoundMixer *_mixer;
|
||||
PlayingSoundHandle *_handle;
|
||||
RateConverter *_converter;
|
||||
AudioInputStream *_input;
|
||||
byte _volume;
|
||||
int8 _pan;
|
||||
bool _paused;
|
||||
|
||||
public:
|
||||
int _id;
|
||||
|
||||
Channel(SoundMixer *mixer, PlayingSoundHandle *handle)
|
||||
: _mixer(mixer), _handle(handle), _converter(0), _input(0), _volume(0), _pan(0), _paused(false), _id(-1) {
|
||||
assert(mixer);
|
||||
}
|
||||
virtual ~Channel();
|
||||
void destroy();
|
||||
virtual void mix(int16 *data, uint len);
|
||||
virtual void pause(bool paused) {
|
||||
_paused = paused;
|
||||
}
|
||||
virtual bool isPaused() {
|
||||
return _paused;
|
||||
}
|
||||
virtual void setChannelVolume(const byte volume) {
|
||||
_volume = volume;
|
||||
}
|
||||
virtual void setChannelPan(const int8 pan) {
|
||||
_pan = pan;
|
||||
}
|
||||
virtual int getVolume() const {
|
||||
return _mixer->getVolume();
|
||||
}
|
||||
};
|
||||
|
||||
class ChannelRaw : public Channel {
|
||||
byte *_ptr;
|
||||
public:
|
||||
ChannelRaw(SoundMixer *mixer, PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, byte volume, int8 pan, int id, uint32 loopStart, uint32 loopEnd);
|
||||
~ChannelRaw();
|
||||
};
|
||||
|
||||
class ChannelStream : public Channel {
|
||||
bool _finished;
|
||||
public:
|
||||
ChannelStream(SoundMixer *mixer, PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume, int8 pan);
|
||||
void mix(int16 *data, uint len);
|
||||
void append(void *sound, uint32 size);
|
||||
void finish() { _finished = true; }
|
||||
};
|
||||
|
||||
SoundMixer::SoundMixer() {
|
||||
_mutex = NULL;
|
||||
|
||||
_premixParam = NULL;
|
||||
_premixProc = NULL;
|
||||
int i = 0;
|
||||
|
||||
_outputRate = 0;
|
||||
|
||||
_globalVolume = 0;
|
||||
|
||||
_paused = false;
|
||||
|
||||
for (i = 0; i != NUM_CHANNELS; i++)
|
||||
_channels[i] = NULL;
|
||||
}
|
||||
|
||||
SoundMixer::~SoundMixer() {
|
||||
SDL_CloseAudio();
|
||||
for (int i = 0; i != NUM_CHANNELS; i++) {
|
||||
delete _channels[i];
|
||||
}
|
||||
delete_mutex(_mutex);
|
||||
}
|
||||
|
||||
void set_sound_proc(SoundProc proc, void *param) {
|
||||
SDL_AudioSpec desired;
|
||||
|
||||
memset(&desired, 0, sizeof(desired));
|
||||
|
||||
/* only one format supported at the moment */
|
||||
desired.freq = 22050;
|
||||
desired.format = AUDIO_S16SYS;
|
||||
desired.channels = 2;
|
||||
desired.samples = 2048;
|
||||
desired.callback = proc;
|
||||
desired.userdata = param;
|
||||
if (SDL_OpenAudio(&desired, NULL) != 0) {
|
||||
return;
|
||||
}
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
void SoundMixer::bindToSystem() {
|
||||
_mutex = create_mutex();
|
||||
_outputRate = 22050;
|
||||
|
||||
if (_outputRate == 0)
|
||||
error("OSystem returned invalid sample rate");
|
||||
|
||||
set_sound_proc(mixCallback, this);
|
||||
}
|
||||
|
||||
void SoundMixer::setupPremix(PremixProc *proc, void *param) {
|
||||
StackLock lock(_mutex);
|
||||
_premixParam = param;
|
||||
_premixProc = proc;
|
||||
}
|
||||
|
||||
int SoundMixer::newStream(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume, int8 pan) {
|
||||
StackLock lock(_mutex);
|
||||
return insertChannel(handle, new ChannelStream(this, handle, sound, size, rate, flags, buffer_size, volume, pan));
|
||||
}
|
||||
|
||||
void SoundMixer::appendStream(PlayingSoundHandle handle, void *sound, uint32 size) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::appendStream has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelStream *chan;
|
||||
chan = (ChannelStream *)_channels[index];
|
||||
if (!chan) {
|
||||
error("Trying to append to nonexistant streamer : %d", index);
|
||||
} else {
|
||||
chan->append(sound, size);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::endStream(PlayingSoundHandle handle) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
// Simply ignore stop requests for handles of sounds that already terminated
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::endStream has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelStream *chan;
|
||||
chan = (ChannelStream *)_channels[index];
|
||||
if (!chan) {
|
||||
error("Trying to end a nonexistant streamer : %d", index);
|
||||
} else {
|
||||
chan->finish();
|
||||
}
|
||||
}
|
||||
|
||||
int SoundMixer::insertChannel(PlayingSoundHandle *handle, Channel *chan) {
|
||||
int index = -1;
|
||||
for (int i = 0; i != NUM_CHANNELS; i++) {
|
||||
if (_channels[i] == NULL) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(index == -1) {
|
||||
warning("SoundMixer::out of mixer slots");
|
||||
delete chan;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_channels[index] = chan;
|
||||
if (handle)
|
||||
*handle = index + 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
int SoundMixer::playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, int id, byte volume, int8 pan, uint32 loopStart, uint32 loopEnd) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
// Prevent duplicate sounds
|
||||
if (id != -1) {
|
||||
for (int i = 0; i != NUM_CHANNELS; i++)
|
||||
if (_channels[i] != NULL && _channels[i]->_id == id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return insertChannel(handle, new ChannelRaw(this, handle, sound, size, rate, flags, volume, pan, id, loopStart, loopEnd));
|
||||
}
|
||||
|
||||
void SoundMixer::mix(int16 *buf, uint len) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (_premixProc && !_paused) {
|
||||
_premixProc(_premixParam, buf, len);
|
||||
} else {
|
||||
// zero the buf out
|
||||
memset(buf, 0, 2 * len * sizeof(int16));
|
||||
}
|
||||
|
||||
if (!_paused) {
|
||||
// now mix all channels
|
||||
for (int i = 0; i != NUM_CHANNELS; i++)
|
||||
if (_channels[i] && !_channels[i]->isPaused())
|
||||
_channels[i]->mix(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::mixCallback(void *s, byte *samples, int len) {
|
||||
assert(s);
|
||||
assert(samples);
|
||||
// Len is the number of bytes in the buffer; we divide it by
|
||||
// four to get the number of samples (stereo 16 bit).
|
||||
((SoundMixer *)s)->mix((int16 *)samples, len >> 2);
|
||||
}
|
||||
|
||||
void SoundMixer::stopAll() {
|
||||
StackLock lock(_mutex);
|
||||
for (int i = 0; i != NUM_CHANNELS; i++)
|
||||
if (_channels[i])
|
||||
_channels[i]->destroy();
|
||||
}
|
||||
|
||||
void SoundMixer::stopChannel(int index) {
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::stop has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
StackLock lock(_mutex);
|
||||
if (_channels[index])
|
||||
_channels[index]->destroy();
|
||||
}
|
||||
|
||||
void SoundMixer::stopID(int id) {
|
||||
StackLock lock(_mutex);
|
||||
for (int i = 0; i != NUM_CHANNELS; i++) {
|
||||
if (_channels[i] != NULL && _channels[i]->_id == id) {
|
||||
_channels[i]->destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::stopHandle(PlayingSoundHandle handle) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
// Simply ignore stop requests for handles of sounds that already terminated
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::stopHandle has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_channels[index])
|
||||
_channels[index]->destroy();
|
||||
}
|
||||
|
||||
void SoundMixer::setChannelVolume(PlayingSoundHandle handle, byte volume) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::setChannelVolume has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_channels[index])
|
||||
_channels[index]->setChannelVolume(volume);
|
||||
}
|
||||
|
||||
void SoundMixer::setChannelPan(PlayingSoundHandle handle, int8 pan) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::setChannelVolume has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_channels[index])
|
||||
_channels[index]->setChannelPan(pan);
|
||||
}
|
||||
|
||||
void SoundMixer::pauseAll(bool paused) {
|
||||
_paused = paused;
|
||||
}
|
||||
|
||||
void SoundMixer::pauseChannel(int index, bool paused) {
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::pauseChannel has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
StackLock lock(_mutex);
|
||||
if (_channels[index])
|
||||
_channels[index]->pause(paused);
|
||||
}
|
||||
|
||||
void SoundMixer::pauseID(int id, bool paused) {
|
||||
StackLock lock(_mutex);
|
||||
for (int i = 0; i != NUM_CHANNELS; i++) {
|
||||
if (_channels[i] != NULL && _channels[i]->_id == id) {
|
||||
_channels[i]->pause(paused);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::pauseHandle(PlayingSoundHandle handle, bool paused) {
|
||||
StackLock lock(_mutex);
|
||||
|
||||
// Simply ignore pause/unpause requests for handles of sound that alreayd terminated
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
int index = handle - 1;
|
||||
|
||||
if ((index < 0) || (index >= NUM_CHANNELS)) {
|
||||
warning("soundMixer::pauseHandle has invalid index %d", index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_channels[index])
|
||||
_channels[index]->pause(paused);
|
||||
}
|
||||
|
||||
void SoundMixer::setVolume(int volume) {
|
||||
// Check range
|
||||
if (volume > 256)
|
||||
volume = 256;
|
||||
else if (volume < 0)
|
||||
volume = 0;
|
||||
|
||||
_globalVolume = volume;
|
||||
}
|
||||
|
||||
Channel::~Channel() {
|
||||
delete _converter;
|
||||
delete _input;
|
||||
if (_handle)
|
||||
*_handle = 0;
|
||||
}
|
||||
|
||||
void Channel::destroy() {
|
||||
for (int i = 0; i != SoundMixer::NUM_CHANNELS; i++)
|
||||
if (_mixer->_channels[i] == this)
|
||||
_mixer->_channels[i] = 0;
|
||||
delete this;
|
||||
}
|
||||
|
||||
/* len indicates the number of sample *pairs*. So a value of
|
||||
10 means that the buffer contains twice 10 sample, each
|
||||
16 bits, for a total of 40 bytes.
|
||||
*/
|
||||
void Channel::mix(int16 *data, uint len) {
|
||||
assert(_input);
|
||||
if (_input->eos()) {
|
||||
// TODO: call drain method
|
||||
destroy();
|
||||
} else {
|
||||
assert(_converter);
|
||||
|
||||
// The pan value ranges from -127 to +127. That's 255 different values.
|
||||
// From the channel pan/volume and the global volume, we compute the
|
||||
// effective volume for the left and right channel.
|
||||
// Note the slightly odd divisor: the 255 reflects the fact that
|
||||
// the maximal value for _volume is 255, while the 254 is there
|
||||
// because the maximal left/right pan value is 2*127 = 254.
|
||||
// The value getVolume() returns is in the range 0 - 256.
|
||||
// Hence, the vol_l/vol_r values will be in that range, too
|
||||
|
||||
int vol = getVolume() * _volume;
|
||||
st_volume_t vol_l = (127 - _pan) * vol / (255 * 254);
|
||||
st_volume_t vol_r = (127 + _pan) * vol / (255 * 254);
|
||||
|
||||
_converter->flow(*_input, data, len, vol_l, vol_r);
|
||||
}
|
||||
}
|
||||
|
||||
/* RAW mixer */
|
||||
ChannelRaw::ChannelRaw(SoundMixer *mixer, PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, byte volume, int8 pan, int id, uint32 loopStart, uint32 loopEnd)
|
||||
: Channel(mixer, handle) {
|
||||
_id = id;
|
||||
_ptr = (byte *)sound;
|
||||
_volume = volume;
|
||||
_pan = pan;
|
||||
|
||||
// Create the input stream
|
||||
if (flags & SoundMixer::FLAG_LOOP) {
|
||||
if (loopEnd == 0) {
|
||||
_input = makeLinearInputStream(flags, _ptr, size, 0, size);
|
||||
} else {
|
||||
assert(loopStart < loopEnd && loopEnd <= size);
|
||||
_input = makeLinearInputStream(flags, _ptr, size, loopStart, loopEnd - loopStart);
|
||||
}
|
||||
} else {
|
||||
_input = makeLinearInputStream(flags, _ptr, size, 0, 0);
|
||||
}
|
||||
|
||||
// Get a rate converter instance
|
||||
_converter = makeRateConverter(rate, mixer->getOutputRate(), _input->isStereo(), (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0);
|
||||
|
||||
if (!(flags & SoundMixer::FLAG_AUTOFREE))
|
||||
_ptr = 0;
|
||||
}
|
||||
|
||||
ChannelRaw::~ChannelRaw() {
|
||||
free(_ptr);
|
||||
}
|
||||
|
||||
ChannelStream::ChannelStream(SoundMixer *mixer, PlayingSoundHandle *handle,
|
||||
void *sound, uint32 size, uint rate,
|
||||
byte flags, uint32 buffer_size, byte volume, int8 pan)
|
||||
: Channel(mixer, handle) {
|
||||
_volume = volume;
|
||||
_pan = pan;
|
||||
assert(size <= buffer_size);
|
||||
|
||||
// Create the input stream
|
||||
_input = makeWrappedInputStream(flags, buffer_size);
|
||||
|
||||
// Append the initial data
|
||||
((WrappedAudioInputStream *)_input)->append((const byte *)sound, size);
|
||||
|
||||
// Get a rate converter instance
|
||||
_converter = makeRateConverter(rate, mixer->getOutputRate(), _input->isStereo(), (flags & SoundMixer::FLAG_REVERSE_STEREO) != 0);
|
||||
|
||||
_finished = false;
|
||||
}
|
||||
|
||||
void ChannelStream::append(void *data, uint32 len) {
|
||||
((WrappedAudioInputStream *)_input)->append((const byte *)data, len);
|
||||
}
|
||||
|
||||
void ChannelStream::mix(int16 *data, uint len) {
|
||||
assert(_input);
|
||||
if (_input->eos()) {
|
||||
// TODO: call drain method
|
||||
|
||||
// Normally, the stream stays around even if all its data is used up.
|
||||
// This is in case more data is streamed into it. To make the stream
|
||||
// go away, one can either stop() it (which takes effect immediately,
|
||||
// ignoring any remaining sound data), or finish() it, which means
|
||||
// it will finish playing before it terminates itself.
|
||||
if (_finished) {
|
||||
destroy();
|
||||
}
|
||||
} else {
|
||||
// Invoke the parent implementation.
|
||||
Channel::mix(data, len);
|
||||
}
|
||||
}
|
145
mixer/mixer.h
Normal file
145
mixer/mixer.h
Normal file
@ -0,0 +1,145 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#ifndef SOUND_MIXER_H
|
||||
#define SOUND_MIXER_H
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../bits.h"
|
||||
#include <SDL.h>
|
||||
|
||||
typedef uint32 PlayingSoundHandle;
|
||||
typedef struct Mutex *MutexRef;
|
||||
typedef void (*SoundProc)(void *param, byte *buf, int len);
|
||||
typedef int (*TimerProc)(int interval);
|
||||
|
||||
class StackLock {
|
||||
MutexRef _mutex;
|
||||
public:
|
||||
StackLock(MutexRef mutex);
|
||||
~StackLock();
|
||||
};
|
||||
|
||||
MutexRef create_mutex();
|
||||
void lock_mutex(MutexRef mutex);
|
||||
void unlock_mutex(MutexRef mutex);
|
||||
void delete_mutex(MutexRef mutex);
|
||||
|
||||
class Channel;
|
||||
|
||||
class SoundMixer {
|
||||
friend class Channel;
|
||||
public:
|
||||
typedef void PremixProc (void *param, int16 *data, uint len);
|
||||
|
||||
enum {
|
||||
NUM_CHANNELS = 16
|
||||
};
|
||||
|
||||
enum {
|
||||
FLAG_UNSIGNED = 1 << 0, // unsigned samples (default: signed)
|
||||
FLAG_STEREO = 1 << 1, // sound is in stereo (default: mono)
|
||||
FLAG_16BITS = 1 << 2, // sound is 16 bits wide (default: 8bit)
|
||||
FLAG_AUTOFREE = 1 << 3, // sound buffer is freed automagically at the end of playing
|
||||
FLAG_REVERSE_STEREO = 1 << 4, // reverse the left and right stereo channel
|
||||
FLAG_LOOP = 1 << 5 // loop the audio
|
||||
};
|
||||
|
||||
private:
|
||||
MutexRef _mutex;
|
||||
|
||||
void *_premixParam;
|
||||
PremixProc *_premixProc;
|
||||
|
||||
uint _outputRate;
|
||||
|
||||
int _globalVolume;
|
||||
|
||||
bool _paused;
|
||||
|
||||
Channel *_channels[NUM_CHANNELS];
|
||||
|
||||
public:
|
||||
SoundMixer();
|
||||
~SoundMixer();
|
||||
|
||||
void bindToSystem();
|
||||
|
||||
void setupPremix(PremixProc *proc, void *param);
|
||||
|
||||
// start playing a raw sound
|
||||
int playRaw(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags,
|
||||
int id = -1, byte volume = 255, int8 pan = 0, uint32 loopStart = 0, uint32 loopEnd = 0);
|
||||
|
||||
/** Start a new stream. */
|
||||
int newStream(PlayingSoundHandle *handle, void *sound, uint32 size, uint rate, byte flags, uint32 buffer_size, byte volume = 255, int8 pan = 0);
|
||||
|
||||
/** Append to an existing stream. */
|
||||
void appendStream(PlayingSoundHandle handle, void *sound, uint32 size);
|
||||
|
||||
/** Mark a stream as finished - it will play all its remaining data, then stop. */
|
||||
void endStream(PlayingSoundHandle handle);
|
||||
|
||||
/** stop all currently playing sounds */
|
||||
void stopAll();
|
||||
|
||||
/** stop playing the given channel */
|
||||
void stopChannel(int channel);
|
||||
|
||||
/** stop playing the sound with given ID */
|
||||
void stopID(int id);
|
||||
|
||||
/** stop playing the channel for the given handle */
|
||||
void stopHandle(PlayingSoundHandle handle);
|
||||
|
||||
/** pause/unpause all channels */
|
||||
void pauseAll(bool paused);
|
||||
|
||||
/** pause/unpause the given channel */
|
||||
void pauseChannel(int index, bool paused);
|
||||
|
||||
/** pause/unpause the sound with the given ID */
|
||||
void pauseID(int id, bool paused);
|
||||
|
||||
/** pause/unpause the channel for the given handle */
|
||||
void pauseHandle(PlayingSoundHandle handle, bool paused);
|
||||
|
||||
/** set the channel volume for the given handle (0 - 255) */
|
||||
void setChannelVolume(PlayingSoundHandle handle, byte volume);
|
||||
|
||||
/** set the channel pan for the given handle (-127 ... 0 ... 127) (left ... center ... right)*/
|
||||
void setChannelPan(PlayingSoundHandle handle, int8 pan);
|
||||
|
||||
/** set the global volume, 0-256 */
|
||||
void setVolume(int volume);
|
||||
|
||||
/** query the global volume, 0-256 */
|
||||
int getVolume() const { return _globalVolume; }
|
||||
|
||||
/** query the output rate in kHz */
|
||||
uint getOutputRate() const { return _outputRate; }
|
||||
|
||||
private:
|
||||
int insertChannel(PlayingSoundHandle *handle, Channel *chan);
|
||||
|
||||
/** main mixer method */
|
||||
void mix(int16 *buf, uint len);
|
||||
|
||||
static void mixCallback(void *s, byte *samples, int len);
|
||||
};
|
||||
|
||||
#endif
|
235
mixer/rate.cpp
Normal file
235
mixer/rate.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
/*
|
||||
* The code in this file is based on code with Copyright 1998 Fabrice Bellard
|
||||
* Fabrice original code is part of SoX (http://sox.sourceforge.net).
|
||||
* Max Horn adapted that code to the needs of ScummVM and rewrote it partial,
|
||||
* in the process removing any use of floating point arithmetic. Various other
|
||||
* improvments over the original code were made.
|
||||
*/
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "rate.h"
|
||||
#include "audiostream.h"
|
||||
#include "../debug.h"
|
||||
|
||||
/**
|
||||
* The precision of the fractional computations used by the rate converter.
|
||||
* Normally you should never have to modify this value.
|
||||
*/
|
||||
#define FRAC_BITS 16
|
||||
|
||||
/**
|
||||
* The size of the intermediate input cache. Bigger values may increase
|
||||
* performance, but only until some point (depends largely on cache size,
|
||||
* target processor and various other factors), at which it will decrease
|
||||
* again.
|
||||
*/
|
||||
#define INTERMEDIATE_BUFFER_SIZE 512
|
||||
|
||||
|
||||
/**
|
||||
* Audio rate converter based on simple linear Interpolation.
|
||||
*
|
||||
* The use of fractional increment allows us to use no buffer. It
|
||||
* avoid the problems at the end of the buffer we had with the old
|
||||
* method which stored a possibly big buffer of size
|
||||
* lcm(in_rate,out_rate).
|
||||
*
|
||||
* Limited to sampling frequency <= 65535 Hz.
|
||||
*/
|
||||
|
||||
template<bool stereo, bool reverseStereo>
|
||||
class LinearRateConverter : public RateConverter {
|
||||
protected:
|
||||
st_sample_t inBuf[INTERMEDIATE_BUFFER_SIZE];
|
||||
const st_sample_t *inPtr;
|
||||
int inLen;
|
||||
|
||||
/** fractional position of the output stream in input stream unit */
|
||||
unsigned long opos, opos_frac;
|
||||
|
||||
/** fractional position increment in the output stream */
|
||||
unsigned long opos_inc, opos_inc_frac;
|
||||
|
||||
/** position in the input stream (integer) */
|
||||
unsigned long ipos;
|
||||
|
||||
/** last sample(s) in the input stream (left/right channel) */
|
||||
st_sample_t ilast[2];
|
||||
/** current sample(s) in the input stream (left/right channel) */
|
||||
st_sample_t icur[2];
|
||||
|
||||
public:
|
||||
LinearRateConverter(st_rate_t inrate, st_rate_t outrate);
|
||||
int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r);
|
||||
int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
|
||||
return (ST_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Prepare processing.
|
||||
*/
|
||||
template<bool stereo, bool reverseStereo>
|
||||
LinearRateConverter<stereo, reverseStereo>::LinearRateConverter(st_rate_t inrate, st_rate_t outrate) {
|
||||
unsigned long incr;
|
||||
|
||||
if (inrate == outrate) {
|
||||
error("Input and Output rates must be different to use rate effect");
|
||||
}
|
||||
|
||||
if (inrate >= 65536 || outrate >= 65536) {
|
||||
error("rate effect can only handle rates < 65536");
|
||||
}
|
||||
|
||||
opos_frac = 0;
|
||||
opos = 1;
|
||||
|
||||
/* increment */
|
||||
incr = (inrate << FRAC_BITS) / outrate;
|
||||
|
||||
opos_inc_frac = incr & ((1UL << FRAC_BITS) - 1);
|
||||
opos_inc = incr >> FRAC_BITS;
|
||||
|
||||
ipos = 0;
|
||||
|
||||
ilast[0] = ilast[1] = 0;
|
||||
icur[0] = icur[1] = 0;
|
||||
|
||||
inLen = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processed signed long samples from ibuf to obuf.
|
||||
* Return number of samples processed.
|
||||
*/
|
||||
template<bool stereo, bool reverseStereo>
|
||||
int LinearRateConverter<stereo, reverseStereo>::flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
|
||||
st_sample_t *ostart, *oend;
|
||||
st_sample_t out[2];
|
||||
|
||||
const int numChannels = stereo ? 2 : 1;
|
||||
int i;
|
||||
|
||||
ostart = obuf;
|
||||
oend = obuf + osamp * 2;
|
||||
|
||||
while (obuf < oend) {
|
||||
|
||||
// read enough input samples so that ipos > opos
|
||||
while (ipos <= opos) {
|
||||
// Check if we have to refill the buffer
|
||||
if (inLen == 0) {
|
||||
inPtr = inBuf;
|
||||
inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf));
|
||||
if (inLen <= 0)
|
||||
goto the_end;
|
||||
}
|
||||
for (i = 0; i < numChannels; i++) {
|
||||
ilast[i] = icur[i];
|
||||
icur[i] = *inPtr++;
|
||||
inLen--;
|
||||
}
|
||||
ipos++;
|
||||
}
|
||||
|
||||
// Loop as long as the outpos trails behind, and as long as there is
|
||||
// still space in the output buffer.
|
||||
while (ipos > opos) {
|
||||
|
||||
// interpolate
|
||||
out[0] = out[1] = (st_sample_t)(ilast[0] + (((icur[0] - ilast[0]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS));
|
||||
|
||||
if (stereo) {
|
||||
// interpolate
|
||||
out[reverseStereo ? 0 : 1] = (st_sample_t)(ilast[1] + (((icur[1] - ilast[1]) * opos_frac + (1UL << (FRAC_BITS-1))) >> FRAC_BITS));
|
||||
}
|
||||
|
||||
// output left channel
|
||||
clampedAdd(*obuf++, (out[0] * (int)vol_l) >> 8);
|
||||
|
||||
// output right channel
|
||||
clampedAdd(*obuf++, (out[1] * (int)vol_r) >> 8);
|
||||
|
||||
// Increment output position
|
||||
unsigned long tmp = opos_frac + opos_inc_frac;
|
||||
opos += opos_inc + (tmp >> FRAC_BITS);
|
||||
opos_frac = tmp & ((1UL << FRAC_BITS) - 1);
|
||||
|
||||
// Abort if we reached the end of the output buffer
|
||||
if (obuf >= oend)
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
|
||||
the_end:
|
||||
return (ST_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple audio rate converter for the case that the inrate equals the outrate.
|
||||
*/
|
||||
template<bool stereo, bool reverseStereo>
|
||||
class CopyRateConverter : public RateConverter {
|
||||
public:
|
||||
virtual int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) {
|
||||
int16 tmp[2];
|
||||
st_size_t len = osamp;
|
||||
assert(input.isStereo() == stereo);
|
||||
while (!input.eos() && len--) {
|
||||
tmp[0] = tmp[1] = input.read();
|
||||
if (stereo)
|
||||
tmp[reverseStereo ? 0 : 1] = input.read();
|
||||
|
||||
// output left channel
|
||||
clampedAdd(*obuf++, (tmp[0] * (int)vol_l) >> 8);
|
||||
|
||||
// output right channel
|
||||
clampedAdd(*obuf++, (tmp[1] * (int)vol_r) >> 8);
|
||||
}
|
||||
return (ST_SUCCESS);
|
||||
}
|
||||
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) {
|
||||
return (ST_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create and return a RateConverter object for the specified input and output rates.
|
||||
*/
|
||||
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo) {
|
||||
if (inrate != outrate) {
|
||||
if (stereo) {
|
||||
if (reverseStereo)
|
||||
return new LinearRateConverter<true, true>(inrate, outrate);
|
||||
else
|
||||
return new LinearRateConverter<true, false>(inrate, outrate);
|
||||
} else
|
||||
return new LinearRateConverter<false, false>(inrate, outrate);
|
||||
} else {
|
||||
if (stereo) {
|
||||
if (reverseStereo)
|
||||
return new CopyRateConverter<true, true>();
|
||||
else
|
||||
return new CopyRateConverter<true, false>();
|
||||
} else
|
||||
return new CopyRateConverter<false, false>();
|
||||
}
|
||||
}
|
73
mixer/rate.h
Normal file
73
mixer/rate.h
Normal file
@ -0,0 +1,73 @@
|
||||
// Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#ifndef SOUND_RATE_H
|
||||
#define SOUND_RATE_H
|
||||
|
||||
#include "../bits.h"
|
||||
|
||||
class AudioInputStream;
|
||||
|
||||
typedef int16 st_sample_t;
|
||||
typedef uint16 st_volume_t;
|
||||
typedef uint32 st_size_t;
|
||||
typedef uint32 st_rate_t;
|
||||
|
||||
/* Minimum and maximum values a sample can hold. */
|
||||
#define ST_SAMPLE_MAX 0x7fffL
|
||||
#define ST_SAMPLE_MIN (-ST_SAMPLE_MAX - 1L)
|
||||
|
||||
#define ST_EOF (-1)
|
||||
#define ST_SUCCESS (0)
|
||||
|
||||
static inline void clampedAdd(int16& a, int b) {
|
||||
register int val;
|
||||
#ifdef OUTPUT_UNSIGNED_AUDIO
|
||||
val = (a ^ 0x8000) + b;
|
||||
#else
|
||||
val = a + b;
|
||||
#endif
|
||||
|
||||
if (val > ST_SAMPLE_MAX)
|
||||
val = ST_SAMPLE_MAX;
|
||||
else if (val < ST_SAMPLE_MIN)
|
||||
val = ST_SAMPLE_MIN;
|
||||
|
||||
#ifdef OUTPUT_UNSIGNED_AUDIO
|
||||
a = ((int16)val) ^ 0x8000;
|
||||
#else
|
||||
a = val;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Q&D hack to get this SOX stuff to work
|
||||
#define st_report warning
|
||||
#define st_warn warning
|
||||
#define st_fail error
|
||||
|
||||
|
||||
class RateConverter {
|
||||
public:
|
||||
RateConverter() {}
|
||||
virtual ~RateConverter() {}
|
||||
virtual int flow(AudioInputStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) = 0;
|
||||
virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) = 0;
|
||||
};
|
||||
|
||||
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate, bool stereo, bool reverseStereo = false);
|
||||
|
||||
#endif
|
@ -191,12 +191,6 @@
|
||||
<File
|
||||
RelativePath="material.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="mixer.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="mixer.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="model.cpp">
|
||||
</File>
|
||||
@ -282,6 +276,40 @@
|
||||
RelativePath="vector3d.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="mixer"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="mixer\audiostream.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="mixer\audiostream.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="mixer\mixer.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="mixer\mixer.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="mixer\rate.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="mixer\rate.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
|
28
smush.cpp
28
smush.cpp
@ -19,10 +19,8 @@
|
||||
#include "bits.h"
|
||||
#include "debug.h"
|
||||
#include <cstring>
|
||||
//#include "file.h"
|
||||
//#include "util.h"
|
||||
#include "smush.h"
|
||||
//#include "system.h"
|
||||
#include "mixer/mixer.h"
|
||||
#include <SDL.h>
|
||||
|
||||
Smush::Smush() {
|
||||
@ -33,18 +31,11 @@ Smush::Smush() {
|
||||
_speed = 0;
|
||||
_channels = -1;
|
||||
_freq = 0;
|
||||
// g_system = new OSystem();
|
||||
// _mixer = new SoundMixer();
|
||||
// _mixer->bindToSystem(g_system);
|
||||
// _mixer->setVolume(100);
|
||||
// _mixer->setMusicVolume(100);
|
||||
// _soundHandle = 0;
|
||||
_soundHandle = 0;
|
||||
}
|
||||
|
||||
Smush::~Smush() {
|
||||
deinit();
|
||||
// delete g_system;
|
||||
// delete _mixer;
|
||||
}
|
||||
|
||||
void Smush::init() {
|
||||
@ -68,23 +59,24 @@ void Smush::handleBlocky16(byte *src) {
|
||||
void decompressVima(const char *src, int16 *dest, int destLen);
|
||||
void vimaInit();
|
||||
|
||||
extern SoundMixer *g_mixer;
|
||||
|
||||
void Smush::handleWave(const byte *src, uint32 size) {
|
||||
|
||||
int16 *dst = new int16[size * _channels];
|
||||
decompressVima((char *)src, dst, size * _channels * 2); delete dst;
|
||||
/* // convert our LE ones to BE
|
||||
for (uint32_t j = 0; j < size * _channels; j++)
|
||||
decompressVima((char *)src, dst, size * _channels * 2);
|
||||
// convert our LE ones to BE
|
||||
for (uint32 j = 0; j < size * _channels; j++)
|
||||
dst[j] = SWAP_BYTES_16(dst[j]);
|
||||
|
||||
int flags = SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE;
|
||||
if (_channels == 2)
|
||||
flags |= SoundMixer::FLAG_STEREO;
|
||||
if (_soundHandle == 0)
|
||||
_mixer->newStream(&_soundHandle, (byte *)dst, size * _channels * 2, _freq,
|
||||
g_mixer->newStream(&_soundHandle, (byte *)dst, size * _channels * 2, _freq,
|
||||
flags, 300000);
|
||||
else
|
||||
_mixer->appendStream(_soundHandle, (byte *)dst, size * _channels * 2);
|
||||
*/
|
||||
g_mixer->appendStream(_soundHandle, (byte *)dst, size * _channels * 2);
|
||||
}
|
||||
|
||||
void Smush::handleFrame() {
|
||||
@ -196,7 +188,7 @@ void Smush::play(const char *filename, const char *directory) {
|
||||
|
||||
// SDL_BlitSurface(image, &src, screen, NULL);
|
||||
// SDL_UpdateRect(screen, 0, 0, 0, 0);
|
||||
// SDL_Delay(_speed / 1000);
|
||||
SDL_Delay(_speed / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
5
smush.h
5
smush.h
@ -22,7 +22,7 @@
|
||||
#include "debug.h"
|
||||
#include <cstring>
|
||||
#include "blocky16.h"
|
||||
//#include "mixer.h"
|
||||
#include "mixer/mixer.h"
|
||||
|
||||
class File {
|
||||
private:
|
||||
@ -65,8 +65,7 @@ private:
|
||||
int32 _nbframes;
|
||||
Blocky16 _blocky16;
|
||||
File _file;
|
||||
// SoundMixer *_mixer;
|
||||
// PlayingSoundHandle _soundHandle;
|
||||
PlayingSoundHandle _soundHandle;
|
||||
|
||||
int32 _frame;
|
||||
bool _alreadyInit;
|
||||
|
391
sound.cpp
391
sound.cpp
@ -17,11 +17,402 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "sound.h"
|
||||
#include "mixer/mixer.h"
|
||||
#include "bits.h"
|
||||
#include "debug.h"
|
||||
#include <cstring>
|
||||
#include <SDL_endian.h>
|
||||
|
||||
struct imuseTableEntry {
|
||||
int stateNum;
|
||||
const char *filename;
|
||||
};
|
||||
|
||||
static const imuseTableEntry grimMusicTable[] = {
|
||||
{ 1001, "1001 - Manny's Office.IMC" },
|
||||
{ 1002, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1003, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1004, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1005, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1006, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1007, "1002 - Mr. Frustration.IMC" },
|
||||
{ 1008, "1008 - Domino's Office.IMC" },
|
||||
{ 1009, "1009 - Copal's Office.IMC" },
|
||||
{ 1010, "1010 - Ledge.IMC" },
|
||||
{ 1011, "1011 - Roof.IMC" },
|
||||
{ 1020, "1020 - Tube Room.IMC" },
|
||||
{ 1021, "1021 - Brennis.IMC" },
|
||||
{ 1022, "1022 - Lobby.IMC" },
|
||||
{ 1023, "1023 - Packing Room.IMC" },
|
||||
{ 1030, "1030 - Garage.IMC" },
|
||||
{ 1031, "1031 - Glottis' Shop.IMC" },
|
||||
{ 1032, "1030 - Garage.IMC" },
|
||||
{ 1040, "1040 - Festival Wet.IMC" },
|
||||
{ 1041, "1041 - Festival Dry.IMC" },
|
||||
{ 1042, "1041 - Festival Dry.IMC" },
|
||||
{ 1043, "1041 - Festival Dry.IMC" },
|
||||
{ 1044, "1040 - Festival Wet.IMC" },
|
||||
{ 1050, "1050 - Headquarters.IMC" },
|
||||
{ 1060, "1060 - Real World.IMC" },
|
||||
{ 1070, "1070 - Stump Room.IMC" },
|
||||
{ 1071, "1071 - Signpost Room.IMC" },
|
||||
{ 1072, "1072 - Navigation.IMC" },
|
||||
{ 1073, "1071 - Signpost Room.IMC" },
|
||||
{ 1074, "1074 - Bone Wagon.IMC" },
|
||||
{ 1075, "1075 - Spider's Eye.IMC" },
|
||||
{ 1076, "1076 - Spider Room.IMC" },
|
||||
{ 1077, "1077 - Tree Pump Amb.IMC" },
|
||||
{ 1078, "1078 - Tree Pump.IMC" },
|
||||
{ 1079, "1071 - Signpost Room.IMC" },
|
||||
{ 1080, "1080 - Beaver Room Lobby.IMC" },
|
||||
{ 1081, "1081 - Beaver Dam.IMC" },
|
||||
{ 1082, "1083 - Beaver Room.IMC" },
|
||||
{ 1083, "1083 - Beaver Room.IMC" },
|
||||
{ 1084, "1084 - Foggy Cactus.IMC" },
|
||||
{ 1085, "1085 - Rubamat Exterior.IMC" },
|
||||
{ 1086, "1086 - Blue Hector.IMC" },
|
||||
{ 1100, "1109 - Cafe Exterior.IMC" },
|
||||
{ 1101, "1101 - Cafe Office.IMC" },
|
||||
{ 1102, "1102 - Cafe Intercom.IMC" },
|
||||
{ 1103, "1103 - Coat Check.IMC" },
|
||||
{ 1104, "1104 - Lupe.IMC" },
|
||||
{ 1105, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1106, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1107, "1101 - Cafe Office.IMC" },
|
||||
{ 1108, "1108 - Casino Interior.IMC" },
|
||||
{ 1109, "1109 - Cafe Exterior.IMC" },
|
||||
{ 1110, "1110 - Cafe Ledge.IMC" },
|
||||
{ 1111, "1108 - Casino Interior.IMC" },
|
||||
{ 1112, "1112 - Rusty Sans Vox.IMC" },
|
||||
{ 1120, "1120 - Elevator Station.IMC" },
|
||||
{ 1121, "1122 - Blue Exterior.IMC" },
|
||||
{ 1122, "1122 - Blue Exterior.IMC" },
|
||||
{ 1123, "1123 - Blue Casket Ins.IMC" },
|
||||
{ 1124, "1124 - Blue Casket Amb.IMC" },
|
||||
{ 1125, "1125 - Smooth Hector.IMC" },
|
||||
{ 1126, "1122 - Blue Exterior.IMC" },
|
||||
{ 1127, "1127 - Limbo Dock.IMC" },
|
||||
{ 1128, "1128 - Limbo Talk.IMC" },
|
||||
{ 1129, "1129 - Limbo Poem.IMC" },
|
||||
{ 1130, "1130 - Dry Dock.IMC" },
|
||||
{ 1131, "1131 - Dry Dock Strike.IMC" },
|
||||
{ 1132, "1132 - Lighthouse Ext.IMC" },
|
||||
{ 1133, "1133 - Lola's Last.IMC" },
|
||||
{ 1140, "1140 - Police Station.IMC" },
|
||||
{ 1141, "1141 - Police Interior.IMC" },
|
||||
{ 1142, "1141 - Police Interior.IMC" },
|
||||
{ 1143, "1143 - Morgue.IMC" },
|
||||
{ 1144, "1140 - Police Station.IMC" },
|
||||
{ 1145, "1145 - Bridge Blimp.IMC" },
|
||||
{ 1146, "1146 - LOL Security Ext.IMC" },
|
||||
{ 1147, "1147 - LOL Security Int.IMC" },
|
||||
{ 1148, "1148 - Carla's Life.IMC" },
|
||||
{ 1149, "1149 - Bomb.IMC" },
|
||||
{ 1150, "1150 - Track Stairs.IMC" },
|
||||
{ 1151, "1151 - Track Stairs.IMC" },
|
||||
{ 1152, "1152 - Track Stairs.IMC" },
|
||||
{ 1153, "1153 - Track Base.IMC" },
|
||||
{ 1154, "1154 - Kitty Hall.IMC" },
|
||||
{ 1155, "1155 - Sanspoof.IMC" },
|
||||
{ 1156, "1156 - Kitty Stables.IMC" },
|
||||
{ 1160, "1160 - High Roller Hall.IMC" },
|
||||
{ 1161, "1161 - High Roller Lnge.IMC" },
|
||||
{ 1162, "1162 - Glottis Gambling.IMC" },
|
||||
{ 1163, "1163 - Max's Office.IMC" },
|
||||
{ 1164, "1125 - Hector Steps Out.IMC" },
|
||||
{ 1165, "1125 - Hector Steps Out.IMC" },
|
||||
{ 1166, "1125 - Hector Steps Out.IMC" },
|
||||
{ 1167, "1167 - Dillopede Elev.IMC" },
|
||||
{ 1168, "1168 - Dillopede Elev.IMC" },
|
||||
{ 1169, "1169 - Dillopede Elev.IMC" },
|
||||
{ 1170, "1170 - Extendo Bridge.IMC" },
|
||||
{ 1171, "1170 - Extendo Bridge.IMC" },
|
||||
{ 1172, "1170 - Extendo Bridge.IMC" },
|
||||
{ 1173, "1173 - Scrimshaw Int.IMC" },
|
||||
{ 1174, "1174 - Scrim Sleep.IMC" },
|
||||
{ 1180, "1180 - Note to Manny.IMC" },
|
||||
{ 1181, "1155 - Sanspoof.IMC" },
|
||||
{ 1190, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1191, "1106 - Glottis Noodle.IMC" },
|
||||
{ 1201, "1201 - Lola Zapata.IMC" },
|
||||
{ 1202, "1202 - Inside the Lola.IMC" },
|
||||
{ 1203, "1203 - Engine Room.IMC" },
|
||||
{ 1204, "1204 - Porthole.IMC" },
|
||||
{ 1205, "1204 - Porthole.IMC" },
|
||||
{ 1210, "1210 - Sunken Lola.IMC" },
|
||||
{ 1211, "1211 - Pearl Crater Sub.IMC" },
|
||||
{ 1220, "1220 - Miner's Room.IMC" },
|
||||
{ 1221, "1221 - Miner's Room.IMC" },
|
||||
{ 1222, "1222 - Exterior Airlock.IMC" },
|
||||
{ 1223, "1223 - Factory Hub.IMC" },
|
||||
{ 1224, "1224 - Foreman's Office.IMC" },
|
||||
{ 1230, "1230 - Vault Door.IMC" },
|
||||
{ 1231, "1231 - Outer Vault.IMC" },
|
||||
{ 1232, "1232 - Inner Vault.IMC" },
|
||||
{ 1233, "1233 - Ashtray Room.IMC" },
|
||||
{ 1234, "1234 - Ashtray Scary.IMC" },
|
||||
{ 1235, "1235 - Ashtray Pensive.IMC" },
|
||||
{ 1236, "1236 - Domino's Room.IMC" },
|
||||
{ 1240, "1240 - Conveyor Under.IMC" },
|
||||
{ 1241, "1240 - Conveyor Under.IMC" },
|
||||
{ 1242, "1241 - Crane Intro.IMC" },
|
||||
{ 1243, "1243 - Anchor Room.IMC" },
|
||||
{ 1244, "1244 - Glottis Hanging.IMC" },
|
||||
{ 1245, "1245 - End of the World.IMC" },
|
||||
{ 1246, "1246 - End World Later.IMC" },
|
||||
{ 1247, "1241 - Crane Intro.IMC" },
|
||||
{ 1250, "1250 - Upper Beach.IMC" },
|
||||
{ 1251, "1250 - Upper Beach.IMC" },
|
||||
{ 1252, "1252 - Lower Beach Boat.IMC" },
|
||||
{ 1253, "1253 - Lamancha Sub.IMC" },
|
||||
{ 1254, "1254 - Crane Later.IMC" },
|
||||
{ 1301, "1301 - Temple Gate.IMC" },
|
||||
{ 1302, "1301 - Temple Gate.IMC" },
|
||||
{ 1303, "1303 - Truck Depot.IMC" },
|
||||
{ 1304, "1304 - Mayan Train Sta.IMC" },
|
||||
{ 1305, "1305 - Mayan Workshop.IMC" },
|
||||
{ 1306, "1306 - Mayan Train Pad.IMC" },
|
||||
{ 1307, "1307 - Mechanic's Kitch.IMC" },
|
||||
{ 1310, "1310 - Jello Bomb.IMC" },
|
||||
{ 1311, "1310 - Jello Bomb.IMC" },
|
||||
{ 1312, "1125 - Smooth Hector.IMC" },
|
||||
{ 1313, "1125 - Smooth Hector.IMC" },
|
||||
{ 1314, "1125 - Smooth Hector.IMC" },
|
||||
{ 1315, "1122 - Blue Exterior.IMC" },
|
||||
{ 1316, "1122 - Blue Exterior.IMC" },
|
||||
{ 1317, "1122 - Blue Exterior.IMC" },
|
||||
{ 1318, "1332 - Hector's Foyer.IMC" },
|
||||
{ 1319, "1319 - Florist Video.IMC" },
|
||||
{ 1320, "1320 - New LSA HQ.IMC" },
|
||||
{ 1321, "1321 - LSA Sewer.IMC" },
|
||||
{ 1322, "1321 - LSA Sewer.IMC" },
|
||||
{ 1323, "1323 - Sewer Maze.IMC" },
|
||||
{ 1324, "1324 - Albinozod.IMC" },
|
||||
{ 1325, "1325 - Florist Shop.IMC" },
|
||||
{ 1326, "1326 - Florist Shop Int.IMC" },
|
||||
{ 1327, "1327 - Florist OK.IMC" },
|
||||
{ 1328, "1323 - Sewer Maze.IMC" },
|
||||
{ 1329, "1329 - Theater Backstag.IMC" },
|
||||
{ 1330, "1330 - Lemans Lobby.IMC" },
|
||||
{ 1331, "1330 - Lemans Lobby.IMC" },
|
||||
{ 1332, "1332 - Hector's Foyer.IMC" },
|
||||
{ 1333, "1333 - Brennis Talk.IMC" },
|
||||
{ 1334, "1334 - Albino Trap.IMC" },
|
||||
{ 1340, "1342 - Neon Ledge.IMC" },
|
||||
{ 1350, "1350 - Meadow Flowers.IMC" },
|
||||
{ 1351, "1351 - Meadow.IMC" },
|
||||
{ 1352, "1352 - Car Head.IMC" },
|
||||
{ 1353, "1353 - Greenhouse Appr.IMC" },
|
||||
{ 1354, "1354 - Game Ending.IMC" },
|
||||
{ 1355, "1355 - Shootout.IMC" },
|
||||
{ 1400, "1400 - Start Credits.IMC" },
|
||||
{ 1401, "1401 - Smooth Hector.IMC" },
|
||||
{ 2001, "2001 - Climb Rope.IMC" },
|
||||
{ 2010, "2010 - Glottis OK.IMC" },
|
||||
{ 2020, "2020 - Reap Bruno.IMC" },
|
||||
{ 2030, "2030 - Ledgepeckers.IMC" },
|
||||
{ 2050, "2050 - Glottis Heart.IMC" },
|
||||
{ 2055, "2055 - Slingshot Bone.IMC" },
|
||||
{ 2060, "2060 - Glott Tree Fall.IMC" },
|
||||
{ 2070, "2070 - Beaver Fly.IMC" },
|
||||
{ 2071, "2071 - Beaver Sink.IMC" },
|
||||
{ 2080, "2080 - Meet Velasco.IMC" },
|
||||
{ 2140, "2140 - Ooo Bonewagon.IMC" },
|
||||
{ 2141, "2141 - Ooo Meche.IMC" },
|
||||
{ 2155, "2155 - Find Detector.IMC" },
|
||||
{ 2156, "2156 - Glott Drink Wine.IMC" },
|
||||
{ 2157, "2157 - Glott No Wine.IMC" },
|
||||
{ 2161, "2161 - Raoul Appears.IMC" },
|
||||
{ 2162, "2162 - Raoul KO.IMC" },
|
||||
{ 2163, "2163 - Raoul Dissed.IMC" },
|
||||
{ 2165, "2165 - Fake Tix.IMC" },
|
||||
{ 2180, "2180 - Befriend Commies.IMC" },
|
||||
{ 2186, "2186 - Nick Punchout.IMC" },
|
||||
{ 2200, "2200 - Year 3 Iris.IMC" },
|
||||
{ 2210, "2210 - Hit Men.IMC" },
|
||||
{ 2230, "2230 - Open Vault.IMC" },
|
||||
{ 2235, "2235 - Dead Tix.IMC" },
|
||||
{ 2240, "2240 - Sprinkler.IMC" },
|
||||
{ 2250, "2250 - Crane Track.IMC" },
|
||||
{ 2255, "2255 - Crane Fall.IMC" },
|
||||
{ 2300, "2300 - Yr 4 Iris.IMC" },
|
||||
{ 2301, "2301 - Pop Bruno Casket.IMC" },
|
||||
{ 2310, "2310 - Rocket Idea.IMC" },
|
||||
{ 2320, "2320 - Jello Suspense.IMC" },
|
||||
{ 2325, "2325 - Lumbago Lemo.IMC" },
|
||||
{ 2327, "2327 - Breath Mint.IMC" },
|
||||
{ 2330, "2330 - Pigeon Fly.IMC" },
|
||||
{ 2340, "2340 - Coffee On Boys.IMC" },
|
||||
{ 2350, "2350 - Sprout Aha.IMC" },
|
||||
{ 2360, "2360 - Chowchilla Bye.IMC" },
|
||||
{ 2370, "2370 - Salvador Death.IMC" },
|
||||
{ 2399, "2399 - End Credits.IMC" }
|
||||
};
|
||||
|
||||
Mixer *Mixer::instance_ = NULL;
|
||||
|
||||
Mixer *Mixer::instance() {
|
||||
if (instance_ == NULL)
|
||||
instance_ = new Mixer;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void mixerCallback(void *userdata, int16 *stream, uint len) {
|
||||
Mixer *m = static_cast<Mixer *>(userdata);
|
||||
m->getAudio(stream, len * 2);
|
||||
}
|
||||
|
||||
extern SoundMixer *g_mixer;
|
||||
|
||||
Mixer::Mixer() :
|
||||
musicSound_(NULL), seqSound_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void Mixer::start() {
|
||||
Sound::init();
|
||||
g_mixer->setupPremix(mixerCallback, this);
|
||||
g_mixer->bindToSystem();
|
||||
g_mixer->setVolume(100);
|
||||
}
|
||||
|
||||
void Mixer::playVoice(Sound *s) {
|
||||
s->reset();
|
||||
voiceSounds_.push_back(s);
|
||||
}
|
||||
|
||||
void Mixer::playSfx(Sound *s) {
|
||||
s->reset();
|
||||
sfxSounds_.push_back(s);
|
||||
}
|
||||
|
||||
void Mixer::stopSfx(Sound *s) {
|
||||
for (sound_list::iterator i = sfxSounds_.begin();
|
||||
i != sfxSounds_.end(); ) {
|
||||
if (*i == s)
|
||||
i = sfxSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::stopVoice(Sound *s) {
|
||||
for (sound_list::iterator i = voiceSounds_.begin();
|
||||
i != voiceSounds_.end(); ) {
|
||||
if (*i == s)
|
||||
i = voiceSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static int compareStates(const void *p1, const void *p2) {
|
||||
const imuseTableEntry *e1 = static_cast<const imuseTableEntry *>(p1);
|
||||
const imuseTableEntry *e2 = static_cast<const imuseTableEntry *>(p2);
|
||||
return e1->stateNum - e2->stateNum;
|
||||
}
|
||||
|
||||
void Mixer::setImuseState(int state) {
|
||||
Sound *newSound = NULL;
|
||||
|
||||
if (state != 1000) {
|
||||
imuseTableEntry key;
|
||||
key.stateNum = state;
|
||||
const imuseTableEntry *e = static_cast<imuseTableEntry *>
|
||||
(std::bsearch(&key, grimMusicTable,
|
||||
sizeof(grimMusicTable) / sizeof(grimMusicTable[0]),
|
||||
sizeof(grimMusicTable[0]), compareStates));
|
||||
if (e == NULL) {
|
||||
warning("Unknown IMuse state %d\n", state);
|
||||
return;
|
||||
}
|
||||
|
||||
newSound = ResourceLoader::instance()->loadSound(e->filename);
|
||||
if (newSound == NULL) {
|
||||
warning("Could not find music file %s\n", e->filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (newSound != musicSound_) {
|
||||
if (newSound != NULL)
|
||||
newSound->reset();
|
||||
musicSound_ = newSound;
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::setImuseSeq(int state) {
|
||||
Sound *newSound = NULL;
|
||||
|
||||
if (state != 2000) {
|
||||
imuseTableEntry key;
|
||||
key.stateNum = state;
|
||||
const imuseTableEntry *e = static_cast<imuseTableEntry *>
|
||||
(std::bsearch(&key, grimMusicTable,
|
||||
sizeof(grimMusicTable) / sizeof(grimMusicTable[0]),
|
||||
sizeof(grimMusicTable[0]), compareStates));
|
||||
if (e == NULL) {
|
||||
warning("Unknown IMuse state %d\n", state);
|
||||
return;
|
||||
}
|
||||
|
||||
Sound *newSound = ResourceLoader::instance()->loadSound(e->filename);
|
||||
if (newSound == NULL) {
|
||||
warning("Could not find music file %s\n", e->filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (newSound != seqSound_) {
|
||||
if (newSound != NULL)
|
||||
newSound->reset();
|
||||
seqSound_ = newSound;
|
||||
}
|
||||
}
|
||||
|
||||
Sound *Mixer::findSfx(const char *filename) {
|
||||
for (sound_list::iterator i = sfxSounds_.begin();
|
||||
i != sfxSounds_.end(); i++) {
|
||||
if (std::strcmp((*i)->filename(), filename) == 0)
|
||||
return *i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Mixer::voicePlaying() const {
|
||||
return ! voiceSounds_.empty();
|
||||
}
|
||||
|
||||
void Mixer::getAudio(int16 *data, int numSamples) {
|
||||
memset(data, 0, numSamples * 2);
|
||||
for (sound_list::iterator i = voiceSounds_.begin();
|
||||
i != voiceSounds_.end(); ) {
|
||||
(*i)->mix(data, numSamples);
|
||||
if ((*i)->done())
|
||||
i = voiceSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
for (sound_list::iterator i = sfxSounds_.begin();
|
||||
i != sfxSounds_.end(); ) {
|
||||
(*i)->mix(data, numSamples);
|
||||
if ((*i)->done())
|
||||
i = sfxSounds_.erase(i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
if (seqSound_ != NULL) {
|
||||
seqSound_->mix(data, numSamples);
|
||||
if (seqSound_->done())
|
||||
seqSound_ = NULL;
|
||||
}
|
||||
else if (musicSound_ != NULL) {
|
||||
musicSound_->mix(data, numSamples);
|
||||
if (musicSound_->done())
|
||||
musicSound_->reset();
|
||||
}
|
||||
}
|
||||
|
||||
#define ST_SAMPLE_MAX 0x7fffL
|
||||
#define ST_SAMPLE_MIN (-ST_SAMPLE_MAX - 1L)
|
||||
|
||||
|
31
sound.h
31
sound.h
@ -20,6 +20,7 @@
|
||||
|
||||
#include "bits.h"
|
||||
#include "resource.h"
|
||||
#include <list>
|
||||
|
||||
class Sound : public Resource {
|
||||
public:
|
||||
@ -40,4 +41,34 @@ private:
|
||||
friend class Mixer;
|
||||
};
|
||||
|
||||
class Mixer {
|
||||
public:
|
||||
static Mixer *instance();
|
||||
|
||||
void start();
|
||||
|
||||
void playVoice(Sound *s);
|
||||
void playSfx(Sound *s);
|
||||
void stopSfx(Sound *s);
|
||||
void stopVoice(Sound *s);
|
||||
void setImuseState(int state);
|
||||
void setImuseSeq(int seq);
|
||||
|
||||
Sound *findSfx(const char *filename);
|
||||
bool voicePlaying() const;
|
||||
|
||||
void getAudio(int16 *data, int numSamples);
|
||||
|
||||
private:
|
||||
Mixer();
|
||||
~Mixer();
|
||||
|
||||
static Mixer *instance_;
|
||||
typedef std::list<ResPtr<Sound> > sound_list;
|
||||
sound_list voiceSounds_, sfxSounds_;
|
||||
ResPtr<Sound> musicSound_, seqSound_;
|
||||
|
||||
friend void mixerCallback(void *userdata, uint8 *stream, int len);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user