1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/shared/std/string.h"
24 #include "ultima/nuvie/core/nuvie_defs.h"
25 #include "ultima/nuvie/conf/configuration.h"
26 #include "audio/mixer.h"
27 #include "ultima/nuvie/files/u6_lzw.h"
28 #include "ultima/nuvie/files/nuvie_io.h"
29 #include "ultima/nuvie/files/nuvie_io_file.h"
30 #include "ultima/nuvie/files/u6_lib_n.h"
31 #include "ultima/nuvie/sound/towns_sfx_manager.h"
32 
33 namespace Ultima {
34 namespace Nuvie {
35 
36 typedef struct { // sfx lookup
37 	uint16 sfx_id;
38 	uint8 towns_sample_num;
39 } TownsSfxLookup;
40 
41 #define TOWNS_SFX_TBL_SIZE 12
42 //15 hail effect
43 //16 explosion
44 //17 level not high enough, no effect etc.
45 //18 cast magic sound
46 //19 resurrection tune
47 static const TownsSfxLookup sfx_lookup_tbl[] = {
48 	{NUVIE_SFX_BLOCKED, 0},
49 	{NUVIE_SFX_HIT, 4},
50 	{NUVIE_SFX_BROKEN_GLASS, 12},
51 	{NUVIE_SFX_BELL, 13},
52 	{NUVIE_SFX_FOUNTAIN, 46},
53 	{NUVIE_SFX_PROTECTION_FIELD, 47},
54 	//FIXME {NUVIE_SFX_CLOCK, 1},
55 	{NUVIE_SFX_FIRE, 6},
56 	{NUVIE_SFX_RUBBER_DUCK, 3},
57 	{NUVIE_SFX_WATER_WHEEL, 48},
58 	{NUVIE_SFX_MISSLE, 9},
59 	{NUVIE_SFX_EXPLOSION, 16},
60 	{NUVIE_SFX_ATTACK_SWING, 2}
61 };
62 
TownsSfxManager(Configuration * cfg,Audio::Mixer * m)63 TownsSfxManager::TownsSfxManager(Configuration *cfg, Audio::Mixer *m) : SfxManager(cfg, m),
64 		fireStream(nullptr) {
65 	config->pathFromValue("config/townsdir", "sounds2.dat", sounds2dat_filepath);
66 	loadSound1Dat();
67 }
68 
~TownsSfxManager()69 TownsSfxManager::~TownsSfxManager() {
70 	//FIXME how do we make sure no sfxs are playing before deleting our resources?
71 	//delete fireStream;
72 	//free sounds1_dat[] buffers
73 }
74 
loadSound1Dat()75 void TownsSfxManager::loadSound1Dat() {
76 	Std::string filename;
77 	U6Lzw decompressor;
78 	U6Lib_n lib;
79 	NuvieIOBuffer iobuf;
80 	uint32 slib32_len = 0;
81 
82 	config->pathFromValue("config/townsdir", "sounds1.dat", filename);
83 	unsigned char *slib32_data = decompressor.decompress_file(filename, slib32_len);
84 
85 	if (slib32_len == 0)
86 		return;
87 
88 	iobuf.open(slib32_data, slib32_len);
89 	free(slib32_data);
90 
91 	if (!lib.open(&iobuf, 4))
92 		return;
93 
94 	uint8 i;
95 	for (i = 0; i < TOWNS_SFX_SOUNDS1_SIZE; i++) {
96 		sounds1_dat[i].buf = lib.get_item(i);
97 		sounds1_dat[i].len = lib.get_item_size(i);
98 	}
99 
100 	// Fire SFX is made up of three individual samples played in a random sequence
101 	Std::vector<Audio::RewindableAudioStream *> streams;
102 	streams.push_back(new FMtownsDecoderStream(sounds1_dat[6].buf, sounds1_dat[6].len));
103 	streams.push_back(new FMtownsDecoderStream(sounds1_dat[7].buf, sounds1_dat[7].len));
104 	streams.push_back(new FMtownsDecoderStream(sounds1_dat[8].buf, sounds1_dat[8].len));
105 
106 	fireStream = U6Audio::makeRandomCollectionAudioStream(mixer->getOutputRate(), false, streams, DisposeAfterUse::NO);
107 }
108 
playSfx(SfxIdType sfx_id,uint8 volume)109 bool TownsSfxManager::playSfx(SfxIdType sfx_id, uint8 volume) {
110 	return playSfxLooping(sfx_id, NULL, volume);
111 }
112 
113 
playSfxLooping(SfxIdType sfx_id,Audio::SoundHandle * handle,uint8 volume)114 bool TownsSfxManager::playSfxLooping(SfxIdType sfx_id, Audio::SoundHandle *handle, uint8 volume) {
115 	uint16 i = 0;
116 	for (i = 0; i < TOWNS_SFX_TBL_SIZE; i++) {
117 		if (sfx_lookup_tbl[i].sfx_id == sfx_id) {
118 			playSoundSample(sfx_lookup_tbl[i].towns_sample_num, handle, volume);
119 			return true;
120 		}
121 	}
122 	return false;
123 }
124 
playSoundSample(uint8 sample_num,Audio::SoundHandle * looping_handle,uint8 volume)125 void TownsSfxManager::playSoundSample(uint8 sample_num, Audio::SoundHandle *looping_handle, uint8 volume) {
126 	Audio::AudioStream *stream = NULL;
127 	Audio::SoundHandle handle;
128 
129 	if (sample_num > 5 && sample_num < 9) {
130 		// Fire samples
131 		mixer->playStream(Audio::Mixer::kPlainSoundType, looping_handle ? looping_handle : &handle, fireStream, -1, volume, 0, DisposeAfterUse::NO);
132 		return;
133 	}
134 
135 	if (sample_num < TOWNS_SFX_SOUNDS1_SIZE) {
136 		stream = new FMtownsDecoderStream(sounds1_dat[sample_num].buf, sounds1_dat[sample_num].len);
137 	} else {
138 		stream = new FMtownsDecoderStream(sounds2dat_filepath, sample_num - TOWNS_SFX_SOUNDS1_SIZE, false); //not compressed
139 	}
140 
141 	if (looping_handle) {
142 		Audio::RewindableAudioStream *rwStream = dynamic_cast<Audio::RewindableAudioStream *>(stream);
143 		Audio::LoopingAudioStream *looping_stream = new Audio::LoopingAudioStream(rwStream, 0);
144 		mixer->playStream(Audio::Mixer::kPlainSoundType, looping_handle, looping_stream, -1, volume);
145 	} else {
146 		mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, stream, -1, volume);
147 	}
148 }
149 
150 } // End of namespace Nuvie
151 } // End of namespace Ultima
152