1 /*
2 * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3 *
4 * This file is part of Arx Libertatis.
5 *
6 * Arx Libertatis is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Arx Libertatis is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
33 <http://www.gnu.org/licenses/>.
34
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Copyright (c) 1999-2000 ARKANE Studios SA. All rights reserved
44
45 #include "scene/GameSound.h"
46
47 #include <map>
48 #include <vector>
49 #include <sstream>
50 #include <cstdio>
51
52 #include <boost/algorithm/string/case_conv.hpp>
53
54 #include "animation/Animation.h"
55
56 #include "audio/Audio.h"
57
58 #include "core/Config.h"
59
60 #include "game/EntityManager.h"
61 #include "game/Inventory.h"
62 #include "game/NPC.h"
63 #include "game/Player.h"
64
65 #include "graphics/Math.h"
66 #include "graphics/particle/ParticleEffects.h"
67
68 #include "io/resource/ResourcePath.h"
69 #include "io/resource/PakReader.h"
70 #include "io/IniReader.h"
71 #include "io/log/Logger.h"
72
73 #include "platform/Platform.h"
74 #include "platform/Thread.h"
75
76 #include "scene/Interactive.h"
77
78 #include "util/String.h"
79
80 using std::map;
81 using std::string;
82 using std::istringstream;
83 using std::ostringstream;
84 using std::vector;
85 using std::sprintf;
86
87 using audio::AmbianceId;
88 using audio::SampleId;
89 using audio::SourceId;
90 using audio::EnvId;
91 using audio::INVALID_ID;
92 using audio::FLAG_VOLUME;
93 using audio::FLAG_POSITION;
94 using audio::FLAG_REVERBERATION;
95 using audio::FLAG_FALLOFF;
96 using audio::FLAG_PITCH;
97 using audio::FLAG_RELATIVE;
98 using audio::FLAG_AUTOFREE;
99
100 extern long EXTERNALVIEW;
101 extern Entity * CAMERACONTROLLER;
102
103
104 enum PlayingAmbianceType {
105 PLAYING_AMBIANCE_MENU,
106 PLAYING_AMBIANCE_SCRIPT,
107 PLAYING_AMBIANCE_ZONE
108 };
109
110 // TODO used for saving
111 struct PlayingAmbiance {
112 char name[256];
113 f32 volume;
114 s32 loop;
115 s32 type;
116 };
117
118 static const unsigned long ARX_SOUND_UPDATE_INTERVAL(100);
119 static const unsigned long ARX_SOUND_STREAMING_LIMIT(176400);
120 static const unsigned long MAX_MATERIALS(17);
121 static const unsigned long MAX_VARIANTS(5);
122 static const unsigned long AMBIANCE_FADE_TIME(2000);
123 static const float ARX_SOUND_UNIT_FACTOR(0.01F);
124 static const float ARX_SOUND_ROLLOFF_FACTOR(1.3F);
125 static const float ARX_SOUND_DEFAULT_FALLSTART(200.0F);
126
127 static const float ARX_SOUND_DEFAULT_FALLEND(2200.0F);
128 static const float ARX_SOUND_REFUSE_DISTANCE(2500.0F);
129
130 static const res::path ARX_SOUND_PATH_INI = "localisation";
131 static const char ARX_SOUND_PATH_SAMPLE[] = "sfx";
132 static const char ARX_SOUND_PATH_AMBIANCE[] = "sfx/ambiance";
133 static const char ARX_SOUND_PATH_ENVIRONMENT[] = "sfx/environment";
134 static const res::path ARX_SOUND_PRESENCE_NAME = "presence";
135 static const string ARX_SOUND_FILE_EXTENSION_WAV = ".wav";
136 static const string ARX_SOUND_FILE_EXTENSION_INI = ".ini";
137
138 static const unsigned long ARX_SOUND_COLLISION_MAP_COUNT = 3;
139 static const res::path ARX_SOUND_COLLISION_MAP_NAMES[] = {
140 "snd_armor",
141 "snd_step",
142 "snd_weapon"
143 };
144
145 static bool bIsActive(false);
146
147
148 static AmbianceId ambiance_zone(INVALID_ID);
149 static AmbianceId ambiance_menu = INVALID_ID;
150
151 static long Inter_Materials[MAX_MATERIALS][MAX_MATERIALS][MAX_VARIANTS];
152
153 namespace {
154
155 struct SoundMaterial {
156
157 vector<SampleId> variants;
158
SoundMaterial__anoncd60bbff0111::SoundMaterial159 SoundMaterial() : current(0) { }
160
~SoundMaterial__anoncd60bbff0111::SoundMaterial161 ~SoundMaterial() {
162 for(vector<SampleId>::const_iterator i = variants.begin(); i != variants.end(); ++i) {
163 audio::deleteSample(*i);
164 }
165 }
166
next__anoncd60bbff0111::SoundMaterial167 SampleId next() {
168 arx_assert(current < variants.size());
169 SampleId sample = variants[current];
170 current = (current + 1) % variants.size();
171 return sample;
172 }
173
174 private:
175
176 size_t current;
177
178 };
179
180 typedef map<string, SoundMaterial> CollisionMap;
181 typedef map<string, CollisionMap> CollisionMaps;
182 static CollisionMaps collisionMaps;
183
184 namespace Section {
185 static const string presence = "presence";
186 }
187
188 typedef map<res::path, float> PresenceFactors;
189 static PresenceFactors presence;
190
191 }
192
193
194
195 SampleId ARX_SOUND_MixerGame(INVALID_ID);
196 SampleId ARX_SOUND_MixerGameSample(INVALID_ID);
197 SampleId ARX_SOUND_MixerGameSpeech(INVALID_ID);
198 SampleId ARX_SOUND_MixerGameAmbiance(INVALID_ID);
199 SampleId ARX_SOUND_MixerMenu(INVALID_ID);
200 SampleId ARX_SOUND_MixerMenuSample(INVALID_ID);
201 SampleId ARX_SOUND_MixerMenuSpeech(INVALID_ID);
202 SampleId ARX_SOUND_MixerMenuAmbiance(INVALID_ID);
203
204 // Menu samples
205 SampleId SND_MENU_CLICK(INVALID_ID);
206 SampleId SND_MENU_RELEASE(INVALID_ID);
207
208 // Interface samples
209 SampleId SND_BACKPACK(INVALID_ID);
210 SampleId SND_BOOK_OPEN(INVALID_ID);
211 SampleId SND_BOOK_CLOSE(INVALID_ID);
212 SampleId SND_BOOK_PAGE_TURN(INVALID_ID);
213 SampleId SND_GOLD(INVALID_ID);
214 SampleId SND_INVSTD(INVALID_ID);
215 SampleId SND_SCROLL_OPEN(INVALID_ID);
216 SampleId SND_SCROLL_CLOSE(INVALID_ID);
217 SampleId SND_TORCH_START(INVALID_ID);
218 SampleId SND_TORCH_LOOP(INVALID_ID);
219 SampleId SND_TORCH_END(INVALID_ID);
220
221 // Other SFX samples
222 SampleId SND_FIREPLACE(INVALID_ID);
223 SampleId SND_PLOUF(INVALID_ID);
224 SampleId SND_QUAKE(INVALID_ID);
225 SampleId SND_WHOOSH(INVALID_ID);
226
227 // Player samples
228 SampleId SND_PLAYER_DEATH_BY_FIRE(INVALID_ID);
229
230 SampleId SND_PLAYER_FILLLIFEMANA(INVALID_ID);
231 SampleId SND_PLAYER_HEART_BEAT(INVALID_ID);
232 SampleId SND_PLAYER_LEVEL_UP(INVALID_ID);
233 SampleId SND_PLAYER_POISONED(INVALID_ID);
234
235 // Magic drawing samples
236 SampleId SND_MAGIC_AMBIENT(INVALID_ID);
237 SampleId SND_MAGIC_DRAW(INVALID_ID);
238 SampleId SND_MAGIC_FIZZLE(INVALID_ID);
239
240 // Magic symbols samples
241 SampleId SND_SYMB_AAM(INVALID_ID);
242 SampleId SND_SYMB_CETRIUS(INVALID_ID);
243 SampleId SND_SYMB_COSUM(INVALID_ID);
244 SampleId SND_SYMB_COMUNICATUM(INVALID_ID);
245 SampleId SND_SYMB_FOLGORA(INVALID_ID);
246 SampleId SND_SYMB_FRIDD(INVALID_ID);
247 SampleId SND_SYMB_KAOM(INVALID_ID);
248 SampleId SND_SYMB_MEGA(INVALID_ID);
249 SampleId SND_SYMB_MORTE(INVALID_ID);
250 SampleId SND_SYMB_MOVIS(INVALID_ID);
251 SampleId SND_SYMB_NHI(INVALID_ID);
252 SampleId SND_SYMB_RHAA(INVALID_ID);
253 SampleId SND_SYMB_SPACIUM(INVALID_ID);
254 SampleId SND_SYMB_STREGUM(INVALID_ID);
255 SampleId SND_SYMB_TAAR(INVALID_ID);
256 SampleId SND_SYMB_TEMPUS(INVALID_ID);
257 SampleId SND_SYMB_TERA(INVALID_ID);
258 SampleId SND_SYMB_VISTA(INVALID_ID);
259 SampleId SND_SYMB_VITAE(INVALID_ID);
260 SampleId SND_SYMB_YOK(INVALID_ID);
261
262 // Spells samples
263 SampleId SND_SPELL_ACTIVATE_PORTAL(INVALID_ID);
264 SampleId SND_SPELL_ARMOR_START(INVALID_ID);
265 SampleId SND_SPELL_ARMOR_END(INVALID_ID);
266 SampleId SND_SPELL_ARMOR_LOOP(INVALID_ID);
267 SampleId SND_SPELL_LOWER_ARMOR(INVALID_ID);
268 SampleId SND_SPELL_BLESS(INVALID_ID);
269 SampleId SND_SPELL_COLD_PROTECTION_START(INVALID_ID);
270 SampleId SND_SPELL_COLD_PROTECTION_LOOP(INVALID_ID);
271 SampleId SND_SPELL_COLD_PROTECTION_END(INVALID_ID);
272 SampleId SND_SPELL_CONFUSE(INVALID_ID);
273 SampleId SND_SPELL_CONTROL_TARGET(INVALID_ID);
274 SampleId SND_SPELL_CREATE_FIELD(INVALID_ID);
275 SampleId SND_SPELL_CREATE_FOOD(INVALID_ID);
276 SampleId SND_SPELL_CURE_POISON(INVALID_ID);
277 SampleId SND_SPELL_CURSE(INVALID_ID);
278 SampleId SND_SPELL_DETECT_TRAP(INVALID_ID);
279 SampleId SND_SPELL_DETECT_TRAP_LOOP(INVALID_ID);
280 SampleId SND_SPELL_DISARM_TRAP(INVALID_ID);
281 SampleId SND_SPELL_DISPELL_FIELD(INVALID_ID);
282 SampleId SND_SPELL_DISPELL_ILLUSION(INVALID_ID);
283 SampleId SND_SPELL_DOUSE(INVALID_ID);
284 SampleId SND_SPELL_ELECTRIC(INVALID_ID);
285 SampleId SND_SPELL_ENCHANT_WEAPON(INVALID_ID);
286 SampleId SND_SPELL_EXPLOSION(INVALID_ID);
287 SampleId SND_SPELL_EYEBALL_IN(INVALID_ID);
288 SampleId SND_SPELL_EYEBALL_OUT(INVALID_ID);
289 SampleId SND_SPELL_FIRE_FIELD(INVALID_ID);
290 SampleId SND_SPELL_FIRE_HIT(INVALID_ID);
291 SampleId SND_SPELL_FIRE_LAUNCH(INVALID_ID);
292 SampleId SND_SPELL_FIRE_PROTECTION(INVALID_ID);
293 SampleId SND_SPELL_FIRE_PROTECTION_LOOP(INVALID_ID);
294 SampleId SND_SPELL_FIRE_PROTECTION_END(INVALID_ID);
295 SampleId SND_SPELL_FIRE_WIND(INVALID_ID);
296 SampleId SND_SPELL_FREEZETIME(INVALID_ID);
297 SampleId SND_SPELL_HARM(INVALID_ID);
298 SampleId SND_SPELL_HEALING(INVALID_ID);
299 SampleId SND_SPELL_ICE_FIELD(INVALID_ID);
300 SampleId SND_SPELL_ICE_FIELD_LOOP(INVALID_ID);
301 SampleId SND_SPELL_ICE_FIELD_END(INVALID_ID);
302 SampleId SND_SPELL_ICE_PROJECTILE_LAUNCH(INVALID_ID);
303 SampleId SND_SPELL_INCINERATE(INVALID_ID);
304 SampleId SND_SPELL_INCINERATE_LOOP(INVALID_ID);
305 SampleId SND_SPELL_INCINERATE_END(INVALID_ID);
306 SampleId SND_SPELL_IGNITE(INVALID_ID);
307 SampleId SND_SPELL_INVISIBILITY_START(INVALID_ID);
308 SampleId SND_SPELL_INVISIBILITY_END(INVALID_ID);
309 SampleId SND_SPELL_LEVITATE_START(INVALID_ID);
310 SampleId SND_SPELL_LIGHTNING_START(INVALID_ID);
311 SampleId SND_SPELL_LIGHTNING_LOOP(INVALID_ID);
312 SampleId SND_SPELL_LIGHTNING_END(INVALID_ID);
313 SampleId SND_SPELL_MAGICAL_HIT(INVALID_ID);
314
315 //SampleId SND_SPELL_MASS_LIGHTNING_END(INVALID_ID);
316 SampleId SND_SPELL_FIRE_FIELD_START(INVALID_ID);
317 SampleId SND_SPELL_FIRE_FIELD_LOOP(INVALID_ID);
318 SampleId SND_SPELL_FIRE_FIELD_END(INVALID_ID);
319
320
321 SampleId SND_SPELL_MAGICAL_SHIELD(INVALID_ID);
322 SampleId SND_SPELL_MASS_INCINERATE(INVALID_ID);
323 SampleId SND_SPELL_MASS_PARALYSE(INVALID_ID);
324 SampleId SND_SPELL_MM_CREATE(INVALID_ID);
325 SampleId SND_SPELL_MM_HIT(INVALID_ID);
326 SampleId SND_SPELL_MM_LAUNCH(INVALID_ID);
327 SampleId SND_SPELL_MM_LOOP(INVALID_ID);
328 SampleId SND_SPELL_NEGATE_MAGIC(INVALID_ID);
329 SampleId SND_SPELL_NO_EFFECT(INVALID_ID);
330 SampleId SND_SPELL_PARALYSE(INVALID_ID);
331 SampleId SND_SPELL_PARALYSE_END(INVALID_ID);
332 SampleId SND_SPELL_POISON_PROJECTILE_LAUNCH(INVALID_ID);
333 SampleId SND_SPELL_RAISE_DEAD(INVALID_ID);
334 SampleId SND_SPELL_REPEL_UNDEAD(INVALID_ID);
335 SampleId SND_SPELL_REPEL_UNDEAD_LOOP(INVALID_ID);
336 SampleId SND_SPELL_RUNE_OF_GUARDING(INVALID_ID);
337 SampleId SND_SPELL_RUNE_OF_GUARDING_END(INVALID_ID);
338 SampleId SND_SPELL_SLOW_DOWN(INVALID_ID);
339 SampleId SND_SPELL_SPARK(INVALID_ID);
340 SampleId SND_SPELL_SPEED_START(INVALID_ID);
341 SampleId SND_SPELL_SPEED_LOOP(INVALID_ID);
342 SampleId SND_SPELL_SPEED_END(INVALID_ID);
343 SampleId SND_SPELL_SUMMON_CREATURE(INVALID_ID);
344 SampleId SND_SPELL_TELEKINESIS_START(INVALID_ID);
345 SampleId SND_SPELL_TELEKINESIS_END(INVALID_ID);
346 SampleId SND_SPELL_TELEPORT(INVALID_ID);
347 SampleId SND_SPELL_TELEPORTED(INVALID_ID);
348 SampleId SND_SPELL_VISION_START(INVALID_ID);
349 SampleId SND_SPELL_VISION_LOOP(INVALID_ID);
350
351 static void ARX_SOUND_EnvironmentSet(const res::path & name);
352 static void ARX_SOUND_CreateEnvironments();
353 static void ARX_SOUND_CreateStaticSamples();
354 static void ARX_SOUND_ReleaseStaticSamples();
355 static void ARX_SOUND_LoadCollision(const long & mat1, const long & mat2, const char * name);
356 static void ARX_SOUND_CreateCollisionMaps();
357 static void ARX_SOUND_CreateMaterials();
358 static void ARX_SOUND_CreatePresenceMap();
359 static float GetSamplePresenceFactor(const res::path & name);
360 static void ARX_SOUND_LaunchUpdateThread();
361 static void ARX_SOUND_KillUpdateThread();
362
ARX_SOUND_Init()363 bool ARX_SOUND_Init() {
364
365 if (bIsActive) ARX_SOUND_Release();
366
367 if(audio::init(config.audio.backend, config.audio.eax)) {
368 audio::clean();
369 return false;
370 }
371
372 if(audio::setSamplePath(ARX_SOUND_PATH_SAMPLE)
373 || audio::setAmbiancePath(ARX_SOUND_PATH_AMBIANCE)
374 || audio::setEnvironmentPath(ARX_SOUND_PATH_ENVIRONMENT)) {
375 audio::clean();
376 return false;
377 }
378
379 // Create game mixers
380 ARX_SOUND_MixerGame = audio::createMixer();
381 ARX_SOUND_MixerGameSample = audio::createMixer();
382 audio::setMixerParent(ARX_SOUND_MixerGameSample, ARX_SOUND_MixerGame);
383 ARX_SOUND_MixerGameSpeech = audio::createMixer();
384 audio::setMixerParent(ARX_SOUND_MixerGameSpeech, ARX_SOUND_MixerGame);
385 ARX_SOUND_MixerGameAmbiance = audio::createMixer();
386 audio::setMixerParent(ARX_SOUND_MixerGameAmbiance, ARX_SOUND_MixerGame);
387
388 // Create menu mixers
389 ARX_SOUND_MixerMenu = audio::createMixer();
390 ARX_SOUND_MixerMenuSample = audio::createMixer();
391 audio::setMixerParent(ARX_SOUND_MixerMenuSample, ARX_SOUND_MixerMenu);
392 ARX_SOUND_MixerMenuSpeech = audio::createMixer();
393 audio::setMixerParent(ARX_SOUND_MixerMenuSpeech, ARX_SOUND_MixerMenu);
394 ARX_SOUND_MixerMenuAmbiance = audio::createMixer();
395 audio::setMixerParent(ARX_SOUND_MixerMenuAmbiance, ARX_SOUND_MixerMenu);
396
397 if(ARX_SOUND_MixerGame == INVALID_ID
398 || ARX_SOUND_MixerGameSample == INVALID_ID
399 || ARX_SOUND_MixerGameSpeech == INVALID_ID
400 || ARX_SOUND_MixerGameAmbiance == INVALID_ID
401 || ARX_SOUND_MixerMenu == INVALID_ID
402 || ARX_SOUND_MixerMenuSample == INVALID_ID
403 || ARX_SOUND_MixerMenuSpeech == INVALID_ID
404 || ARX_SOUND_MixerMenuAmbiance == INVALID_ID) {
405 audio::clean();
406 return false;
407 }
408
409 audio::setStreamLimit(ARX_SOUND_STREAMING_LIMIT);
410
411 audio::setUnitFactor(ARX_SOUND_UNIT_FACTOR);
412 audio::setRolloffFactor(ARX_SOUND_ROLLOFF_FACTOR);
413
414 ARX_SOUND_LaunchUpdateThread();
415
416 bIsActive = true;
417
418 return true;
419 }
420
ARX_SOUND_LoadData()421 void ARX_SOUND_LoadData() {
422
423 if(!bIsActive) {
424 return;
425 }
426
427 // Load samples
428 ARX_SOUND_CreateStaticSamples();
429 ARX_SOUND_CreateMaterials();
430 ARX_SOUND_CreateCollisionMaps();
431 ARX_SOUND_CreatePresenceMap();
432
433 // Load environments, enable environment system and set default one if required
434 ARX_SOUND_CreateEnvironments();
435
436 if(config.audio.eax) {
437 audio::setReverbEnabled(true);
438 ARX_SOUND_EnvironmentSet("alley.aef");
439 }
440 }
441
ARX_SOUND_Release()442 void ARX_SOUND_Release() {
443
444 if(!bIsActive) {
445 return;
446 }
447
448 ARX_SOUND_ReleaseStaticSamples();
449 collisionMaps.clear();
450 presence.clear();
451 ARX_SOUND_KillUpdateThread();
452 audio::clean();
453 bIsActive = false;
454 }
455
ARX_SOUND_IsEnabled()456 long ARX_SOUND_IsEnabled()
457 {
458 return bIsActive ? 1 : 0;
459 }
460
ARX_SOUND_MixerSetVolume(audio::MixerId mixer_id,float volume)461 void ARX_SOUND_MixerSetVolume(audio::MixerId mixer_id, float volume) {
462 if(bIsActive) {
463 audio::setMixerVolume(mixer_id, volume);
464 }
465 }
466
ARX_SOUND_MixerStop(audio::MixerId mixer_id)467 void ARX_SOUND_MixerStop(audio::MixerId mixer_id) {
468 if(bIsActive) {
469 audio::mixerStop(mixer_id);
470 }
471 }
472
ARX_SOUND_MixerPause(audio::MixerId mixer_id)473 void ARX_SOUND_MixerPause(audio::MixerId mixer_id) {
474 if(bIsActive) {
475 audio::mixerPause(mixer_id);
476 }
477 }
478
ARX_SOUND_MixerResume(audio::MixerId mixer_id)479 void ARX_SOUND_MixerResume(audio::MixerId mixer_id) {
480 if(bIsActive) {
481 audio::mixerResume(mixer_id);
482 }
483 }
484
ARX_SOUND_MixerSwitch(audio::MixerId from,audio::MixerId to)485 void ARX_SOUND_MixerSwitch(audio::MixerId from, audio::MixerId to) {
486 ARX_SOUND_MixerPause(from);
487 ARX_SOUND_MixerResume(to);
488 }
489
490 // Sets the position of the listener
ARX_SOUND_SetListener(const Vec3f * position,const Vec3f * front,const Vec3f * up)491 void ARX_SOUND_SetListener(const Vec3f * position, const Vec3f * front, const Vec3f * up) {
492 if(bIsActive) {
493 audio::setListenerPosition(*position);
494 audio::setListenerDirection(*front, *up);
495 }
496 }
497
ARX_SOUND_EnvironmentSet(const res::path & name)498 void ARX_SOUND_EnvironmentSet(const res::path & name) {
499
500 if(bIsActive) {
501 EnvId e_id = audio::getEnvironment(name);
502 if(e_id != INVALID_ID) {
503 audio::setListenerEnvironment(e_id);
504 audio::setRoomRolloffFactor(ARX_SOUND_ROLLOFF_FACTOR);
505 }
506 }
507 }
508
ARX_SOUND_PlaySFX(SourceId & sample_id,const Vec3f * position,float pitch,SoundLoopMode loop)509 long ARX_SOUND_PlaySFX(SourceId & sample_id, const Vec3f * position, float pitch, SoundLoopMode loop) {
510
511 if(!bIsActive || sample_id == INVALID_ID) {
512 return INVALID_ID;
513 }
514
515 audio::Channel channel;
516 float presence;
517
518 channel.mixer = ARX_SOUND_MixerGameSample;
519 channel.flags = FLAG_VOLUME | FLAG_POSITION | FLAG_REVERBERATION | FLAG_FALLOFF;
520 channel.volume = 1.0F;
521
522 if(position) {
523 if(ACTIVECAM && distSqr(ACTIVECAM->pos, *position) > square(ARX_SOUND_REFUSE_DISTANCE)) {
524 return -1;
525 }
526 }
527
528 res::path sample_name;
529 audio::getSampleName(sample_id, sample_name);
530 presence = GetSamplePresenceFactor(sample_name);
531 channel.falloff.start = ARX_SOUND_DEFAULT_FALLSTART * presence;
532 channel.falloff.end = ARX_SOUND_DEFAULT_FALLEND * presence;
533
534 if(pitch != 1.0F) {
535 channel.flags |= FLAG_PITCH;
536 channel.pitch = pitch;
537 }
538
539 if(position) {
540 channel.position = *position;
541 } else {
542 channel.flags |= FLAG_RELATIVE;
543 channel.position = Vec3f::Z_AXIS;
544 }
545
546 audio::samplePlay(sample_id, channel, loop);
547
548 return sample_id;
549 }
550
551
ARX_SOUND_PlayInterface(SourceId & sample_id,float pitch,SoundLoopMode loop)552 long ARX_SOUND_PlayInterface(SourceId & sample_id, float pitch, SoundLoopMode loop) {
553
554 if (!bIsActive || sample_id == INVALID_ID) return INVALID_ID;
555
556 audio::Channel channel;
557
558 channel.mixer = ARX_SOUND_MixerGameSample;
559 channel.flags = FLAG_VOLUME;
560 channel.volume = 1.0F;
561
562 if (pitch != 1.0F) channel.flags |= FLAG_PITCH, channel.pitch = pitch;
563
564 audio::samplePlay(sample_id, channel, loop);
565
566 return sample_id;
567 }
568
ARX_SOUND_PlayMenu(SourceId & sample_id,float pitch,SoundLoopMode loop)569 long ARX_SOUND_PlayMenu(SourceId & sample_id, float pitch, SoundLoopMode loop) {
570
571 if (!bIsActive || sample_id == INVALID_ID) return INVALID_ID;
572
573 audio::Channel channel;
574
575 channel.mixer = ARX_SOUND_MixerMenuSample;
576 channel.flags = FLAG_VOLUME;
577 channel.volume = 1.0F;
578
579 if (pitch != 1.0F) channel.flags |= FLAG_PITCH, channel.pitch = pitch;
580
581 audio::samplePlay(sample_id, channel, loop);
582
583 return sample_id;
584 }
585
586
ARX_SOUND_IOFrontPos(const Entity * io,Vec3f & pos)587 void ARX_SOUND_IOFrontPos(const Entity * io, Vec3f & pos) {
588 if(io) {
589 pos.x = io->pos.x - EEsin(radians(MAKEANGLE(io->angle.b))) * 100.0F;
590 pos.y = io->pos.y - 100.0F;
591 pos.z = io->pos.z + EEcos(radians(MAKEANGLE(io->angle.b))) * 100.0F;
592 } else if(ACTIVECAM) {
593 pos.x = ACTIVECAM->pos.x - EEsin(radians(MAKEANGLE(ACTIVECAM->angle.b))) * 100.0F;
594 pos.y = ACTIVECAM->pos.y - 100.0F;
595 pos.z = ACTIVECAM->pos.z + EEcos(radians(MAKEANGLE(ACTIVECAM->angle.b))) * 100.0F;
596 } else {
597 pos = Vec3f::ZERO;
598 }
599 }
600
speechFileName(const res::path & name)601 static res::path speechFileName(const res::path & name) {
602 return res::path("speech") / config.language / name;
603 }
604
ARX_SOUND_PlaySpeech(const res::path & name,const Entity * io)605 long ARX_SOUND_PlaySpeech(const res::path & name, const Entity * io)
606 {
607 if (!bIsActive) return INVALID_ID;
608
609 audio::Channel channel;
610 SampleId sample_id;
611
612 res::path file = speechFileName(name);
613 file.set_ext(ARX_SOUND_FILE_EXTENSION_WAV);
614
615 sample_id = audio::createSample(file);
616
617 channel.mixer = ARX_SOUND_MixerGameSpeech;
618 channel.flags = FLAG_VOLUME | FLAG_POSITION | FLAG_REVERBERATION | FLAG_AUTOFREE | FLAG_FALLOFF;
619 channel.volume = 1.f;
620 channel.falloff.start = ARX_SOUND_DEFAULT_FALLSTART;
621 channel.falloff.end = ARX_SOUND_DEFAULT_FALLEND;
622
623 if (io)
624 {
625 if (((io == entities.player()) && !EXTERNALVIEW) ||
626 (io->ioflags & IO_CAMERA && io == CAMERACONTROLLER))
627 ARX_SOUND_IOFrontPos(io, channel.position);
628 else
629 {
630 channel.position = io->pos;
631 }
632
633 if(ACTIVECAM && distSqr(ACTIVECAM->pos, io->pos) > square(ARX_SOUND_REFUSE_DISTANCE)) {
634 return ARX_SOUND_TOO_FAR; // TODO sample is never freed!
635 }
636
637 if (io->ioflags & IO_NPC && io->_npcdata->speakpitch != 1.f)
638 {
639 channel.flags |= FLAG_PITCH;
640 channel.pitch = io->_npcdata->speakpitch;
641 }
642
643 }
644 else
645 {
646 channel.flags |= FLAG_RELATIVE;
647 channel.position = Vec3f::Z_AXIS * 100.f;
648 }
649
650 audio::samplePlay(sample_id, channel);
651
652 return sample_id;
653 }
654
ARX_SOUND_PlayCollision(long mat1,long mat2,float volume,float power,Vec3f * position,Entity * source)655 long ARX_SOUND_PlayCollision(long mat1, long mat2, float volume, float power, Vec3f * position, Entity * source)
656 {
657 if (!bIsActive) return 0;
658
659 if (mat1 == MATERIAL_NONE || mat2 == MATERIAL_NONE) return 0;
660
661 if (mat1 == MATERIAL_WATER || mat2 == MATERIAL_WATER)
662 ARX_PARTICLES_SpawnWaterSplash(position);
663
664 SampleId sample_id;
665
666 sample_id = Inter_Materials[mat1][mat2][0];
667
668 if (sample_id == INVALID_ID) return 0;
669
670 audio::Channel channel;
671 float presence;
672
673 channel.mixer = ARX_SOUND_MixerGameSample;
674
675 channel.flags = FLAG_VOLUME | FLAG_PITCH | FLAG_POSITION | FLAG_REVERBERATION | FLAG_FALLOFF;
676
677 res::path sample_name;
678 audio::getSampleName(sample_id, sample_name);
679 presence = GetSamplePresenceFactor(sample_name);
680 channel.falloff.start = ARX_SOUND_DEFAULT_FALLSTART * presence;
681 channel.falloff.end = ARX_SOUND_DEFAULT_FALLEND * presence;
682
683 if (position)
684 {
685 if (ACTIVECAM && distSqr(ACTIVECAM->pos, *position) > square(ARX_SOUND_REFUSE_DISTANCE))
686 return -1;
687 }
688
689 //Launch 'ON HEAR' script event
690 ARX_NPC_SpawnAudibleSound(position, source, power, presence);
691
692 if(position) {
693 channel.position = *position;
694 } else {
695 ARX_PLAYER_FrontPos(&channel.position);
696 }
697
698 channel.pitch = 0.9F + 0.2F * rnd();
699 channel.volume = volume;
700 audio::samplePlay(sample_id, channel);
701
702 size_t length;
703 audio::getSampleLength(sample_id, length);
704
705 return (long)(channel.pitch * length);
706 }
707
ARX_SOUND_PlayCollision(const string & name1,const string & name2,float volume,float power,Vec3f * position,Entity * source)708 long ARX_SOUND_PlayCollision(const string & name1, const string & name2, float volume, float power, Vec3f * position, Entity * source) {
709
710 if(!bIsActive) {
711 return 0;
712 }
713
714 if(name1.empty() || name2.empty()) {
715 return 0;
716 }
717
718 if(name2 == "water") {
719 ARX_PARTICLES_SpawnWaterSplash(position);
720 }
721
722 CollisionMaps::iterator mi = collisionMaps.find(name1);
723 if(mi == collisionMaps.end()) {
724 return 0;
725 }
726 CollisionMap & map = mi->second;
727
728 CollisionMap::iterator ci = map.find(name2);
729 if(ci == map.end()) {
730 return 0;
731 }
732 SoundMaterial & mat = ci->second;
733
734 SampleId sample_id = mat.next();
735 arx_assert(sample_id != INVALID_ID);
736
737 audio::Channel channel;
738 channel.mixer = ARX_SOUND_MixerGameSample;
739 channel.flags = FLAG_VOLUME | FLAG_PITCH | FLAG_POSITION | FLAG_REVERBERATION | FLAG_FALLOFF;
740
741 res::path sample_name;
742 audio::getSampleName(sample_id, sample_name);
743 float presence = GetSamplePresenceFactor(sample_name);
744 channel.falloff.start = ARX_SOUND_DEFAULT_FALLSTART * presence;
745 channel.falloff.end = ARX_SOUND_DEFAULT_FALLEND * presence;
746
747 // Launch 'ON HEAR' script event
748 ARX_NPC_SpawnAudibleSound(position, source, power, presence);
749
750 if(position) {
751 channel.position = *position;
752 if(ACTIVECAM && fartherThan(ACTIVECAM->pos, *position, ARX_SOUND_REFUSE_DISTANCE)) {
753 return -1;
754 }
755 } else {
756 ARX_PLAYER_FrontPos(&channel.position);
757 }
758
759 channel.pitch = 0.975f + 0.5f * rnd();
760 channel.volume = volume;
761 audio::samplePlay(sample_id, channel);
762
763 size_t length;
764 audio::getSampleLength(sample_id, length);
765
766 return (long)(channel.pitch * length);
767 }
768
ARX_SOUND_PlayScript(const res::path & name,const Entity * io,float pitch,SoundLoopMode loop)769 long ARX_SOUND_PlayScript(const res::path & name, const Entity * io, float pitch, SoundLoopMode loop)
770 {
771 if (!bIsActive) {
772 return INVALID_ID;
773 }
774
775 audio::Channel channel;
776 SampleId sample_id;
777
778 sample_id = audio::createSample(name);
779
780 if (sample_id == INVALID_ID) {
781 return INVALID_ID;
782 }
783
784 channel.mixer = ARX_SOUND_MixerGameSample;
785 channel.flags = FLAG_VOLUME | FLAG_AUTOFREE | FLAG_POSITION | FLAG_REVERBERATION | FLAG_FALLOFF;
786 channel.volume = 1.0F;
787 channel.falloff.start = ARX_SOUND_DEFAULT_FALLSTART * GetSamplePresenceFactor(name);
788 channel.falloff.end = ARX_SOUND_DEFAULT_FALLEND;
789
790 if(io) {
791 GetItemWorldPositionSound(io, &channel.position);
792 if(loop != ARX_SOUND_PLAY_LOOPED) {
793 if (ACTIVECAM && distSqr(ACTIVECAM->pos, channel.position) > square(ARX_SOUND_REFUSE_DISTANCE)) {
794 // TODO the sample will never be freed!
795 return ARX_SOUND_TOO_FAR;
796 }
797 }
798 } else {
799 channel.flags |= FLAG_RELATIVE;
800 channel.position = Vec3f::Z_AXIS * 100.f;
801 }
802
803 if(pitch != 1.0F) {
804 channel.flags |= FLAG_PITCH;
805 channel.pitch = pitch;
806 }
807
808 audio::samplePlay(sample_id, channel, loop);
809
810 return sample_id;
811 }
812
ARX_SOUND_PlayAnim(SourceId & sample_id,const Vec3f * position)813 long ARX_SOUND_PlayAnim(SourceId & sample_id, const Vec3f * position)
814 {
815 if (!bIsActive || sample_id == INVALID_ID) return INVALID_ID;
816
817 audio::Channel channel;
818
819 channel.mixer = ARX_SOUND_MixerGameSample;
820 channel.flags = FLAG_VOLUME;
821 channel.volume = 1.0F;
822
823 if(position) {
824 if(ACTIVECAM && fartherThan(ACTIVECAM->pos, *position, ARX_SOUND_REFUSE_DISTANCE)) {
825 return -1;
826 }
827 channel.flags |= FLAG_POSITION | FLAG_REVERBERATION | FLAG_FALLOFF;
828 res::path sample_name;
829 audio::getSampleName(sample_id, sample_name);
830 float presence = GetSamplePresenceFactor(sample_name);
831 channel.falloff.start = ARX_SOUND_DEFAULT_FALLSTART * presence;
832 channel.falloff.end = ARX_SOUND_DEFAULT_FALLEND * presence;
833 channel.position = *position;
834 }
835
836 audio::samplePlay(sample_id, channel);
837
838 return sample_id;
839 }
840
ARX_SOUND_PlayCinematic(const res::path & name,bool isSpeech)841 long ARX_SOUND_PlayCinematic(const res::path & name, bool isSpeech) {
842
843 res::path file = (isSpeech) ? speechFileName(name) : name;
844 file.set_ext(ARX_SOUND_FILE_EXTENSION_WAV);
845
846 s32 sample_id = audio::createSample(file);
847 if(sample_id == INVALID_ID) {
848 LogError << "Cannot load sound for cinematic: " << file;
849 return INVALID_ID;
850 }
851
852 audio::Channel channel;
853 channel.mixer = ARX_SOUND_MixerGameSpeech;
854 channel.flags = FLAG_VOLUME | FLAG_AUTOFREE | FLAG_POSITION | FLAG_FALLOFF
855 | FLAG_REVERBERATION | FLAG_POSITION;
856 channel.volume = 1.0f;
857 channel.falloff.start = ARX_SOUND_DEFAULT_FALLSTART;
858 channel.falloff.end = ARX_SOUND_DEFAULT_FALLEND;
859
860 if (ACTIVECAM) {
861 Vec3f front, up;
862 float t;
863 t = radians(MAKEANGLE(ACTIVECAM->angle.b));
864 front.x = -EEsin(t);
865 front.y = 0.f;
866 front.z = EEcos(t);
867 front.normalize();
868 up.x = 0.f;
869 up.y = 1.f;
870 up.z = 0.f;
871 ARX_SOUND_SetListener(&ACTIVECAM->pos, &front, &up);
872 }
873
874 ARX_SOUND_IOFrontPos(NULL, channel.position);
875
876 audio::samplePlay(sample_id, channel);
877
878 return sample_id;
879 }
880
ARX_SOUND_IsPlaying(SourceId & sample_id)881 long ARX_SOUND_IsPlaying(SourceId & sample_id) {
882 return bIsActive ? audio::isSamplePlaying(sample_id) : 0;
883 }
884
885
ARX_SOUND_GetDuration(SampleId & sample_id)886 float ARX_SOUND_GetDuration(SampleId & sample_id) {
887
888 if(bIsActive && sample_id != INVALID_ID) {
889 size_t length;
890 audio::getSampleLength(sample_id, length);
891 return static_cast<float>(length);
892 }
893
894 return 0.f;
895 }
896
ARX_SOUND_RefreshVolume(SourceId & sample_id,float volume)897 void ARX_SOUND_RefreshVolume(SourceId & sample_id, float volume) {
898 if(bIsActive && sample_id != INVALID_ID) {
899 audio::setSampleVolume(sample_id, volume);
900 }
901 }
902
ARX_SOUND_RefreshPitch(SourceId & sample_id,float pitch)903 void ARX_SOUND_RefreshPitch(SourceId & sample_id, float pitch) {
904 if(bIsActive && sample_id != INVALID_ID) {
905 audio::setSamplePitch(sample_id, pitch);
906 }
907 }
908
ARX_SOUND_RefreshPosition(SourceId & sample_id,const Vec3f * position)909 void ARX_SOUND_RefreshPosition(SourceId & sample_id, const Vec3f * position) {
910
911 if(bIsActive && sample_id != INVALID_ID) {
912 if(position) {
913 audio::setSamplePosition(sample_id, *position);
914 } else {
915 Vec3f pos;
916 ARX_PLAYER_FrontPos(&pos);
917 audio::setSamplePosition(sample_id, pos);
918 }
919 }
920 }
921
ARX_SOUND_RefreshSpeechPosition(SourceId & sample_id,const Entity * io)922 void ARX_SOUND_RefreshSpeechPosition(SourceId & sample_id, const Entity * io) {
923
924 if(!bIsActive || !io || sample_id == INVALID_ID) {
925 return;
926 }
927
928 Vec3f position;
929 if(io) {
930 if((io == entities.player() && !EXTERNALVIEW)
931 || ((io->ioflags & IO_CAMERA) && io == CAMERACONTROLLER)) {
932 ARX_SOUND_IOFrontPos(io, position);
933 } else {
934 position = io->pos;
935 }
936 }
937
938 audio::setSamplePosition(sample_id, position);
939 }
940
ARX_SOUND_Load(const res::path & name)941 SampleId ARX_SOUND_Load(const res::path & name) {
942
943 if(!bIsActive) {
944 return INVALID_ID;
945 }
946
947 res::path sample_name = name;
948
949 return audio::createSample(sample_name.set_ext(ARX_SOUND_FILE_EXTENSION_WAV));
950 }
951
ARX_SOUND_Free(const SampleId & sample)952 void ARX_SOUND_Free(const SampleId & sample) {
953 if(bIsActive && sample != INVALID_ID) {
954 audio::deleteSample(sample);
955 }
956 }
957
ARX_SOUND_Stop(SourceId & sample_id)958 void ARX_SOUND_Stop(SourceId & sample_id) {
959 if(bIsActive && sample_id != INVALID_ID) {
960 audio::sampleStop(sample_id);
961 }
962 }
963
ARX_SOUND_PlayScriptAmbiance(const res::path & name,SoundLoopMode loop,float volume)964 bool ARX_SOUND_PlayScriptAmbiance(const res::path & name, SoundLoopMode loop, float volume) {
965
966 if (!bIsActive) return INVALID_ID;
967
968 res::path temp = res::path(name).set_ext("amb");
969
970 AmbianceId ambiance_id = audio::getAmbiance(temp);
971
972 if (ambiance_id == INVALID_ID)
973 {
974 if (volume == 0.f) return true;
975
976 ambiance_id = audio::createAmbiance(temp);
977 if(ambiance_id == INVALID_ID) {
978 return false;
979 }
980
981 audio::setAmbianceUserData(ambiance_id, reinterpret_cast<void *>(PLAYING_AMBIANCE_SCRIPT));
982
983 audio::Channel channel;
984
985 channel.mixer = ARX_SOUND_MixerGameAmbiance;
986 channel.flags = FLAG_VOLUME | FLAG_AUTOFREE;
987 channel.volume = volume;
988
989 audio::ambiancePlay(ambiance_id, channel, loop == ARX_SOUND_PLAY_LOOPED);
990 }
991 else
992 {
993 if (volume <= 0.0F)
994 {
995 audio::deleteAmbiance(ambiance_id);
996 return true;
997 }
998
999 audio::setAmbianceVolume(ambiance_id, volume);
1000 }
1001
1002 return true;
1003 }
1004
ARX_SOUND_PlayZoneAmbiance(const res::path & name,SoundLoopMode loop,float volume)1005 bool ARX_SOUND_PlayZoneAmbiance(const res::path & name, SoundLoopMode loop, float volume) {
1006
1007 if (!bIsActive) return true;
1008
1009 if(name == "none") {
1010 audio::ambianceStop(ambiance_zone, AMBIANCE_FADE_TIME);
1011 ambiance_zone = INVALID_ID;
1012 return true;
1013 }
1014
1015 res::path temp = res::path(name).set_ext("amb");
1016
1017 AmbianceId ambiance_id = audio::getAmbiance(temp);
1018
1019 if (ambiance_id == INVALID_ID)
1020 {
1021 ambiance_id = audio::createAmbiance(temp);
1022 if(ambiance_id == INVALID_ID) {
1023 return false;
1024 }
1025 audio::setAmbianceUserData(ambiance_id, reinterpret_cast<void *>(PLAYING_AMBIANCE_ZONE));
1026 }
1027 else if (ambiance_id == ambiance_zone)
1028 return true;
1029
1030 audio::Channel channel;
1031
1032 channel.mixer = ARX_SOUND_MixerGameAmbiance;
1033 channel.flags = FLAG_VOLUME | FLAG_AUTOFREE;
1034 channel.volume = volume;
1035
1036 audio::ambianceStop(ambiance_zone, AMBIANCE_FADE_TIME);
1037 ambiance_zone = ambiance_id;
1038 audio::ambiancePlay(ambiance_zone, channel, loop == ARX_SOUND_PLAY_LOOPED, AMBIANCE_FADE_TIME);
1039
1040 return true;
1041 }
1042
ARX_SOUND_SetAmbianceTrackStatus(const string & ambiance_name,const string & track_name,unsigned long status)1043 AmbianceId ARX_SOUND_SetAmbianceTrackStatus(const string & ambiance_name, const string & track_name, unsigned long status) {
1044
1045 if(!bIsActive) {
1046 return INVALID_ID;
1047 }
1048
1049 AmbianceId ambiance_id = audio::getAmbiance(res::path(ambiance_name).set_ext("amb"));
1050 if(ambiance_id == INVALID_ID) {
1051 return INVALID_ID;
1052 }
1053
1054 audio::muteAmbianceTrack(ambiance_id, track_name, status != 0);
1055
1056 return ambiance_id;
1057 }
1058
ARX_SOUND_KillAmbiances()1059 void ARX_SOUND_KillAmbiances() {
1060
1061 if(!bIsActive) {
1062 return;
1063 }
1064
1065 AmbianceId ambiance_id = audio::getNextAmbiance();
1066
1067 while(ambiance_id != INVALID_ID) {
1068 audio::deleteAmbiance(ambiance_id);
1069 ambiance_id = audio::getNextAmbiance(ambiance_id);
1070 }
1071
1072 ambiance_zone = INVALID_ID;
1073 }
1074
ARX_SOUND_PlayMenuAmbiance(const res::path & ambiance_name)1075 AmbianceId ARX_SOUND_PlayMenuAmbiance(const res::path & ambiance_name) {
1076
1077 if(!bIsActive) {
1078 return INVALID_ID;
1079 }
1080
1081 audio::deleteAmbiance(ambiance_menu);
1082 ambiance_menu = audio::createAmbiance(ambiance_name);
1083
1084 audio::setAmbianceUserData(ambiance_menu, reinterpret_cast<void *>(PLAYING_AMBIANCE_MENU));
1085
1086 audio::Channel channel;
1087
1088 channel.mixer = ARX_SOUND_MixerMenuAmbiance;
1089 channel.flags = FLAG_VOLUME;
1090 channel.volume = 1.0F;
1091
1092 audio::ambiancePlay(ambiance_menu, channel, true);
1093
1094 return ambiance_menu;
1095 }
1096
1097 static long nbelems = 0;
1098 static char ** elems = NULL;
1099 static long * numbers = NULL;
1100
ARX_SOUND_FreeAnimSamples()1101 void ARX_SOUND_FreeAnimSamples() {
1102
1103 if(elems) {
1104 for(long i = 0; i < nbelems; i++) {
1105 free(elems[i]), elems[i] = NULL;
1106 }
1107 free(elems), elems = NULL;
1108 }
1109 nbelems = 0;
1110
1111 free(numbers), numbers = NULL;
1112 }
1113
1114 extern ANIM_HANDLE animations[];
ARX_SOUND_PushAnimSamples()1115 void ARX_SOUND_PushAnimSamples()
1116 {
1117 ARX_SOUND_FreeAnimSamples();
1118
1119 long number = 0;
1120
1121 for(size_t i = 0; i < MAX_ANIMATIONS; i++) {
1122
1123 if (!animations[i].path.empty())
1124 {
1125 for (long j = 0; j < animations[i].alt_nb; j++)
1126 {
1127 EERIE_ANIM * anim = animations[i].anims[j];
1128
1129 for (long k = 0; k < anim->nb_key_frames; k++)
1130 {
1131 number++;
1132
1133 if (anim->frames[k].sample != -1)
1134 {
1135 res::path dest;
1136 audio::getSampleName(anim->frames[k].sample, dest);
1137 if(!dest.empty()) {
1138 elems = (char **)realloc(elems, sizeof(char *) * (nbelems + 1));
1139 elems[nbelems] = strdup(dest.string().c_str());
1140 numbers = (long *)realloc(numbers, sizeof(long) * (nbelems + 1));
1141 numbers[nbelems] = number;
1142 nbelems++;
1143 }
1144 }
1145 }
1146 }
1147 }
1148 }
1149 }
1150
ARX_SOUND_PopAnimSamples()1151 void ARX_SOUND_PopAnimSamples()
1152 {
1153 if ((!elems) ||
1154 (!bIsActive))
1155 {
1156 return;
1157 }
1158
1159 long curelem = 0;
1160 long number = 0;
1161
1162 for(size_t i = 0; i < MAX_ANIMATIONS; i++) {
1163
1164 if (!animations[i].path.empty())
1165 {
1166 for (long j = 0; j < animations[i].alt_nb; j++)
1167 {
1168 EERIE_ANIM * anim = animations[i].anims[j];
1169
1170 for (long k = 0; k < anim->nb_key_frames; k++)
1171 {
1172 number++;
1173
1174 if (number == numbers[curelem])
1175 {
1176 arx_assert(elems[curelem] != NULL);
1177 anim->frames[k].sample = audio::createSample(elems[curelem++]);
1178 }
1179 }
1180 }
1181 }
1182 }
1183
1184
1185 ARX_SOUND_FreeAnimSamples();
1186 }
1187
ARX_SOUND_AmbianceSavePlayList(size_t & size)1188 char * ARX_SOUND_AmbianceSavePlayList(size_t & size) {
1189
1190 unsigned long count(0);
1191 PlayingAmbiance * play_list = NULL;
1192 long ambiance_id(INVALID_ID);
1193
1194 ambiance_id = audio::getNextAmbiance();
1195
1196 while (ambiance_id != INVALID_ID)
1197 {
1198 void * storedType;
1199 audio::getAmbianceUserData(ambiance_id, &storedType);
1200 long type = reinterpret_cast<long>(storedType);
1201
1202 if (type == PLAYING_AMBIANCE_SCRIPT || type == PLAYING_AMBIANCE_ZONE)
1203 {
1204 void * ptr;
1205 PlayingAmbiance * playing;
1206
1207 ptr = realloc(play_list, (count + 1) * sizeof(PlayingAmbiance));
1208
1209 if (!ptr) break;
1210
1211 play_list = (PlayingAmbiance *)ptr;
1212 playing = &play_list[count];
1213
1214 memset(playing->name, 0, sizeof(playing->name));
1215
1216 res::path name;
1217 audio::getAmbianceName(ambiance_id, name);
1218 arx_assert(name.string().length() + 1 < ARRAY_SIZE(playing->name));
1219 strcpy(playing->name, name.string().c_str());
1220 audio::getAmbianceVolume(ambiance_id, playing->volume);
1221 playing->loop = audio::isAmbianceLooped(ambiance_id) ? ARX_SOUND_PLAY_LOOPED : ARX_SOUND_PLAY_ONCE;
1222 playing->type = type;
1223
1224 count++;
1225 }
1226
1227 ambiance_id = audio::getNextAmbiance(ambiance_id);
1228 }
1229
1230 size = count * sizeof(PlayingAmbiance);
1231 return reinterpret_cast<char *>(play_list);
1232 }
1233
ARX_SOUND_AmbianceRestorePlayList(const char * _play_list,size_t size)1234 void ARX_SOUND_AmbianceRestorePlayList(const char * _play_list, size_t size) {
1235
1236 size_t count = size / sizeof(PlayingAmbiance);
1237 const PlayingAmbiance * play_list = reinterpret_cast<const PlayingAmbiance *>(_play_list);
1238
1239 for(size_t i = 0; i < count; i++) {
1240
1241 const PlayingAmbiance * playing = &play_list[i];
1242
1243 res::path name = res::path::load(util::loadString(playing->name));
1244
1245 // TODO save/load enum
1246 switch (playing->type) {
1247
1248 case PLAYING_AMBIANCE_SCRIPT :
1249 ARX_SOUND_PlayScriptAmbiance(name, (SoundLoopMode)playing->loop, playing->volume);
1250 break;
1251
1252 case PLAYING_AMBIANCE_ZONE :
1253 ARX_SOUND_PlayZoneAmbiance(name, (SoundLoopMode)playing->loop, playing->volume);
1254 break;
1255 }
1256 }
1257 }
1258
ARX_SOUND_CreateEnvironments()1259 static void ARX_SOUND_CreateEnvironments() {
1260
1261 PakDirectory * dir = resources->getDirectory(ARX_SOUND_PATH_ENVIRONMENT);
1262 if(!dir) {
1263 return;
1264 }
1265
1266 for(PakDirectory::files_iterator i = dir->files_begin(); i != dir->files_end(); i++) {
1267 audio::createEnvironment(i->first);
1268 }
1269 }
1270
ARX_SOUND_CreateStaticSamples()1271 static void ARX_SOUND_CreateStaticSamples() {
1272
1273 // Interface
1274 SND_BACKPACK = audio::createSample("interface_backpack.wav");
1275 SND_BOOK_OPEN = audio::createSample("book_open.wav");
1276 SND_BOOK_CLOSE = audio::createSample("book_close.wav");
1277 SND_BOOK_PAGE_TURN = audio::createSample("book_page_turn.wav");
1278 SND_SCROLL_OPEN = audio::createSample("scroll_open.wav");
1279 SND_SCROLL_CLOSE = audio::createSample("scroll_close.wav");
1280 SND_TORCH_START = audio::createSample("torch_start.wav");
1281 SND_TORCH_LOOP = audio::createSample("sfx_torch_11khz.wav");
1282 SND_TORCH_END = audio::createSample("torch_end.wav");
1283 SND_INVSTD = audio::createSample("interface_invstd.wav");
1284 SND_GOLD = audio::createSample("drop_coin.wav");
1285
1286 //Menu
1287 SND_MENU_CLICK = audio::createSample("menu_click.wav");
1288 SND_MENU_RELEASE = audio::createSample("menu_release.wav");
1289
1290 //Other SFX samples
1291 SND_FIREPLACE = audio::createSample("fire_place.wav");
1292 SND_PLOUF = audio::createSample("fishing_plouf.wav");
1293 SND_QUAKE = audio::createSample("sfx_quake.wav");
1294 SND_WHOOSH = audio::createSample("whoosh07.wav");
1295
1296 // Player
1297 SND_PLAYER_FILLLIFEMANA = audio::createSample("player_filllifemana.wav");
1298 SND_PLAYER_HEART_BEAT = audio::createSample("player_heartb.wav");
1299 SND_PLAYER_LEVEL_UP = audio::createSample("player_level_up.wav");
1300 SND_PLAYER_POISONED = audio::createSample("player_poisoned.wav");
1301 SND_PLAYER_DEATH_BY_FIRE = audio::createSample("lava_death.wav");
1302
1303 // Magic draw
1304 SND_MAGIC_AMBIENT = audio::createSample("magic_ambient.wav");
1305 SND_MAGIC_DRAW = audio::createSample("magic_draw.wav");
1306 SND_MAGIC_FIZZLE = audio::createSample("magic_fizzle.wav");
1307
1308 // Magic symbols
1309 SND_SYMB_AAM = audio::createSample("magic_aam.wav");
1310 SND_SYMB_CETRIUS = audio::createSample("magic_citrius.wav");
1311 SND_SYMB_COSUM = audio::createSample("magic_cosum.wav");
1312 SND_SYMB_COMUNICATUM = audio::createSample("magic_comunicatum.wav");
1313 SND_SYMB_FOLGORA = audio::createSample("magic_folgora.wav");
1314 SND_SYMB_FRIDD = audio::createSample("magic_fridd.wav");
1315 SND_SYMB_KAOM = audio::createSample("magic_kaom.wav");
1316 SND_SYMB_MEGA = audio::createSample("magic_mega.wav");
1317 SND_SYMB_MORTE = audio::createSample("magic_morte.wav");
1318 SND_SYMB_MOVIS = audio::createSample("magic_movis.wav");
1319 SND_SYMB_NHI = audio::createSample("magic_nhi.wav");
1320 SND_SYMB_RHAA = audio::createSample("magic_rhaa.wav");
1321 SND_SYMB_SPACIUM = audio::createSample("magic_spacium.wav");
1322 SND_SYMB_STREGUM = audio::createSample("magic_stregum.wav");
1323 SND_SYMB_TAAR = audio::createSample("magic_taar.wav");
1324 SND_SYMB_TEMPUS = audio::createSample("magic_tempus.wav");
1325 SND_SYMB_TERA = audio::createSample("magic_tera.wav");
1326 SND_SYMB_VISTA = audio::createSample("magic_vista.wav");
1327 SND_SYMB_VITAE = audio::createSample("magic_vitae.wav");
1328 SND_SYMB_YOK = audio::createSample("magic_yok.wav");
1329
1330 // Spells
1331 SND_SPELL_ACTIVATE_PORTAL = audio::createSample("magic_spell_activate_portal.wav");
1332 SND_SPELL_ARMOR_START = audio::createSample("magic_spell_armor_start.wav");
1333 SND_SPELL_ARMOR_END = audio::createSample("magic_spell_armor_end.wav");
1334 SND_SPELL_ARMOR_LOOP = audio::createSample("magic_spell_armor_loop.wav");
1335 SND_SPELL_LOWER_ARMOR = audio::createSample("magic_spell_decrease_armor.wav");
1336 SND_SPELL_BLESS = audio::createSample("magic_spell_bless.wav");
1337 SND_SPELL_COLD_PROTECTION_START = audio::createSample("magic_spell_cold_protection.wav");
1338 SND_SPELL_COLD_PROTECTION_LOOP = audio::createSample("magic_spell_cold_protection_loop.wav");
1339 SND_SPELL_COLD_PROTECTION_END = audio::createSample("magic_spell_cold_protection_end.wav");
1340 SND_SPELL_CONFUSE = audio::createSample("magic_spell_confuse.wav");
1341 SND_SPELL_CONTROL_TARGET = audio::createSample("magic_spell_control_target.wav");
1342 SND_SPELL_CREATE_FIELD = audio::createSample("magic_spell_create_field.wav");
1343 SND_SPELL_CREATE_FOOD = audio::createSample("magic_spell_create_food.wav");
1344 SND_SPELL_CURE_POISON = audio::createSample("magic_spell_cure_poison.wav");
1345 SND_SPELL_CURSE = audio::createSample("magic_spell_curse.wav");
1346 SND_SPELL_DETECT_TRAP = audio::createSample("magic_spell_detect_trap.wav");
1347 SND_SPELL_DETECT_TRAP_LOOP = audio::createSample("magic_spell_detect_trap_loop.wav");
1348 SND_SPELL_DISARM_TRAP = audio::createSample("magic_spell_disarm_trap.wav");
1349 SND_SPELL_DISPELL_FIELD = audio::createSample("magic_spell_dispell_field.wav");
1350 SND_SPELL_DISPELL_ILLUSION = audio::createSample("magic_spell_dispell_illusion.wav");
1351 SND_SPELL_DOUSE = audio::createSample("magic_spell_douse.wav");
1352 SND_SPELL_ELECTRIC = audio::createSample("sfx_electric.wav");
1353 SND_SPELL_ENCHANT_WEAPON = audio::createSample("magic_spell_enchant_weapon.wav");
1354 SND_SPELL_EXPLOSION = audio::createSample("magic_spell_explosion.wav");
1355 SND_SPELL_EYEBALL_IN = audio::createSample("magic_spell_eyeball_in.wav");
1356 SND_SPELL_EYEBALL_OUT = audio::createSample("magic_spell_eyeball_out.wav");
1357 SND_SPELL_FIRE_HIT = audio::createSample("magic_spell_firehit.wav");
1358 SND_SPELL_FIRE_LAUNCH = audio::createSample("magic_spell_firelaunch.wav");
1359 SND_SPELL_FIRE_PROTECTION = audio::createSample("magic_spell_fire_protection.wav");
1360 SND_SPELL_FIRE_PROTECTION_LOOP = audio::createSample("magic_spell_fire_protection_loop.wav");
1361 SND_SPELL_FIRE_PROTECTION_END = audio::createSample("magic_spell_fire_protection_end.wav");
1362 SND_SPELL_FIRE_WIND = audio::createSample("magic_spell_firewind.wav");
1363 SND_SPELL_FREEZETIME = audio::createSample("magic_spell_freezetime.wav");
1364 SND_SPELL_HARM = audio::createSample("magic_spell_harm.wav");
1365 SND_SPELL_HEALING = audio::createSample("magic_spell_healing.wav");
1366 SND_SPELL_ICE_FIELD = audio::createSample("magic_spell_ice_field.wav");
1367 SND_SPELL_ICE_FIELD_LOOP = audio::createSample("magic_spell_ice_fieldloop.wav");
1368 SND_SPELL_ICE_FIELD_END = audio::createSample("magic_spell_ice_field_end.wav");
1369 SND_SPELL_ICE_PROJECTILE_LAUNCH = audio::createSample("magic_spell_ice_projectile_launch.wav");
1370 SND_SPELL_INCINERATE = audio::createSample("magic_spell_incinerate.wav");
1371 SND_SPELL_INCINERATE_LOOP = audio::createSample("magic_spell_incinerate_loop.wav");
1372 SND_SPELL_INCINERATE_END = audio::createSample("magic_spell_incinerate_end.wav");
1373 SND_SPELL_IGNITE = audio::createSample("magic_spell_ignite.wav");
1374 SND_SPELL_INVISIBILITY_START = audio::createSample("magic_spell_invisibilityon.wav");
1375 SND_SPELL_INVISIBILITY_END = audio::createSample("magic_spell_invisibilityoff.wav");
1376 SND_SPELL_LEVITATE_START = audio::createSample("magic_spell_levitate_start.wav");
1377 SND_SPELL_LIGHTNING_START = audio::createSample("magic_spell_lightning_start.wav");
1378 SND_SPELL_LIGHTNING_LOOP = audio::createSample("magic_spell_lightning_loop.wav");
1379 SND_SPELL_LIGHTNING_END = audio::createSample("magic_spell_lightning_end.wav");
1380 SND_SPELL_MAGICAL_HIT = audio::createSample("magic_spell_magicalhit.wav");
1381
1382 SND_SPELL_FIRE_FIELD_START = audio::createSample("magic_spell_fire_field.wav");
1383 SND_SPELL_FIRE_FIELD_LOOP = audio::createSample("magic_spell_fire_field_loop.wav");
1384 SND_SPELL_FIRE_FIELD_END = audio::createSample("magic_spell_fire_field_end.wav");
1385
1386 SND_SPELL_MAGICAL_SHIELD = audio::createSample("magic_spell_magicalshield.wav");
1387 SND_SPELL_MASS_INCINERATE = audio::createSample("magic_spell_mass_incinerate.wav");
1388 SND_SPELL_MASS_PARALYSE = audio::createSample("magic_spell_mass_paralyse.wav");
1389 SND_SPELL_MM_CREATE = audio::createSample("magic_spell_missilecreate.wav");
1390 SND_SPELL_MM_HIT = audio::createSample("magic_spell_missilehit.wav");
1391 SND_SPELL_MM_LAUNCH = audio::createSample("magic_spell_missilelaunch.wav");
1392 SND_SPELL_MM_LOOP = audio::createSample("magic_spell_missileloop.wav");
1393 SND_SPELL_NEGATE_MAGIC = audio::createSample("magic_spell_negate_magic.wav");
1394 SND_SPELL_NO_EFFECT = audio::createSample("magic_spell_noeffect.wav");
1395 SND_SPELL_PARALYSE = audio::createSample("magic_spell_paralyse.wav");
1396 SND_SPELL_PARALYSE_END = audio::createSample("magic_spell_paralyse_end.wav");
1397 SND_SPELL_POISON_PROJECTILE_LAUNCH = audio::createSample("magic_spell_poison_projectile_launch.wav");
1398 SND_SPELL_RAISE_DEAD = audio::createSample("magic_spell_raise_dead.wav");
1399 SND_SPELL_REPEL_UNDEAD = audio::createSample("magic_spell_repel_undead.wav");
1400 SND_SPELL_REPEL_UNDEAD_LOOP = audio::createSample("magic_spell_repell_loop.wav");
1401 SND_SPELL_RUNE_OF_GUARDING = audio::createSample("magic_spell_rune_of_guarding.wav");
1402 SND_SPELL_RUNE_OF_GUARDING_END = audio::createSample("magic_spell_rune_of_guarding_explode.wav");
1403 SND_SPELL_SLOW_DOWN = audio::createSample("magic_spell_slow_down.wav");
1404 SND_SPELL_SPARK = audio::createSample("sfx_spark.wav");
1405 SND_SPELL_SPEED_START = audio::createSample("magic_spell_speedstart.wav");
1406 SND_SPELL_SPEED_LOOP = audio::createSample("magic_spell_speed.wav");
1407 SND_SPELL_SPEED_END = audio::createSample("magic_spell_speedend.wav");
1408 SND_SPELL_SUMMON_CREATURE = audio::createSample("magic_spell_summon_creature.wav");
1409 SND_SPELL_TELEKINESIS_START = audio::createSample("magic_spell_telekinesison.wav");
1410 SND_SPELL_TELEKINESIS_END = audio::createSample("magic_spell_telekinesisoff.wav");
1411 SND_SPELL_TELEPORT = audio::createSample("magic_spell_teleport.wav");
1412 SND_SPELL_TELEPORTED = audio::createSample("magic_spell_teleported.wav");
1413 SND_SPELL_VISION_START = audio::createSample("magic_spell_vision2.wav");
1414 SND_SPELL_VISION_LOOP = audio::createSample("magic_spell_vision.wav");
1415 }
1416
1417 // Reset each static sample to INVALID_ID
1418 // Those samples are freed from memory when by audio::clean() is deleted
ARX_SOUND_ReleaseStaticSamples()1419 static void ARX_SOUND_ReleaseStaticSamples() {
1420
1421 // Interface samples
1422 SND_BACKPACK = INVALID_ID;
1423 SND_BOOK_OPEN = INVALID_ID;
1424 SND_BOOK_CLOSE = INVALID_ID;
1425 SND_BOOK_PAGE_TURN = INVALID_ID;
1426 SND_GOLD = INVALID_ID;
1427 SND_INVSTD = INVALID_ID;
1428 SND_SCROLL_OPEN = INVALID_ID;
1429 SND_SCROLL_CLOSE = INVALID_ID;
1430 SND_TORCH_START = INVALID_ID;
1431 SND_TORCH_LOOP = INVALID_ID;
1432 SND_TORCH_END = INVALID_ID;
1433
1434 // Other SFX samples
1435 SND_FIREPLACE = INVALID_ID;
1436 SND_PLOUF = INVALID_ID;
1437 SND_QUAKE = INVALID_ID;
1438
1439 // Menu samples
1440 SND_MENU_CLICK = INVALID_ID;
1441 SND_MENU_RELEASE = INVALID_ID;
1442
1443 // Player samples
1444 SND_PLAYER_DEATH_BY_FIRE = INVALID_ID;
1445 SND_PLAYER_FILLLIFEMANA = INVALID_ID;
1446 SND_PLAYER_HEART_BEAT = INVALID_ID;
1447 SND_PLAYER_LEVEL_UP = INVALID_ID;
1448 SND_PLAYER_POISONED = INVALID_ID;
1449
1450 // Magic drawing samples
1451 SND_MAGIC_AMBIENT = INVALID_ID;
1452 SND_MAGIC_DRAW = INVALID_ID;
1453 SND_MAGIC_FIZZLE = INVALID_ID;
1454
1455 // Magic symbols samples
1456 SND_SYMB_AAM = INVALID_ID;
1457 SND_SYMB_CETRIUS = INVALID_ID;
1458 SND_SYMB_COSUM = INVALID_ID;
1459 SND_SYMB_COMUNICATUM = INVALID_ID;
1460 SND_SYMB_FOLGORA = INVALID_ID;
1461 SND_SYMB_FRIDD = INVALID_ID;
1462 SND_SYMB_KAOM = INVALID_ID;
1463 SND_SYMB_MEGA = INVALID_ID;
1464 SND_SYMB_MORTE = INVALID_ID;
1465 SND_SYMB_MOVIS = INVALID_ID;
1466 SND_SYMB_NHI = INVALID_ID;
1467 SND_SYMB_RHAA = INVALID_ID;
1468 SND_SYMB_SPACIUM = INVALID_ID;
1469 SND_SYMB_STREGUM = INVALID_ID;
1470 SND_SYMB_TAAR = INVALID_ID;
1471 SND_SYMB_TEMPUS = INVALID_ID;
1472 SND_SYMB_TERA = INVALID_ID;
1473 SND_SYMB_VISTA = INVALID_ID;
1474 SND_SYMB_VITAE = INVALID_ID;
1475 SND_SYMB_YOK = INVALID_ID;
1476
1477 // Spells samples
1478 SND_SPELL_ACTIVATE_PORTAL = INVALID_ID;
1479 SND_SPELL_ARMOR_START = INVALID_ID;
1480 SND_SPELL_ARMOR_END = INVALID_ID;
1481 SND_SPELL_ARMOR_LOOP = INVALID_ID;
1482 SND_SPELL_LOWER_ARMOR = INVALID_ID;
1483 SND_SPELL_BLESS = INVALID_ID;
1484 SND_SPELL_COLD_PROTECTION_START = INVALID_ID;
1485 SND_SPELL_COLD_PROTECTION_LOOP = INVALID_ID;
1486 SND_SPELL_COLD_PROTECTION_END = INVALID_ID;
1487 SND_SPELL_CONFUSE = INVALID_ID;
1488 SND_SPELL_CONTROL_TARGET = INVALID_ID;
1489 SND_SPELL_CREATE_FIELD = INVALID_ID;
1490 SND_SPELL_CREATE_FOOD = INVALID_ID;
1491 SND_SPELL_CURE_POISON = INVALID_ID;
1492 SND_SPELL_CURSE = INVALID_ID;
1493 SND_SPELL_DETECT_TRAP = INVALID_ID;
1494 SND_SPELL_DETECT_TRAP_LOOP = INVALID_ID;
1495 SND_SPELL_DISARM_TRAP = INVALID_ID;
1496 SND_SPELL_DISPELL_FIELD = INVALID_ID;
1497 SND_SPELL_DISPELL_ILLUSION = INVALID_ID;
1498 SND_SPELL_DOUSE = INVALID_ID;
1499 SND_SPELL_ELECTRIC = INVALID_ID;
1500 SND_SPELL_ENCHANT_WEAPON = INVALID_ID;
1501 SND_SPELL_EXPLOSION = INVALID_ID;
1502 SND_SPELL_EYEBALL_IN = INVALID_ID;
1503 SND_SPELL_EYEBALL_OUT = INVALID_ID;
1504 SND_SPELL_FIRE_FIELD = INVALID_ID;
1505 SND_SPELL_FIRE_HIT = INVALID_ID;
1506 SND_SPELL_FIRE_LAUNCH = INVALID_ID;
1507 SND_SPELL_FIRE_PROTECTION = INVALID_ID;
1508 SND_SPELL_FIRE_PROTECTION_LOOP = INVALID_ID;
1509 SND_SPELL_FIRE_PROTECTION_END = INVALID_ID;
1510 SND_SPELL_FIRE_WIND = INVALID_ID;
1511 SND_SPELL_FREEZETIME = INVALID_ID;
1512 SND_SPELL_HARM = INVALID_ID;
1513 SND_SPELL_HEALING = INVALID_ID;
1514 SND_SPELL_ICE_FIELD = INVALID_ID;
1515 SND_SPELL_ICE_FIELD_LOOP = INVALID_ID;
1516 SND_SPELL_ICE_FIELD_END = INVALID_ID;
1517 SND_SPELL_ICE_PROJECTILE_LAUNCH = INVALID_ID;
1518 SND_SPELL_INCINERATE = INVALID_ID;
1519 SND_SPELL_INCINERATE_LOOP = INVALID_ID;
1520 SND_SPELL_INCINERATE_END = INVALID_ID;
1521 SND_SPELL_IGNITE = INVALID_ID;
1522 SND_SPELL_INVISIBILITY_START = INVALID_ID;
1523 SND_SPELL_INVISIBILITY_END = INVALID_ID;
1524 SND_SPELL_LEVITATE_START = INVALID_ID;
1525 SND_SPELL_LIGHTNING_START = INVALID_ID;
1526 SND_SPELL_LIGHTNING_LOOP = INVALID_ID;
1527 SND_SPELL_LIGHTNING_END = INVALID_ID;
1528 SND_SPELL_MAGICAL_HIT = INVALID_ID;
1529
1530 //SND_SPELL_MASS_LIGHTNING_END = INVALID_ID;
1531 SND_SPELL_FIRE_FIELD_START = INVALID_ID;
1532 SND_SPELL_FIRE_FIELD_LOOP = INVALID_ID;
1533 SND_SPELL_FIRE_FIELD_END = INVALID_ID;
1534
1535 SND_SPELL_MAGICAL_SHIELD = INVALID_ID;
1536 SND_SPELL_MASS_INCINERATE = INVALID_ID;
1537 SND_SPELL_MASS_PARALYSE = INVALID_ID;
1538 SND_SPELL_MM_CREATE = INVALID_ID;
1539 SND_SPELL_MM_HIT = INVALID_ID;
1540 SND_SPELL_MM_LAUNCH = INVALID_ID;
1541 SND_SPELL_MM_LOOP = INVALID_ID;
1542 SND_SPELL_NEGATE_MAGIC = INVALID_ID;
1543 SND_SPELL_PARALYSE = INVALID_ID;
1544 SND_SPELL_PARALYSE_END = INVALID_ID;
1545 SND_SPELL_POISON_PROJECTILE_LAUNCH = INVALID_ID;
1546 SND_SPELL_RAISE_DEAD = INVALID_ID;
1547 SND_SPELL_REPEL_UNDEAD = INVALID_ID;
1548 SND_SPELL_REPEL_UNDEAD_LOOP = INVALID_ID;
1549 SND_SPELL_RUNE_OF_GUARDING = INVALID_ID;
1550 SND_SPELL_RUNE_OF_GUARDING_END = INVALID_ID;
1551 SND_SPELL_SLOW_DOWN = INVALID_ID;
1552 SND_SPELL_SPARK = INVALID_ID;
1553 SND_SPELL_SPEED_START = INVALID_ID;
1554 SND_SPELL_SPEED_LOOP = INVALID_ID;
1555 SND_SPELL_SPEED_END = INVALID_ID;
1556 SND_SPELL_SUMMON_CREATURE = INVALID_ID;
1557 SND_SPELL_TELEKINESIS_START = INVALID_ID;
1558 SND_SPELL_TELEKINESIS_END = INVALID_ID;
1559 SND_SPELL_TELEPORT = INVALID_ID;
1560 SND_SPELL_TELEPORTED = INVALID_ID;
1561 SND_SPELL_VISION_START = INVALID_ID;
1562 SND_SPELL_VISION_LOOP = INVALID_ID;
1563 }
1564
ARX_MATERIAL_GetNameById(long id,char * name)1565 bool ARX_MATERIAL_GetNameById(long id, char * name)
1566 {
1567 switch (id)
1568 {
1569 case MATERIAL_WEAPON:
1570 strcpy(name, "weapon");
1571 return true;
1572 break;
1573 case MATERIAL_FLESH:
1574 strcpy(name, "flesh");
1575 return true;
1576 break;
1577 case MATERIAL_METAL:
1578 strcpy(name, "metal");
1579 return true;
1580 break;
1581 case MATERIAL_GLASS:
1582 strcpy(name, "glass");
1583 return true;
1584 break;
1585 case MATERIAL_CLOTH:
1586 strcpy(name, "cloth");
1587 return true;
1588 break;
1589 case MATERIAL_WOOD:
1590 strcpy(name, "wood");
1591 return true;
1592 break;
1593 case MATERIAL_EARTH:
1594 strcpy(name, "earth");
1595 return true;
1596 break;
1597 case MATERIAL_WATER:
1598 strcpy(name, "water");
1599 return true;
1600 break;
1601 case MATERIAL_ICE:
1602 strcpy(name, "ice");
1603 return true;
1604 break;
1605 case MATERIAL_GRAVEL:
1606 strcpy(name, "gravel");
1607 return true;
1608 break;
1609 case MATERIAL_STONE:
1610 strcpy(name, "stone");
1611 return true;
1612 break;
1613 case MATERIAL_FOOT_LARGE:
1614 strcpy(name, "foot_large");
1615 return true;
1616 break;
1617 case MATERIAL_FOOT_BARE:
1618 strcpy(name, "foot_bare");
1619 return true;
1620 break;
1621 case MATERIAL_FOOT_SHOE:
1622 strcpy(name, "foot_shoe");
1623 return true;
1624 break;
1625 case MATERIAL_FOOT_METAL:
1626 strcpy(name, "foot_metal");
1627 return true;
1628 break;
1629 case MATERIAL_FOOT_STEALTH:
1630 strcpy(name, "foot_stealth");
1631 return true;
1632 break;
1633 }
1634
1635 strcpy(name, "none");
1636 return false;
1637 }
ARX_SOUND_LoadCollision(const long & mat1,const long & mat2,const char * name)1638 static void ARX_SOUND_LoadCollision(const long & mat1, const long & mat2, const char * name)
1639 {
1640 char path[256];
1641
1642 for (size_t i = 0; i < MAX_VARIANTS; i++)
1643 {
1644 sprintf(path, "%s_%lu.wav", name, (unsigned long)(i + 1));
1645 Inter_Materials[mat1][mat2][i] = audio::createSample(path);
1646
1647 if (mat1 != mat2)
1648 Inter_Materials[mat2][mat1][i] = Inter_Materials[mat1][mat2][i];
1649 }
1650 }
1651
ARX_SOUND_CreateCollisionMaps()1652 static void ARX_SOUND_CreateCollisionMaps() {
1653
1654 collisionMaps.clear();
1655
1656 for(size_t i = 0; i < ARX_SOUND_COLLISION_MAP_COUNT; i++) {
1657
1658 res::path file = ARX_SOUND_PATH_INI / ARX_SOUND_COLLISION_MAP_NAMES[i];
1659 file.set_ext(ARX_SOUND_FILE_EXTENSION_INI);
1660
1661 size_t fileSize;
1662 char * data = resources->readAlloc(file, fileSize);
1663 if(!data) {
1664 LogWarning << "Could not find collision map " << file;
1665 return;
1666 }
1667
1668 istringstream iss(string(data, fileSize));
1669 free(data);
1670
1671 IniReader reader;
1672 if(!reader.read(iss)) {
1673 LogWarning << "Errors while parsing collision map " << file;
1674 }
1675
1676 for(IniReader::iterator si = reader.begin(); si != reader.end(); ++si) {
1677 const IniSection & section = si->second;
1678 CollisionMap & map = collisionMaps[si->first];
1679
1680 for(IniSection::iterator ki = section.begin(); ki != section.end(); ++ki) {
1681 const IniKey & key = *ki;
1682 SoundMaterial & mat = map[key.getName()];
1683
1684 for(size_t mi = 0; mi < MAX_VARIANTS; mi++) {
1685
1686 ostringstream oss;
1687 oss << boost::to_lower_copy(key.getValue());
1688 if(mi) {
1689 oss << mi;
1690 }
1691 oss << ARX_SOUND_FILE_EXTENSION_WAV;
1692 SampleId sample = audio::createSample(oss.str());
1693
1694 if(sample == INVALID_ID) {
1695 ostringstream oss2;
1696 oss2 << boost::to_lower_copy(key.getValue()) << '_' << mi << ARX_SOUND_FILE_EXTENSION_WAV;
1697 sample = audio::createSample(oss2.str());
1698 }
1699
1700 if(sample != INVALID_ID) {
1701 mat.variants.push_back(sample);
1702 }
1703 }
1704
1705 if(mat.variants.empty()) {
1706 map.erase(key.getName());
1707 }
1708
1709 }
1710
1711 if(map.empty()) {
1712 collisionMaps.erase(si->first);
1713 }
1714
1715 }
1716
1717 }
1718
1719 }
1720
ARX_SOUND_CreateMaterials()1721 static void ARX_SOUND_CreateMaterials()
1722 {
1723 memset(Inter_Materials, -1, sizeof(long) * MAX_MATERIALS * MAX_MATERIALS * MAX_VARIANTS);
1724
1725 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_WEAPON, "weapon_on_weapon");
1726 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_FLESH, "weapon_on_flesh");
1727 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_METAL, "weapon_on_metal");
1728 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_GLASS, "weapon_on_glass");
1729 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_CLOTH, "weapon_on_cloth");
1730 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_WOOD, "weapon_on_wood");
1731 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_EARTH, "weapon_on_earth");
1732 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_WATER, "weapon_on_water");
1733 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_ICE, "weapon_on_ice");
1734 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_GRAVEL, "weapon_on_gravel");
1735 ARX_SOUND_LoadCollision(MATERIAL_WEAPON, MATERIAL_STONE, "weapon_on_stone");
1736
1737 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_FLESH, "flesh_on_flesh");
1738 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_METAL, "flesh_on_metal");
1739 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_GLASS, "flesh_on_glass");
1740 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_CLOTH, "flesh_on_cloth");
1741 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_WOOD, "flesh_on_wood");
1742 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_EARTH, "flesh_on_earth");
1743 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_WATER, "flesh_on_water");
1744 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_ICE, "flesh_on_ice");
1745 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_GRAVEL, "flesh_on_gravel");
1746 ARX_SOUND_LoadCollision(MATERIAL_FLESH, MATERIAL_STONE, "flesh_on_stone");
1747
1748 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_METAL, "metal_on_metal");
1749 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_GLASS, "metal_on_glass");
1750 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_CLOTH, "metal_on_cloth");
1751 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_WOOD, "metal_on_wood");
1752 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_EARTH, "metal_on_earth");
1753 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_WATER, "metal_on_water");
1754 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_ICE, "metal_on_ice");
1755 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_GRAVEL, "metal_on_gravel");
1756 ARX_SOUND_LoadCollision(MATERIAL_METAL, MATERIAL_STONE, "metal_on_stone");
1757
1758 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_GLASS, "glass_on_glass");
1759 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_CLOTH, "glass_on_cloth");
1760 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_WOOD, "glass_on_wood");
1761 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_EARTH, "glass_on_earth");
1762 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_WATER, "glass_on_water");
1763 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_ICE, "glass_on_ice");
1764 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_GRAVEL, "glass_on_gravel");
1765 ARX_SOUND_LoadCollision(MATERIAL_GLASS, MATERIAL_STONE, "glass_on_stone");
1766
1767 ARX_SOUND_LoadCollision(MATERIAL_CLOTH, MATERIAL_CLOTH, "cloth_on_cloth");
1768 ARX_SOUND_LoadCollision(MATERIAL_CLOTH, MATERIAL_WOOD, "cloth_on_wood");
1769 ARX_SOUND_LoadCollision(MATERIAL_CLOTH, MATERIAL_EARTH, "cloth_on_earth");
1770 ARX_SOUND_LoadCollision(MATERIAL_CLOTH, MATERIAL_WATER, "cloth_on_water");
1771 ARX_SOUND_LoadCollision(MATERIAL_CLOTH, MATERIAL_ICE, "cloth_on_ice");
1772 ARX_SOUND_LoadCollision(MATERIAL_CLOTH, MATERIAL_GRAVEL, "cloth_on_gravel");
1773 ARX_SOUND_LoadCollision(MATERIAL_CLOTH, MATERIAL_STONE, "cloth_on_stone");
1774
1775 ARX_SOUND_LoadCollision(MATERIAL_WOOD, MATERIAL_WOOD, "wood_on_wood");
1776 ARX_SOUND_LoadCollision(MATERIAL_WOOD, MATERIAL_EARTH, "wood_on_earth");
1777 ARX_SOUND_LoadCollision(MATERIAL_WOOD, MATERIAL_WATER, "wood_on_water");
1778 ARX_SOUND_LoadCollision(MATERIAL_WOOD, MATERIAL_ICE, "wood_on_ice");
1779 ARX_SOUND_LoadCollision(MATERIAL_WOOD, MATERIAL_GRAVEL, "wood_on_gravel");
1780 ARX_SOUND_LoadCollision(MATERIAL_WOOD, MATERIAL_STONE, "wood_on_stone");
1781
1782 ARX_SOUND_LoadCollision(MATERIAL_EARTH, MATERIAL_EARTH, "earth_on_earth");
1783 ARX_SOUND_LoadCollision(MATERIAL_EARTH, MATERIAL_WATER, "earth_on_water");
1784 ARX_SOUND_LoadCollision(MATERIAL_EARTH, MATERIAL_ICE, "earth_on_ice");
1785 ARX_SOUND_LoadCollision(MATERIAL_EARTH, MATERIAL_GRAVEL, "earth_on_gravel");
1786 ARX_SOUND_LoadCollision(MATERIAL_EARTH, MATERIAL_STONE, "earth_on_stone");
1787
1788 ARX_SOUND_LoadCollision(MATERIAL_WATER, MATERIAL_WATER, "water_on_water");
1789 ARX_SOUND_LoadCollision(MATERIAL_WATER, MATERIAL_ICE, "water_on_ice");
1790 ARX_SOUND_LoadCollision(MATERIAL_WATER, MATERIAL_GRAVEL, "water_on_gravel");
1791 ARX_SOUND_LoadCollision(MATERIAL_WATER, MATERIAL_STONE, "water_on_stone");
1792
1793 ARX_SOUND_LoadCollision(MATERIAL_ICE, MATERIAL_ICE, "ice_on_ice");
1794 ARX_SOUND_LoadCollision(MATERIAL_ICE, MATERIAL_GRAVEL, "ice_on_gravel");
1795 ARX_SOUND_LoadCollision(MATERIAL_ICE, MATERIAL_STONE, "ice_on_stone");
1796
1797 ARX_SOUND_LoadCollision(MATERIAL_GRAVEL, MATERIAL_GRAVEL, "gravel_on_gravel");
1798 ARX_SOUND_LoadCollision(MATERIAL_GRAVEL, MATERIAL_STONE, "gravel_on_stone");
1799
1800 ARX_SOUND_LoadCollision(MATERIAL_STONE, MATERIAL_STONE, "stone_on_stone");
1801 }
1802
1803
ARX_SOUND_CreatePresenceMap()1804 static void ARX_SOUND_CreatePresenceMap() {
1805
1806 presence.clear();
1807
1808 res::path file = (ARX_SOUND_PATH_INI / ARX_SOUND_PRESENCE_NAME).set_ext(ARX_SOUND_FILE_EXTENSION_INI);
1809
1810 size_t fileSize;
1811 char * data = resources->readAlloc(file, fileSize);
1812 if(!data) {
1813 LogWarning << "Could not find presence map " << file;
1814 return;
1815 }
1816
1817 istringstream iss(string(data, fileSize));
1818 free(data);
1819
1820 IniReader reader;
1821 if(!reader.read(iss)) {
1822 LogWarning << "Errors while parsing presence map " << file;
1823 }
1824
1825 const IniSection * section = reader.getSection(Section::presence);
1826 if(!section) {
1827 LogWarning << "No [" << Section::presence << "] section in presence map " << file;
1828 return;
1829 }
1830
1831 for(IniSection::iterator i = section->begin(); i != section->end(); ++i) {
1832 float factor = i->getValue(100.f) / 100.f;
1833 presence[res::path::load(i->getName()).set_ext(ARX_SOUND_FILE_EXTENSION_WAV)] = factor;
1834 }
1835
1836 }
1837
GetSamplePresenceFactor(const res::path & name)1838 static float GetSamplePresenceFactor(const res::path & name) {
1839
1840 arx_assert_msg(name.string().find_first_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ") == string::npos,
1841 "bad sample name: \"%s\"", name.string().c_str());
1842
1843 PresenceFactors::const_iterator it = presence.find(name);
1844 if(it != presence.end()) {
1845 return it->second;
1846 }
1847
1848 return 1.f;
1849 }
1850
1851 class SoundUpdateThread : public StoppableThread {
1852
run()1853 void run() {
1854
1855 while(!isStopRequested()) {
1856
1857 sleep(ARX_SOUND_UPDATE_INTERVAL);
1858
1859 audio::update();
1860 }
1861
1862 }
1863
1864 };
1865
1866 static SoundUpdateThread * updateThread = NULL;
1867
ARX_SOUND_LaunchUpdateThread()1868 static void ARX_SOUND_LaunchUpdateThread() {
1869
1870 arx_assert(!updateThread);
1871
1872 updateThread = new SoundUpdateThread();
1873 updateThread->setThreadName("Sound Update");
1874 updateThread->start();
1875 }
1876
ARX_SOUND_KillUpdateThread()1877 static void ARX_SOUND_KillUpdateThread() {
1878
1879 if(!updateThread) {
1880 return;
1881 }
1882
1883 updateThread->stop();
1884 delete updateThread, updateThread = NULL;
1885 }
1886