1 /*
2 * This file is part of Dune Legacy.
3 *
4 * Dune Legacy is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * Dune Legacy is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Dune Legacy. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <FileClasses/SFXManager.h>
19
20 #include <globals.h>
21
22 #include <FileClasses/FileManager.h>
23 #include <FileClasses/Vocfile.h>
24
25 #include <FileClasses/adl/sound_adlib.h>
26
27 #include <misc/sound_util.h>
28 #include <misc/exceptions.h>
29
30 // Not used:
31 // - EXCANNON.VOC (same as EXSMALL.VOC)
32 // - DROPEQ2P.VOC
33 // - POPPA.VOC
34
SFXManager()35 SFXManager::SFXManager() {
36 // load voice and language specific sounds
37 if(settings.general.language == "de") {
38 loadNonEnglishVoice("G");
39 } else if(settings.general.language == "fr") {
40 loadNonEnglishVoice("F");
41 } else {
42 loadEnglishVoice();
43 }
44
45 for(int i = 0; i < NUM_SOUNDCHUNK; i++) {
46 if(soundChunk[i] == nullptr) {
47 THROW(std::runtime_error, "Not all sounds could be loaded: soundChunk[%d] == nullptr!", i);
48 }
49 }
50 }
51
~SFXManager()52 SFXManager::~SFXManager() {
53 // unload voice
54 for(int i = 0; i < numLngVoice; i++) {
55 if(lngVoice[i] != nullptr) {
56 Mix_FreeChunk(lngVoice[i]);
57 lngVoice[i] = nullptr;
58 }
59 }
60
61 free(lngVoice);
62
63 // unload sound
64 for(int i = 0; i < NUM_SOUNDCHUNK; i++) {
65 if(soundChunk[i] != nullptr) {
66 Mix_FreeChunk(soundChunk[i]);
67 soundChunk[i] = nullptr;
68 }
69 }
70 }
71
getVoice(Voice_enum id,int house)72 Mix_Chunk* SFXManager::getVoice(Voice_enum id, int house) {
73 if(settings.general.language == "de" || settings.general.language == "fr") {
74 return getNonEnglishVoice(id,house);
75 } else {
76 return getEnglishVoice(id,house);
77 }
78 }
79
getSound(Sound_enum id)80 Mix_Chunk* SFXManager::getSound(Sound_enum id) {
81 if(id >= NUM_SOUNDCHUNK)
82 return nullptr;
83
84 return soundChunk[id];
85 }
86
loadMixFromADL(const std::string & adlFile,int index,int volume)87 Mix_Chunk* SFXManager::loadMixFromADL(const std::string& adlFile, int index, int volume) {
88
89 SDL_RWops* rwop = pFileManager->openFile(adlFile);
90 SoundAdlibPC *pSoundAdlibPC = new SoundAdlibPC(rwop, AUDIO_FREQUENCY);
91 pSoundAdlibPC->setVolume(volume);
92 Mix_Chunk* chunk = pSoundAdlibPC->getSubsong(index);
93 delete pSoundAdlibPC;
94 SDL_RWclose(rwop);
95
96 return chunk;
97 }
98
loadEnglishVoice()99 void SFXManager::loadEnglishVoice() {
100 numLngVoice = NUM_VOICE*NUM_HOUSES;
101
102 if((lngVoice = (Mix_Chunk**) malloc(sizeof(Mix_Chunk*) * numLngVoice)) == nullptr) {
103 THROW(std::runtime_error, "Cannot allocate memory");
104 }
105
106 for(int i = 0; i < numLngVoice; i++) {
107 lngVoice[i] = nullptr;
108 }
109
110 // now we can load
111 for(int house = 0; house < NUM_HOUSES; house++) {
112 Mix_Chunk* HouseNameChunk = nullptr;
113
114 std::string HouseString;
115 int VoiceNum = house;
116 switch(house) {
117 case HOUSE_HARKONNEN:
118 HouseString = "H";
119 HouseNameChunk = getChunkFromFile(HouseString + "HARK.VOC");
120 break;
121 case HOUSE_ATREIDES:
122 HouseString = "A";
123 HouseNameChunk = getChunkFromFile(HouseString + "ATRE.VOC");
124 break;
125 case HOUSE_ORDOS:
126 HouseString = "O";
127 HouseNameChunk = getChunkFromFile(HouseString + "ORDOS.VOC");
128 break;
129 case HOUSE_FREMEN:
130 HouseString = "A";
131 HouseNameChunk = getChunkFromFile(HouseString + "FREMEN.VOC");
132 break;
133 case HOUSE_SARDAUKAR:
134 HouseString = "H";
135 HouseNameChunk = getChunkFromFile(HouseString + "SARD.VOC");
136 break;
137 case HOUSE_MERCENARY:
138 HouseString = "O";
139 HouseNameChunk = getChunkFromFile(HouseString + "MERC.VOC");
140 break;
141 }
142
143 // "... Harvester deployed", "... Unit deployed" and "... Unit launched"
144 Mix_Chunk* Harvester = getChunkFromFile(HouseString + "HARVEST.VOC");
145 Mix_Chunk* Unit = getChunkFromFile(HouseString + "UNIT.VOC");
146 Mix_Chunk* Deployed = getChunkFromFile(HouseString + "DEPLOY.VOC");
147 Mix_Chunk* Launched = getChunkFromFile(HouseString + "LAUNCH.VOC");
148 lngVoice[HarvesterDeployed*NUM_HOUSES+VoiceNum] = concat3Chunks(HouseNameChunk, Harvester, Deployed);
149 lngVoice[UnitDeployed*NUM_HOUSES+VoiceNum] = concat3Chunks(HouseNameChunk, Unit, Deployed);
150 lngVoice[UnitLaunched*NUM_HOUSES+VoiceNum] = concat3Chunks(HouseNameChunk, Unit, Launched);
151 Mix_FreeChunk(Harvester);
152 Mix_FreeChunk(Unit);
153 Mix_FreeChunk(Deployed);
154 Mix_FreeChunk(Launched);
155
156 // "Contruction complete"
157 lngVoice[ConstructionComplete*NUM_HOUSES+VoiceNum] = getChunkFromFile(HouseString + "CONST.VOC");
158
159 // "Vehicle repaired"
160 Mix_Chunk* Vehicle = getChunkFromFile(HouseString + "VEHICLE.VOC");
161 Mix_Chunk* Repaired = getChunkFromFile(HouseString + "REPAIR.VOC");
162 lngVoice[VehicleRepaired*NUM_HOUSES+VoiceNum] = concat2Chunks(Vehicle, Repaired);
163 Mix_FreeChunk(Vehicle);
164 Mix_FreeChunk(Repaired);
165
166 // "Frigate has arrived"
167 Mix_Chunk* FrigateChunk = getChunkFromFile(HouseString + "FRIGATE.VOC");
168 Mix_Chunk* HasArrivedChunk = getChunkFromFile(HouseString + "ARRIVE.VOC");
169 lngVoice[FrigateHasArrived*NUM_HOUSES+VoiceNum] = concat2Chunks(FrigateChunk, HasArrivedChunk);
170 Mix_FreeChunk(FrigateChunk);
171 Mix_FreeChunk(HasArrivedChunk);
172
173 // "Your mission is complete"
174 lngVoice[YourMissionIsComplete*NUM_HOUSES+VoiceNum] = getChunkFromFile(HouseString + "WIN.VOC");
175
176 // "You have failed your mission"
177 lngVoice[YouHaveFailedYourMission*NUM_HOUSES+VoiceNum] = getChunkFromFile(HouseString + "LOSE.VOC");
178
179 // "Radar activated"/"Radar deactivated"
180 Mix_Chunk* RadarChunk = getChunkFromFile(HouseString + "RADAR.VOC");
181 Mix_Chunk* RadarActivatedChunk = getChunkFromFile(HouseString + "ON.VOC");
182 Mix_Chunk* RadarDeactivatedChunk = getChunkFromFile(HouseString + "OFF.VOC");
183 lngVoice[RadarActivated*NUM_HOUSES+VoiceNum] = concat2Chunks(RadarChunk, RadarActivatedChunk);
184 lngVoice[RadarDeactivated*NUM_HOUSES+VoiceNum] = concat2Chunks(RadarChunk, RadarDeactivatedChunk);
185 Mix_FreeChunk(RadarChunk);
186 Mix_FreeChunk(RadarActivatedChunk);
187 Mix_FreeChunk(RadarDeactivatedChunk);
188
189 // "Bloom located"
190 Mix_Chunk* Bloom = getChunkFromFile(HouseString + "BLOOM.VOC");
191 Mix_Chunk* Located = getChunkFromFile(HouseString + "LOCATED.VOC");
192 lngVoice[BloomLocated*NUM_HOUSES+VoiceNum] = concat2Chunks(Bloom, Located);
193 Mix_FreeChunk(Bloom);
194 Mix_FreeChunk(Located);
195
196 // "Warning Wormsign"
197 Mix_Chunk* WarningChunk = getChunkFromFile(HouseString + "WARNING.VOC");
198 Mix_Chunk* WormSignChunk = getChunkFromFile(HouseString + "WORMY.VOC");
199 lngVoice[WarningWormSign*NUM_HOUSES+VoiceNum] = concat2Chunks(WarningChunk, WormSignChunk);
200 Mix_FreeChunk(WarningChunk);
201 Mix_FreeChunk(WormSignChunk);
202
203 // "Our base is under attack"
204 lngVoice[BaseIsUnderAttack*NUM_HOUSES+VoiceNum] = getChunkFromFile(HouseString + "ATTACK.VOC");
205
206 // "Saboteur approaching" and "Missile approaching"
207 Mix_Chunk* SabotChunk = getChunkFromFile(HouseString + "SABOT.VOC");
208 Mix_Chunk* MissileChunk = getChunkFromFile(HouseString + "MISSILE.VOC");
209 Mix_Chunk* ApproachingChunk = getChunkFromFile(HouseString + "APPRCH.VOC");
210 lngVoice[SaboteurApproaching*NUM_HOUSES+VoiceNum] = concat2Chunks(SabotChunk, ApproachingChunk);
211 lngVoice[MissileApproaching*NUM_HOUSES+VoiceNum] = concat2Chunks(MissileChunk, ApproachingChunk);
212 Mix_FreeChunk(SabotChunk);
213 Mix_FreeChunk(MissileChunk);
214 Mix_FreeChunk(ApproachingChunk);
215
216 Mix_FreeChunk(HouseNameChunk);
217
218 // "Yes Sir"
219 lngVoice[YesSir*NUM_HOUSES+VoiceNum] = getChunkFromFile("ZREPORT1.VOC", "REPORT1.VOC");
220
221 // "Reporting"
222 lngVoice[Reporting*NUM_HOUSES+VoiceNum] = getChunkFromFile("ZREPORT2.VOC", "REPORT2.VOC");
223
224 // "Acknowledged"
225 lngVoice[Acknowledged*NUM_HOUSES+VoiceNum] = getChunkFromFile("ZREPORT3.VOC", "REPORT3.VOC");
226
227 // "Affirmative"
228 lngVoice[Affirmative*NUM_HOUSES+VoiceNum] = getChunkFromFile("ZAFFIRM.VOC", "AFFIRM.VOC");
229
230 // "Moving out"
231 lngVoice[MovingOut*NUM_HOUSES+VoiceNum] = getChunkFromFile("ZMOVEOUT.VOC", "MOVEOUT.VOC");
232
233 // "Infantry out"
234 lngVoice[InfantryOut*NUM_HOUSES+VoiceNum] = getChunkFromFile("ZOVEROUT.VOC", "OVEROUT.VOC");
235
236 // "Somthing's under the sand"
237 lngVoice[SomethingUnderTheSand*NUM_HOUSES+VoiceNum] = getChunkFromFile("SANDBUG.VOC");
238
239 // "House Harkonnen"
240 lngVoice[HouseHarkonnen*NUM_HOUSES+VoiceNum] = getChunkFromFile("MHARK.VOC");
241
242 // "House Atreides"
243 lngVoice[HouseAtreides*NUM_HOUSES+VoiceNum] = getChunkFromFile("MATRE.VOC");
244
245 // "House Ordos"
246 lngVoice[HouseOrdos*NUM_HOUSES+VoiceNum] = getChunkFromFile("MORDOS.VOC");
247 }
248
249 for(int i = 0; i < numLngVoice; i++) {
250 if(lngVoice[i] == nullptr) {
251 THROW(std::runtime_error, "Not all voice sounds could be loaded: lngVoice[%d] == nullptr!", i);
252 }
253 }
254
255 // Sfx
256 soundChunk[Sound_PlaceStructure] = getChunkFromFile("EXDUD.VOC");
257 soundChunk[Sound_ButtonClick] = getChunkFromFile("BUTTON.VOC");
258 soundChunk[Sound_InvalidAction] = loadMixFromADL("DUNE1.ADL", 47);
259 soundChunk[Sound_CreditsTick] = loadMixFromADL("DUNE1.ADL", 52, 4*MIX_MAX_VOLUME);
260 soundChunk[Sound_Tick] = loadMixFromADL("DUNE1.ADL", 38);
261 soundChunk[Sound_RadarNoise] = getChunkFromFile("STATICP.VOC");
262 soundChunk[Sound_ExplosionGas] = getChunkFromFile("EXGAS.VOC");
263 soundChunk[Sound_ExplosionTiny] = getChunkFromFile("EXTINY.VOC");
264 soundChunk[Sound_ExplosionSmall] = getChunkFromFile("EXSMALL.VOC");
265 soundChunk[Sound_ExplosionMedium] = getChunkFromFile("EXMED.VOC");
266 soundChunk[Sound_ExplosionLarge] = getChunkFromFile("EXLARGE.VOC");
267 soundChunk[Sound_ExplosionStructure] = getChunkFromFile("CRUMBLE.VOC");
268 soundChunk[Sound_WormAttack] = getChunkFromFile("WORMET3P.VOC");
269 soundChunk[Sound_Gun] = getChunkFromFile("GUN.VOC");
270 soundChunk[Sound_Rocket] = getChunkFromFile("ROCKET.VOC");
271 soundChunk[Sound_Bloom] = getChunkFromFile("EXSAND.VOC");
272 soundChunk[Sound_Scream1] = getChunkFromFile("VSCREAM1.VOC");
273 soundChunk[Sound_Scream2] = getChunkFromFile("VSCREAM2.VOC");
274 soundChunk[Sound_Scream3] = getChunkFromFile("VSCREAM3.VOC");
275 soundChunk[Sound_Scream4] = getChunkFromFile("VSCREAM4.VOC");
276 soundChunk[Sound_Scream5] = getChunkFromFile("VSCREAM5.VOC");
277 soundChunk[Sound_Trumpet] = loadMixFromADL("DUNE1.ADL", 30);
278 soundChunk[Sound_Drop] = loadMixFromADL("DUNE1.ADL", 24);
279 soundChunk[Sound_Squashed] = getChunkFromFile("SQUISH2.VOC");
280 soundChunk[Sound_MachineGun] = getChunkFromFile("GUNMULTI.VOC");
281 soundChunk[Sound_Sonic] = loadMixFromADL("DUNE1.ADL", 43);
282 soundChunk[Sound_RocketSmall] = getChunkFromFile("MISLTINP.VOC");
283 }
284
285
getEnglishVoice(Voice_enum id,int house)286 Mix_Chunk* SFXManager::getEnglishVoice(Voice_enum id, int house) {
287 if((int) id >= numLngVoice)
288 return nullptr;
289
290 return lngVoice[id*NUM_HOUSES + house];
291 }
292
loadNonEnglishVoice(const std::string & languagePrefix)293 void SFXManager::loadNonEnglishVoice(const std::string& languagePrefix) {
294 numLngVoice = NUM_VOICE;
295
296 if((lngVoice = (Mix_Chunk**) malloc(sizeof(Mix_Chunk*) * NUM_VOICE)) == nullptr) {
297 THROW(std::runtime_error, "Cannot allocate memory");
298 }
299
300 for(int i = 0; i < NUM_VOICE; i++) {
301 lngVoice[i] = nullptr;
302 }
303
304 // "Harvester deployed"
305 lngVoice[HarvesterDeployed] = getChunkFromFile(languagePrefix + "HARVEST.VOC");
306
307 // "Unit deployed"
308 lngVoice[UnitDeployed] = getChunkFromFile(languagePrefix + "DEPLOY.VOC");
309
310 // "Unit launched"
311 lngVoice[UnitLaunched] = getChunkFromFile(languagePrefix + "LAUNCH.VOC");
312
313 // "Contruction complete"
314 lngVoice[ConstructionComplete] = getChunkFromFile(languagePrefix + "CONST.VOC");
315
316 // "Vehicle repaired"
317 lngVoice[VehicleRepaired] = getChunkFromFile(languagePrefix + "REPAIR.VOC");
318
319 // "Frigate has arrived"
320 lngVoice[FrigateHasArrived] = getChunkFromFile(languagePrefix + "FRIGATE.VOC");
321
322 // "Your mission is complete" (No non-english voc available)
323 lngVoice[YourMissionIsComplete] = createEmptyChunk();
324
325 // "You have failed your mission" (No non-english voc available)
326 lngVoice[YouHaveFailedYourMission] = createEmptyChunk();
327
328 // "Radar activated"/"Radar deactivated"
329 lngVoice[RadarActivated] = getChunkFromFile(languagePrefix + "ON.VOC");
330 lngVoice[RadarDeactivated] = getChunkFromFile(languagePrefix + "OFF.VOC");
331
332 // "Bloom located"
333 lngVoice[BloomLocated] = getChunkFromFile(languagePrefix + "BLOOM.VOC");
334
335 // "Warning Wormsign"
336 if(pFileManager->exists(languagePrefix + "WORMY.VOC")) {
337 Mix_Chunk* WarningChunk = getChunkFromFile(languagePrefix + "WARNING.VOC");
338 Mix_Chunk* WormSignChunk = getChunkFromFile(languagePrefix + "WORMY.VOC");
339 lngVoice[WarningWormSign] = concat2Chunks(WarningChunk, WormSignChunk);
340 Mix_FreeChunk(WarningChunk);
341 Mix_FreeChunk(WormSignChunk);
342 } else {
343 lngVoice[WarningWormSign] = getChunkFromFile(languagePrefix + "WARNING.VOC");
344 }
345
346 // "Our base is under attack"
347 lngVoice[BaseIsUnderAttack] = getChunkFromFile(languagePrefix + "ATTACK.VOC");
348
349 // "Saboteur approaching"
350 lngVoice[SaboteurApproaching] = getChunkFromFile(languagePrefix + "SABOT.VOC");
351
352 // "Missile approaching"
353 lngVoice[MissileApproaching] = getChunkFromFile(languagePrefix + "MISSILE.VOC");
354
355 // "Yes Sir"
356 lngVoice[YesSir] = getChunkFromFile(languagePrefix + "REPORT1.VOC");
357
358 // "Reporting"
359 lngVoice[Reporting] = getChunkFromFile(languagePrefix + "REPORT2.VOC");
360
361 // "Acknowledged"
362 lngVoice[Acknowledged] = getChunkFromFile(languagePrefix + "REPORT3.VOC");
363
364 // "Affirmative"
365 lngVoice[Affirmative] = getChunkFromFile(languagePrefix + "AFFIRM.VOC");
366
367 // "Moving out"
368 lngVoice[MovingOut] = getChunkFromFile(languagePrefix + "MOVEOUT.VOC");
369
370 // "Infantry out"
371 lngVoice[InfantryOut] = getChunkFromFile(languagePrefix + "OVEROUT.VOC");
372
373 // "Somthing's under the sand"
374 lngVoice[SomethingUnderTheSand] = getChunkFromFile("SANDBUG.VOC");
375
376 // "House Atreides"
377 lngVoice[HouseAtreides] = getChunkFromFile(languagePrefix + "ATRE.VOC");
378
379 // "House Ordos"
380 lngVoice[HouseOrdos] = getChunkFromFile(languagePrefix + "ORDOS.VOC");
381
382 // "House Harkonnen"
383 lngVoice[HouseHarkonnen] = getChunkFromFile(languagePrefix + "HARK.VOC");
384
385 for(int i = 0; i < numLngVoice; i++) {
386 if(lngVoice[i] == nullptr) {
387 THROW(std::runtime_error, "Not all voice sounds could be loaded: lngVoice[%d] == nullptr!", i);
388 }
389 }
390
391 // Sfx
392 soundChunk[Sound_PlaceStructure] = getChunkFromFile("EXDUD.VOC");
393 soundChunk[Sound_ButtonClick] = getChunkFromFile("BUTTON.VOC");
394 soundChunk[Sound_InvalidAction] = loadMixFromADL("DUNE1.ADL", 47);
395 soundChunk[Sound_CreditsTick] = loadMixFromADL("DUNE1.ADL", 52, 4*MIX_MAX_VOLUME);
396 soundChunk[Sound_Tick] = loadMixFromADL("DUNE1.ADL", 38);
397 soundChunk[Sound_RadarNoise] = getChunkFromFile("STATICP.VOC");
398 soundChunk[Sound_ExplosionGas] = getChunkFromFile("EXGAS.VOC");
399 soundChunk[Sound_ExplosionTiny] = getChunkFromFile("EXTINY.VOC");
400 soundChunk[Sound_ExplosionSmall] = getChunkFromFile("EXSMALL.VOC");
401 soundChunk[Sound_ExplosionMedium] = getChunkFromFile("EXMED.VOC");
402 soundChunk[Sound_ExplosionLarge] = getChunkFromFile("EXLARGE.VOC");
403 soundChunk[Sound_ExplosionStructure] = getChunkFromFile("CRUMBLE.VOC");
404 soundChunk[Sound_WormAttack] = getChunkFromFile("WORMET3P.VOC");
405 soundChunk[Sound_Gun] = getChunkFromFile("GUN.VOC");
406 soundChunk[Sound_Rocket] = getChunkFromFile("ROCKET.VOC");
407 soundChunk[Sound_Bloom] = getChunkFromFile("EXSAND.VOC");
408 soundChunk[Sound_Scream1] = getChunkFromFile("VSCREAM1.VOC");
409 soundChunk[Sound_Scream2] = getChunkFromFile("VSCREAM2.VOC");
410 soundChunk[Sound_Scream3] = getChunkFromFile("VSCREAM3.VOC");
411 soundChunk[Sound_Scream4] = getChunkFromFile("VSCREAM4.VOC");
412 soundChunk[Sound_Scream5] = getChunkFromFile("VSCREAM5.VOC");
413 soundChunk[Sound_Trumpet] = loadMixFromADL("DUNE1.ADL", 30);
414 soundChunk[Sound_Drop] = loadMixFromADL("DUNE1.ADL", 24);
415 soundChunk[Sound_Squashed] = getChunkFromFile("SQUISH2.VOC");
416 soundChunk[Sound_MachineGun] = getChunkFromFile("GUNMULTI.VOC");
417 soundChunk[Sound_Sonic] = loadMixFromADL("DUNE1.ADL", 43);
418 soundChunk[Sound_RocketSmall] = getChunkFromFile("MISLTINP.VOC");
419 }
420
getNonEnglishVoice(Voice_enum id,int house)421 Mix_Chunk* SFXManager::getNonEnglishVoice(Voice_enum id, int house) {
422 if((int)id >= numLngVoice)
423 return nullptr;
424
425 return lngVoice[id];
426 }
427