Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
SoundScriptManager.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5
6 For more information, see http://www.rigsofrods.org/
7
8 Rigs of Rods is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 3, as
10 published by the Free Software Foundation.
11
12 Rigs of Rods 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 Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#ifdef USE_OPENAL
22
23#include "SoundScriptManager.h"
24
25#include "Actor.h"
26#include "CameraManager.h"
27#include "Sound.h"
28#include "SoundManager.h"
29#include "Utils.h"
30
31#include <OgreResourceGroupManager.h>
32
33using namespace Ogre;
34using namespace RoR;
35
38
39const SoundPtr SoundScriptInstance::SOUNDPTR_NULL; // Dummy value to be returned as const reference.
40
42 disabled(true)
43 , loading_base(false)
44 , instance_counter(0)
45 , max_distance(500.0f)
46 , rolloff_factor(1.0f)
47 , reference_distance(7.5f)
48 , sound_manager(nullptr)
49{
50 for (int i = 0; i < SS_MAX_TRIG; i++)
51 {
52 free_trigs[i] = 0;
53 }
54
55 for (int i = 0; i < SS_MAX_MOD; i++)
56 {
57 free_pitches[i] = 0;
58 free_gains[i] = 0;
59 }
60
61 // TODO: there is a memory corruption going on here, need to fix
62 for (int i = 0; i < SS_MAX_TRIG * MAX_INSTANCES_PER_GROUP; i++)
63 {
64 trigs[i] = 0;
65 }
66
67 for (int i = 0; i < SS_MAX_MOD * MAX_INSTANCES_PER_GROUP; i++)
68 {
69 pitches[i] = 0;
70 gains[i] = 0;
71 }
72
73 // reset all states
74 state_map.clear();
75
77
78 if (!sound_manager)
79 {
80 LOG("SoundScriptManager: Failed to create the Sound Manager");
81 return;
82 }
83
85
86 if (disabled)
87 {
88 LOG("SoundScriptManager: Sound Manager is disabled");
89 return;
90 }
91
92 LOG("SoundScriptManager: Sound Manager started with " + TOSTRING(sound_manager->getNumHardwareSources())+" sources");
93 script_patterns.push_back("*.soundscript");
94 ResourceGroupManager::getSingleton()._registerScriptLoader(this);
95}
96
102
103void SoundScriptManager::trigOnce(const ActorPtr& actor, int trig, int linkType, int linkItemID)
104{
105 if (disabled)
106 return;
107
108 if (actor)
109 {
110 trigOnce(actor->ar_instance_id, trig, linkType, linkItemID);
111 }
112}
113
114void SoundScriptManager::trigOnce(int actor_id, int trig, int linkType, int linkItemID)
115{
116 if (disabled)
117 return;
118
119 for (int i = 0; i < free_trigs[trig]; i++)
120 {
121 // cycle through all instance groups
122 const SoundScriptInstancePtr& inst = trigs[trig + i * SS_MAX_TRIG];
123
124 if (inst && inst->actor_id == actor_id && inst->sound_link_type == linkType && inst->sound_link_item_id == linkItemID)
125 {
126 inst->runOnce();
127 }
128 }
129}
130
131void SoundScriptManager::trigStart(const ActorPtr& actor, int trig, int linkType, int linkItemID)
132{
133 if (disabled)
134 return;
135
136 if (actor)
137 {
138 trigStart(actor->ar_instance_id, trig, linkType, linkItemID);
139 }
140}
141
142void SoundScriptManager::trigStart(int actor_id, int trig, int linkType, int linkItemID)
143{
144 if (disabled)
145 return;
146 if (getTrigState(actor_id, trig, linkType, linkItemID))
147 return;
148
149 state_map[linkType][linkItemID][actor_id][trig] = true;
150
151 for (int i = 0; i < free_trigs[trig]; i++)
152 {
153 const SoundScriptInstancePtr& inst = trigs[trig + i * SS_MAX_TRIG];
154
155 if (inst && inst->actor_id == actor_id && inst->sound_link_type == linkType && inst->sound_link_item_id == linkItemID)
156 {
157 inst->start();
158 }
159 }
160}
161
162void SoundScriptManager::trigStop(const ActorPtr& actor, int trig, int linkType, int linkItemID)
163{
164 if (disabled)
165 return;
166
167 if (actor)
168 {
169 trigStop(actor->ar_instance_id, trig, linkType, linkItemID);
170 }
171}
172
173void SoundScriptManager::trigStop(int actor_id, int trig, int linkType, int linkItemID)
174{
175 if (disabled)
176 return;
177 if (!getTrigState(actor_id, trig, linkType, linkItemID))
178 return;
179
180 state_map[linkType][linkItemID][actor_id][trig] = false;
181 for (int i = 0; i < free_trigs[trig]; i++)
182 {
183 const SoundScriptInstancePtr& inst = trigs[trig + i * SS_MAX_TRIG];
184
185 if (inst && inst->actor_id == actor_id && inst->sound_link_type == linkType && inst->sound_link_item_id == linkItemID)
186 {
187 inst->stop();
188 }
189 }
190}
191
192void SoundScriptManager::trigKill(const ActorPtr& actor, int trig, int linkType, int linkItemID)
193{
194 if (disabled)
195 return;
196
197 if (actor)
198 {
199 trigKill(actor->ar_instance_id, trig, linkType, linkItemID);
200 }
201}
202
203void SoundScriptManager::trigKill(int actor_id, int trig, int linkType, int linkItemID)
204{
205 if (disabled)
206 return;
207 if (!getTrigState(actor_id, trig, linkType, linkItemID))
208 return;
209
210 state_map[linkType][linkItemID][actor_id][trig] = false;
211 for (int i = 0; i < free_trigs[trig]; i++)
212 {
213 const SoundScriptInstancePtr& inst = trigs[trig + i * SS_MAX_TRIG];
214
215 if (inst && inst->actor_id == actor_id && inst->sound_link_type == linkType && inst->sound_link_item_id == linkItemID)
216 {
217 inst->kill();
218 }
219 }
220}
221
222void SoundScriptManager::trigToggle(const ActorPtr& actor, int trig, int linkType, int linkItemID)
223{
224 if (disabled)
225 return;
226
227 if (actor)
228 {
229 trigToggle(actor->ar_instance_id, trig, linkType, linkItemID);
230 }
231}
232
233void SoundScriptManager::trigToggle(int actor_id, int trig, int linkType, int linkItemID)
234{
235 if (disabled)
236 return;
237
238 if (getTrigState(actor_id, trig, linkType, linkItemID))
239 trigStop(actor_id, trig, linkType, linkItemID);
240 else
241 trigStart(actor_id, trig, linkType, linkItemID);
242}
243
244bool SoundScriptManager::getTrigState(const ActorPtr& actor, int trig, int linkType, int linkItemID)
245{
246 if (disabled)
247 return false;
248
249 if (actor)
250 return getTrigState(actor->ar_instance_id, trig, linkType, linkItemID);
251 else
252 return false;
253}
254
255bool SoundScriptManager::getTrigState(int actor_id, int trig, int linkType, int linkItemID)
256{
257 if (disabled)
258 return false;
259
260 return state_map[linkType][linkItemID][actor_id][trig];
261}
262
263void SoundScriptManager::modulate(const ActorPtr& actor, int mod, float value, int linkType, int linkItemID)
264{
265 if (disabled)
266 return;
267
268 if (actor)
269 {
270 modulate(actor->ar_instance_id, mod, value, linkType, linkItemID);
271 }
272}
273
274void SoundScriptManager::modulate(int actor_id, int mod, float value, int linkType, int linkItemID)
275{
276 if (disabled)
277 return;
278
279 if (mod >= SS_MAX_MOD)
280 return;
281
282 for (int i = 0; i < free_gains[mod]; i++)
283 {
284 const SoundScriptInstancePtr& inst = gains[mod + i * SS_MAX_MOD];
285 if (inst && inst->actor_id == actor_id && inst->sound_link_type == linkType && inst->sound_link_item_id == linkItemID)
286 {
287 // this one requires modulation
288 float gain = value * value * inst->templ->gain_square + value * inst->templ->gain_multiplier + inst->templ->gain_offset;
289 gain = std::max(0.0f, gain);
290 gain = std::min(gain, 1.0f);
291 inst->setGain(gain);
292 }
293 }
294
295 for (int i = 0; i < free_pitches[mod]; i++)
296 {
297 const SoundScriptInstancePtr& inst = pitches[mod + i * SS_MAX_MOD];
298 if (inst && inst->actor_id == actor_id && inst->sound_link_type == linkType && inst->sound_link_item_id == linkItemID)
299 {
300 // this one requires modulation
301 float pitch = value * value * inst->templ->pitch_square + value * inst->templ->pitch_multiplier + inst->templ->pitch_offset;
302 pitch = std::max(0.0f, pitch);
303 inst->setPitch(pitch);
304 }
305 }
306}
307
309{
310 if (App::sim_state->getEnum<SimState>() == SimState::RUNNING ||
311 App::sim_state->getEnum<SimState>() == SimState::EDITOR_MODE)
312 {
313 Ogre::SceneNode* camera_node = App::GetCameraManager()->GetCameraNode();
314 static Vector3 last_camera_position;
315 Ogre::Vector3 camera_position = camera_node->getPosition();
316 Vector3 camera_velocity = (camera_position - last_camera_position) / dt;
317 last_camera_position = camera_position;
318 Ogre::Vector3 camera_up = camera_node->getOrientation() * Ogre::Vector3::UNIT_Y;
319 // Direction points down -Z by default (adapted from Ogre::Camera)
320 Ogre::Vector3 camera_direction = camera_node->getOrientation() * -Ogre::Vector3::UNIT_Z;
321
322 this->SetListener(camera_position, camera_direction, camera_up, camera_velocity);
323
325 }
326}
327
328void SoundScriptManager::SetListener(Vector3 position, Vector3 direction, Vector3 up, Vector3 velocity)
329{
330 if (disabled)
331 return;
332 sound_manager->SetListener(position, direction, up, velocity);
333}
334
335const StringVector& SoundScriptManager::getScriptPatterns(void) const
336{
337 return script_patterns;
338}
339
341{
342 // load late
343 return 1000.0f;
344}
345
346SoundScriptTemplatePtr SoundScriptManager::createTemplate(String name, String groupname, String filename)
347{
348 // first, search if there is a template name collision
349 if (templates.find(name) != templates.end())
350 {
351 LOG("SoundScriptManager::createTemplate(): SoundScript with name [" + name + "] already exists, skipping...");
352 return nullptr;
353 }
354
355 SoundScriptTemplatePtr ssi = new SoundScriptTemplate(name, groupname, filename, loading_base);
356 templates[name] = ssi;
357 return ssi;
358}
359
360SoundScriptInstancePtr SoundScriptManager::createInstance(Ogre::String templatename, int actor_id, int soundLinkType, int soundLinkItemId)
361{
362 //first, search template
363 SoundScriptTemplatePtr templ = NULL;
364
365 if (templates.find(templatename) == templates.end())
366 {
367 return NULL; // found no template with this name
368 }
369
370 templ = templates[templatename];
371
372 if (templ->trigger_source == SS_TRIG_NONE)
373 {
374 return NULL; // invalid template!
375 }
376
380 {
381 LOG("SoundScriptManager: Reached MAX_INSTANCES_PER_GROUP limit (" + TOSTRING(MAX_INSTANCES_PER_GROUP) + ")");
382 return NULL; // reached limit!
383 }
384
385 SoundScriptInstancePtr inst = new SoundScriptInstance(actor_id, templ, sound_manager, templ->file_name + "-" + TOSTRING(actor_id) + "-" + TOSTRING(instance_counter), soundLinkType, soundLinkItemId);
386 instances.push_back(inst);
388
389 // register to lookup tables
390 trigs[templ->trigger_source + free_trigs[templ->trigger_source] * SS_MAX_TRIG] = inst;
391 free_trigs[templ->trigger_source]++;
392
393 if (templ->gain_source != SS_MOD_NONE)
394 {
395 gains[templ->gain_source + free_gains[templ->gain_source] * SS_MAX_MOD] = inst;
396 free_gains[templ->gain_source]++;
397 }
398 if (templ->pitch_source != SS_MOD_NONE)
399 {
400 pitches[templ->pitch_source + free_pitches[templ->pitch_source] * SS_MAX_MOD] = inst;
401 free_pitches[templ->pitch_source]++;
402 }
403
404 // SoundTrigger: SS_TRIG_ALWAYSON
405 if (templ->trigger_source == SS_TRIG_ALWAYSON)
406 {
407 inst->start();
408 }
409
410 return inst;
411}
412
414{
415 // Find lookup table entries
416 int trigsPos = -1;
417 for (int i = 0; i < free_trigs[ssi->templ->trigger_source]; i++)
418 {
419 if (trigs[ssi->templ->trigger_source + i * SS_MAX_TRIG] == ssi)
420 {
421 trigsPos = i;
422 }
423 }
424
425 int gainsPos = -1;
426 for (int i = 0; i < free_gains[ssi->templ->gain_source]; i++)
427 {
428 if (gains[ssi->templ->gain_source + i * SS_MAX_MOD] == ssi)
429 {
430 gainsPos = i;
431 }
432 }
433
434 int pitchesPos = -1;
435 for (int i = 0; i < free_gains[ssi->templ->pitch_source]; i++)
436 {
437 if (pitches[ssi->templ->pitch_source + i * SS_MAX_MOD] == ssi)
438 {
439 pitchesPos = i;
440 }
441 }
442
443 // Erase lookup entries
444 if (trigsPos != -1)
445 {
446 for (int i = trigsPos + 1; i < free_trigs[ssi->templ->trigger_source]; i++)
447 {
448 trigs[ssi->templ->trigger_source + (i - 1) * SS_MAX_TRIG]
449 = trigs[ssi->templ->trigger_source + i * SS_MAX_TRIG];
450 }
452 }
453
454 if (gainsPos != -1)
455 {
456 for (int i = gainsPos + 1; i < free_gains[ssi->templ->gain_source]; i++)
457 {
458 gains[ssi->templ->gain_source + (i - 1) * SS_MAX_MOD]
459 = gains[ssi->templ->gain_source + i * SS_MAX_MOD];
460 }
462 }
463
464 if (pitchesPos != -1)
465 {
466 for (int i = pitchesPos + 1; i < free_pitches[ssi->templ->pitch_source]; i++)
467 {
468 pitches[ssi->templ->pitch_source + (i - 1) * SS_MAX_MOD]
469 = pitches[ssi->templ->pitch_source + i * SS_MAX_MOD];
470 }
472 }
473
474 // Finally remove the instance from list
475 EraseIf(instances, [ssi](const SoundScriptInstancePtr& instance) { return ssi == instance; });
476}
477
478void SoundScriptManager::parseScript(DataStreamPtr& stream, const String& groupName)
479{
480 SoundScriptTemplatePtr sst = nullptr;
481 String line = "";
482 std::vector<String> vecparams;
483
484 LOG("SoundScriptManager: Parsing script "+stream->getName());
485
486 while (!stream->eof())
487 {
488 line = SanitizeUtf8String(stream->getLine());
489 // ignore comments & blanks
490 if (!(line.length() == 0 || line.substr(0, 2) == "//"))
491 {
492 if (!sst)
493 {
494 // no current SoundScript
495 // so first valid data should be a SoundScript name
496 LOG("SoundScriptManager: creating template "+line);
497 sst = createTemplate(line, groupName, stream->getName());
498 if (!sst)
499 {
500 // there is a name collision for this Sound Script
501 LOG("SoundScriptManager: Error, this sound script is already defined: "+line);
502 skipToNextOpenBrace(stream);
503 skipToNextCloseBrace(stream);
504 continue;
505 }
506 // skip to and over next {
507 skipToNextOpenBrace(stream);
508 }
509 else
510 {
511 // already in a ss
512 if (line == "}")
513 {
514 // finished ss
515 sst = 0;
516 }
517 else
518 {
519 // attribute
520 // split params on space
521 Ogre::StringVector veclineparams = StringUtil::split(line, "\t ", 0);
522
523 if (!sst->setParameter(veclineparams))
524 {
525 LOG("Bad SoundScript attribute line: '" + line + "' in " + stream->getName());
526 }
527 }
528 }
529 }
530 }
531}
532
534{
535 String line = "";
536
537 while (!stream->eof() && line != "}")
538 {
539 line = stream->getLine();
540 }
541}
542
544{
545 String line = "";
546
547 while (!stream->eof() && line != "{")
548 {
549 line = stream->getLine();
550 }
551}
552
554{
555 if (state)
557 else
559}
560
561//=====================================================================
562
563SoundScriptTemplate::SoundScriptTemplate(String name, String groupname, String filename, bool baseTemplate) :
564 base_template(baseTemplate)
565 , file_name(filename)
566 , group_name(groupname)
567 , free_sound(0)
568 , gain_multiplier(1.0f)
569 , gain_offset(0.0f)
570 , gain_source(SS_MOD_NONE)
571 , gain_square(0.0f)
572 , has_start_sound(false)
573 , has_stop_sound(false)
574 , name(name)
575 , pitch_multiplier(1.0f)
576 , pitch_offset(0.0f)
577 , pitch_source(SS_MOD_NONE)
578 , pitch_square(0.0f)
579 , start_sound_pitch(0.0f)
580 , stop_sound_pitch(0.0f)
581 , trigger_source(SS_TRIG_NONE)
582 , unpitchable(false)
583{
584}
585
586bool SoundScriptTemplate::setParameter(Ogre::StringVector vec)
587{
588 if (vec.empty())
589 return false;
590
591 if (vec[0] == String("trigger_source"))
592 {
593 if (vec.size() < 2)
594 return false;
595 if (vec[1] == String("engine"))
596 {
598 return true;
599 };
600 if (vec[1] == String("aeroengine1"))
601 {
603 return true;
604 };
605 if (vec[1] == String("aeroengine2"))
606 {
608 return true;
609 };
610 if (vec[1] == String("aeroengine3"))
611 {
613 return true;
614 };
615 if (vec[1] == String("aeroengine4"))
616 {
618 return true;
619 };
620 if (vec[1] == String("aeroengine5"))
621 {
623 return true;
624 };
625 if (vec[1] == String("aeroengine6"))
626 {
628 return true;
629 };
630 if (vec[1] == String("aeroengine7"))
631 {
633 return true;
634 };
635 if (vec[1] == String("aeroengine8"))
636 {
638 return true;
639 };
640 if (vec[1] == String("horn"))
641 {
643 return true;
644 };
645 if (vec[1] == String("brake"))
646 {
648 return true;
649 };
650 if (vec[1] == String("pump"))
651 {
653 return true;
654 };
655 if (vec[1] == String("starter"))
656 {
658 return true;
659 };
660 if (vec[1] == String("turbo_BOV"))
661 {
663 return true;
664 };
665 if (vec[1] == String("turbo_waste_gate"))
666 {
668 return true;
669 };
670 if (vec[1] == String("turbo_back_fire"))
671 {
673 return true;
674 };
675 if (vec[1] == String("always_on"))
676 {
678 return true;
679 };
680 if (vec[1] == String("repair"))
681 {
683 return true;
684 };
685 if (vec[1] == String("air"))
686 {
688 return true;
689 };
690 if (vec[1] == String("gpws_ap_disconnect"))
691 {
693 return true;
694 };
695 if (vec[1] == String("gpws_10"))
696 {
698 return true;
699 };
700 if (vec[1] == String("gpws_20"))
701 {
703 return true;
704 };
705 if (vec[1] == String("gpws_30"))
706 {
708 return true;
709 };
710 if (vec[1] == String("gpws_40"))
711 {
713 return true;
714 };
715 if (vec[1] == String("gpws_50"))
716 {
718 return true;
719 };
720 if (vec[1] == String("gpws_100"))
721 {
723 return true;
724 };
725 if (vec[1] == String("gpws_pull_up"))
726 {
728 return true;
729 };
730 if (vec[1] == String("gpws_minimums"))
731 {
733 return true;
734 };
735 if (vec[1] == String("air_purge"))
736 {
738 return true;
739 };
740 if (vec[1] == String("shift"))
741 {
743 return true;
744 };
745 if (vec[1] == String("gear_slide"))
746 {
748 return true;
749 };
750 if (vec[1] == String("creak") && App::audio_enable_creak->getBool())
751 {
753 return true;
754 };
755 if (vec[1] == String("break"))
756 {
758 return true;
759 };
760 if (vec[1] == String("screetch"))
761 {
763 return true;
764 };
765 if (vec[1] == String("parking_brake"))
766 {
768 return true;
769 };
770 if (vec[1] == String("antilock"))
771 {
773 return true;
774 };
775 if (vec[1] == String("tractioncontrol"))
776 {
778 return true;
779 };
780 if (vec[1] == String("afterburner1"))
781 {
783 return true;
784 };
785 if (vec[1] == String("afterburner2"))
786 {
788 return true;
789 };
790 if (vec[1] == String("afterburner3"))
791 {
793 return true;
794 };
795 if (vec[1] == String("afterburner4"))
796 {
798 return true;
799 };
800 if (vec[1] == String("afterburner5"))
801 {
803 return true;
804 };
805 if (vec[1] == String("afterburner6"))
806 {
808 return true;
809 };
810 if (vec[1] == String("afterburner7"))
811 {
813 return true;
814 };
815 if (vec[1] == String("afterburner8"))
816 {
818 return true;
819 };
820 if (vec[1] == String("avionic_chat_01"))
821 {
823 return true;
824 };
825 if (vec[1] == String("avionic_chat_02"))
826 {
828 return true;
829 };
830 if (vec[1] == String("avionic_chat_03"))
831 {
833 return true;
834 };
835 if (vec[1] == String("avionic_chat_04"))
836 {
838 return true;
839 };
840 if (vec[1] == String("avionic_chat_05"))
841 {
843 return true;
844 };
845 if (vec[1] == String("avionic_chat_06"))
846 {
848 return true;
849 };
850 if (vec[1] == String("avionic_chat_07"))
851 {
853 return true;
854 };
855 if (vec[1] == String("avionic_chat_08"))
856 {
858 return true;
859 };
860 if (vec[1] == String("avionic_chat_09"))
861 {
863 return true;
864 };
865 if (vec[1] == String("avionic_chat_10"))
866 {
868 return true;
869 };
870 if (vec[1] == String("avionic_chat_11"))
871 {
873 return true;
874 };
875 if (vec[1] == String("avionic_chat_12"))
876 {
878 return true;
879 };
880 if (vec[1] == String("avionic_chat_13"))
881 {
883 return true;
884 };
885 if (vec[1] == String("aoa_horn"))
886 {
888 return true;
889 };
890 if (vec[1] == String("ignition"))
891 {
893 return true;
894 };
895 if (vec[1] == String("reverse_gear"))
896 {
898 return true;
899 };
900 if (vec[1] == String("turn_signal"))
901 {
903 return true;
904 };
905 if (vec[1] == String("turn_signal_tick"))
906 {
908 return true;
909 };
910 if (vec[1] == String("turn_signal_warn_tick"))
911 {
913 return true;
914 };
915 if (vec[1] == String("linked_command"))
916 {
918 return true;
919 };
920 if (vec[1] == String("main_menu"))
921 {
923 return true;
924 };
925
926 return false;
927 }
928
929 if (vec[0] == String("pitch_source"))
930 {
931 if (vec.size() < 2)
932 return false;
933 int mod = parseModulation(vec[1]);
934 if (mod >= 0)
935 {
936 pitch_source = mod;
937 return true;
938 }
939 return false;
940 }
941
942 if (vec[0] == String("pitch_factors"))
943 {
944 if (vec.size() < 3)
945 return false;
946 pitch_offset = StringConverter::parseReal(vec[1]);
947 pitch_multiplier = StringConverter::parseReal(vec[2]);
948 if (vec.size() == 4)
949 {
950 pitch_square = StringConverter::parseReal(vec[3]);
951 }
952 return true;
953 }
954
955 if (vec[0] == String("gain_source"))
956 {
957 if (vec.size() < 2)
958 return false;
959 int mod = parseModulation(vec[1]);
960 if (mod >= 0)
961 {
962 gain_source = mod;
963 return true;
964 }
965 return false;
966 }
967
968 if (vec[0] == String("gain_factors"))
969 {
970 if (vec.size() < 3)
971 return false;
972 gain_offset = StringConverter::parseReal(vec[1]);
973 gain_multiplier = StringConverter::parseReal(vec[2]);
974 if (vec.size() == 4)
975 {
976 gain_square = StringConverter::parseReal(vec[3]);
977 }
978 return true;
979 }
980
981 if (vec[0] == String("start_sound"))
982 {
983 if (vec.size() < 3)
984 return false;
985 start_sound_pitch = StringConverter::parseReal(vec[1]); // unparsable (e.g. "unpitched") will result in value 0.0
986 start_sound_name = vec[2];
987 has_start_sound = true;
988 return true;
989 }
990
991 if (vec[0] == String("stop_sound"))
992 {
993 if (vec.size() < 3)
994 return false;
995 stop_sound_pitch = StringConverter::parseReal(vec[1]); // unparsable (e.g. "unpitched") will result in value 0.0
996 stop_sound_name = vec[2];
997 has_stop_sound = true;
998 return true;
999 }
1000
1001 if (vec[0] == String("sound"))
1002 {
1003 if (vec.size() < 3)
1004 return false;
1006 {
1007 LOG("SoundScriptManager: Reached MAX_SOUNDS_PER_SCRIPT limit (" + TOSTRING(MAX_SOUNDS_PER_SCRIPT) + ")");
1008 return false;
1009 }
1010 sound_pitches[free_sound] = StringConverter::parseReal(vec[1]); // unparsable (e.g. "unpitched") will result in value 0.0
1011 if (sound_pitches[free_sound] == 0)
1012 {
1013 unpitchable = true;
1014 }
1016 {
1017 return false;
1018 }
1019 sound_names[free_sound] = vec[2];
1020 free_sound++;
1021 return true;
1022 }
1023
1024 return false;
1025}
1026
1028{
1029 if (str == String("none"))
1030 return SS_MOD_NONE;
1031 if (str == String("engine_rpm"))
1032 return SS_MOD_ENGINE;
1033 if (str == String("turbo_rpm"))
1034 return SS_MOD_TURBO;
1035 if (str == String("aeroengine1_rpm"))
1036 return SS_MOD_AEROENGINE1;
1037 if (str == String("aeroengine2_rpm"))
1038 return SS_MOD_AEROENGINE2;
1039 if (str == String("aeroengine3_rpm"))
1040 return SS_MOD_AEROENGINE3;
1041 if (str == String("aeroengine4_rpm"))
1042 return SS_MOD_AEROENGINE4;
1043 if (str == String("aeroengine5_rpm"))
1044 return SS_MOD_AEROENGINE5;
1045 if (str == String("aeroengine6_rpm"))
1046 return SS_MOD_AEROENGINE6;
1047 if (str == String("aeroengine7_rpm"))
1048 return SS_MOD_AEROENGINE7;
1049 if (str == String("aeroengine8_rpm"))
1050 return SS_MOD_AEROENGINE8;
1051 if (str == String("wheel_speed_kmph"))
1052 return SS_MOD_WHEELSPEED;
1053 if (str == String("injector_ratio"))
1054 return SS_MOD_INJECTOR;
1055 if (str == String("torque_nm"))
1056 return SS_MOD_TORQUE;
1057 if (str == String("gearbox_rpm"))
1058 return SS_MOD_GEARBOX;
1059 if (str == String("creak"))
1060 return SS_MOD_CREAK;
1061 if (str == String("break"))
1062 return SS_MOD_BREAK;
1063 if (str == String("screetch"))
1064 return SS_MOD_SCREETCH;
1065 if (str == String("pump_rpm"))
1066 return SS_MOD_PUMP;
1067 if (str == String("aeroengine1_throttle"))
1068 return SS_MOD_THROTTLE1;
1069 if (str == String("aeroengine2_throttle"))
1070 return SS_MOD_THROTTLE2;
1071 if (str == String("aeroengine3_throttle"))
1072 return SS_MOD_THROTTLE3;
1073 if (str == String("aeroengine4_throttle"))
1074 return SS_MOD_THROTTLE4;
1075 if (str == String("aeroengine5_throttle"))
1076 return SS_MOD_THROTTLE5;
1077 if (str == String("aeroengine6_throttle"))
1078 return SS_MOD_THROTTLE6;
1079 if (str == String("aeroengine7_throttle"))
1080 return SS_MOD_THROTTLE7;
1081 if (str == String("aeroengine8_throttle"))
1082 return SS_MOD_THROTTLE8;
1083 if (str == String("air_speed_knots"))
1084 return SS_MOD_AIRSPEED;
1085 if (str == String("angle_of_attack_degree"))
1086 return SS_MOD_AOA;
1087 if (str == String("linked_command_rate"))
1089 if (str == String("music_volume"))
1090 return SS_MOD_MUSIC_VOLUME;
1091
1092 return -1;
1093}
1094
1095//====================================================================
1096
1097SoundScriptInstance::SoundScriptInstance(int actor_id, SoundScriptTemplatePtr templ, SoundManager* sound_manager, String instancename, int soundLinkType, int soundLinkItemId) :
1098 actor_id(actor_id)
1099 , instance_name(instancename)
1100 , templ(templ)
1101 , sound_manager(sound_manager)
1102 , sound_link_type(soundLinkType)
1103 , sound_link_item_id(soundLinkItemId)
1104 , start_sound(NULL)
1105 , start_sound_pitchgain(0.0f)
1106 , stop_sound(NULL)
1107 , stop_sound_pitchgain(0.0f)
1108 , lastgain(1.0f)
1109{
1110 // create sounds
1112 {
1114 }
1115
1116 if (templ->has_stop_sound)
1117 {
1119 }
1120
1121 for (int i = 0; i < templ->free_sound; i++)
1122 {
1124 }
1125
1126 setPitch(0.0f);
1127 setGain(1.0f);
1128
1129 LOG("SoundScriptInstance: instance created: "+instancename);
1130}
1131
1133{
1134 if (start_sound)
1135 {
1137
1138 if (start_sound_pitchgain != 0.0f && templ->start_sound_pitch != 0.0f)
1139 {
1141 }
1142 }
1143
1144 if (templ->free_sound)
1145 {
1146 // searching the interval
1147 int up = 0;
1148
1149 for (up = 0; up < templ->free_sound; up++)
1150 {
1151 if (templ->sound_pitches[up] > value)
1152 {
1153 break;
1154 }
1155 }
1156
1157 if (up == 0)
1158 {
1159 // low sound case
1161
1162 if (sounds_pitchgain[0] != 0.0f && templ->sound_pitches[0] != 0.0f && sounds[0])
1163 {
1164 sounds[0]->setPitch(value / templ->sound_pitches[0]);
1165 }
1166
1167 for (int i = 1; i < templ->free_sound; i++)
1168 {
1169 if (templ->sound_pitches[i] != 0.0f)
1170 {
1171 sounds_pitchgain[i] = 0.0f;
1172 // pause?
1173 }
1174 else
1175 {
1176 sounds_pitchgain[i] = 1.0f; // unpitched
1177 }
1178 }
1179 }
1180 else if (up == templ->free_sound)
1181 {
1182 // high sound case
1183 for (int i = 0; i < templ->free_sound - 1; i++)
1184 {
1185 if (templ->sound_pitches[i] != 0.0f)
1186 {
1187 sounds_pitchgain[i] = 0.0f;
1188 // pause?
1189 }
1190 else
1191 {
1192 sounds_pitchgain[i] = 1.0f; // unpitched
1193 }
1194 }
1195
1196 sounds_pitchgain[templ->free_sound - 1] = 1.0f;
1197
1198 if (templ->sound_pitches[templ->free_sound - 1] != 0.0f && sounds[templ->free_sound - 1])
1199 {
1201 }
1202 }
1203 else
1204 {
1205 // middle sound case
1206 int low = up - 1;
1207
1208 for (int i = 0; i < low; i++)
1209 {
1210 if (templ->sound_pitches[i] != 0.0f)
1211 {
1212 sounds_pitchgain[i] = 0.0f;
1213 // pause?
1214 }
1215 else
1216 {
1217 sounds_pitchgain[i] = 1.0f; // unpitched
1218 }
1219 }
1220
1221 if (templ->sound_pitches[low] != 0.0f && sounds[low])
1222 {
1223 sounds_pitchgain[low] = (templ->sound_pitches[up] - value) / (templ->sound_pitches[up] - templ->sound_pitches[low]);
1224 sounds[low]->setPitch(value / templ->sound_pitches[low]);
1225 }
1226 else
1227 {
1228 sounds_pitchgain[low] = 1.0f; // unpitched
1229 }
1230
1231 if (templ->sound_pitches[up] != 0.0f && sounds[up])
1232 {
1233 sounds_pitchgain[up] = (value - templ->sound_pitches[low]) / (templ->sound_pitches[up] - templ->sound_pitches[low]);
1234 sounds[up]->setPitch(value / templ->sound_pitches[up]);
1235 }
1236 else
1237 {
1238 sounds_pitchgain[up] = 1.0f; // unpitched
1239 }
1240
1241 for (int i = up + 1; i < templ->free_sound; i++)
1242 {
1243 if (templ->sound_pitches[i] != 0.0f)
1244 {
1245 sounds_pitchgain[i] = 0.0f;
1246 // pause?
1247 }
1248 else
1249 {
1250 sounds_pitchgain[i] = 1.0f; // unpitched
1251 }
1252 }
1253 }
1254 }
1255
1256 if (stop_sound)
1257 {
1259
1260 if (stop_sound_pitchgain != 0.0f && templ->stop_sound_pitch != 0.0f)
1261 {
1263 }
1264 }
1265
1266 // propagate new gains
1268}
1269
1270float SoundScriptInstance::pitchgain_cutoff(float sourcepitch, float targetpitch)
1271{
1272 if (sourcepitch == 0.0f)
1273 {
1274 return 1.0f; // unpitchable
1275 }
1276
1277 if (targetpitch > sourcepitch / PITCHDOWN_FADE_FACTOR)
1278 {
1279 return 1.0f; // pass
1280 }
1281
1282 if (targetpitch < sourcepitch / PITCHDOWN_CUTOFF_FACTOR)
1283 {
1284 return 0.0f; // cutoff
1285 }
1286
1287 // linear fading
1288 return (targetpitch - sourcepitch / PITCHDOWN_CUTOFF_FACTOR) / (sourcepitch / PITCHDOWN_FADE_FACTOR - sourcepitch / PITCHDOWN_CUTOFF_FACTOR);
1289}
1290
1292{
1293 if (start_sound)
1294 {
1296 }
1297
1298 for (int i = 0; i < templ->free_sound; i++)
1299 {
1300 if (sounds[i])
1301 {
1302 sounds[i]->setGain(value * sounds_pitchgain[i]);
1303 }
1304 }
1305
1306 if (stop_sound)
1307 {
1309 }
1310
1311 lastgain = value;
1312}
1313
1315{
1316 if (start_sound)
1317 {
1319 }
1320
1321 for (int i = 0; i < templ->free_sound; i++)
1322 {
1323 if (sounds[i])
1324 {
1325 sounds[i]->setPosition(pos);
1326 }
1327 }
1328
1329 if (stop_sound)
1330 {
1331 stop_sound->setPosition(pos);
1332 }
1333}
1334
1336{
1337 if (start_sound)
1338 {
1339 start_sound->setVelocity(velocity);
1340 }
1341
1342 for (int i = 0; i < templ->free_sound; i++)
1343 {
1344 if (sounds[i])
1345 {
1346 sounds[i]->setVelocity(velocity);
1347 }
1348 }
1349
1350 if (stop_sound)
1351 {
1352 stop_sound->setVelocity(velocity);
1353 }
1354}
1355
1357{
1358 if (start_sound)
1359 {
1360 if (start_sound->isPlaying())
1361 {
1362 return;
1363 }
1364 start_sound->play();
1365 }
1366
1367 for (int i = 0; i < templ->free_sound; i++)
1368 {
1369 if (sounds[i])
1370 {
1371 if (sounds[i]->isPlaying())
1372 {
1373 continue;
1374 }
1375 sounds[i]->setLoop(false);
1376 sounds[i]->play();
1377 }
1378 }
1379
1380 if (stop_sound)
1381 {
1382 if (stop_sound->isPlaying())
1383 {
1384 return;
1385 }
1386 stop_sound->play();
1387 }
1388}
1389
1391{
1392 if (start_sound)
1393 {
1394 start_sound->stop();
1395 //start_sound->setLoop(true);
1396 start_sound->play();
1397 }
1398
1399 for (int i = 0; i < templ->free_sound; i++)
1400 {
1401 if (sounds[i])
1402 {
1403 sounds[i]->setLoop(true);
1404 sounds[i]->play();
1405 }
1406 }
1407}
1408
1410{
1411 for (int i = 0; i < templ->free_sound; i++)
1412 {
1413 if (sounds[i])
1414 sounds[i]->stop();
1415 }
1416
1417 if (stop_sound)
1418 {
1419 stop_sound->stop();
1420 stop_sound->play();
1421 }
1422}
1423
1425{
1426 for (int i = 0; i < templ->free_sound; i++)
1427 {
1428 if (sounds[i])
1429 sounds[i]->stop();
1430 }
1431
1432 if (start_sound)
1433 start_sound->stop();
1434
1435 if (stop_sound)
1436 {
1437 stop_sound->stop();
1438 stop_sound->play();
1439 }
1440}
1441
1443{
1444 if (start_sound)
1445 {
1447 }
1448
1449 if (stop_sound)
1450 {
1452 }
1453
1454 for (int i = 0; i < templ->free_sound; i++)
1455 {
1456 if (sounds[i])
1457 {
1458 sounds[i]->setEnabled(e);
1459 }
1460 }
1461}
1462
1463#endif // USE_OPENAL
#define TOSTRING(x)
Definition Application.h:57
void LOG(const char *msg)
Legacy alias - formerly a macro.
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition Actor.h:429
Ogre::SceneNode * GetCameraNode()
void stop()
Definition Sound.cpp:119
void setVelocity(Ogre::Vector3 vel)
Definition Sound.cpp:161
bool isPlaying()
Definition Sound.cpp:88
void setPitch(float pitch)
Definition Sound.cpp:143
void setGain(float gain)
Definition Sound.cpp:125
void setPosition(Ogre::Vector3 pos)
Definition Sound.cpp:152
void setEnabled(bool e)
Definition Sound.cpp:99
void play()
Definition Sound.cpp:113
void setLoop(bool loop)
Definition Sound.cpp:134
void Update(const float dt)
Does the per-frame update of sounds and listener environment.
int getNumHardwareSources()
Returns the number of currently used hardware sources.
void pauseAllSounds()
Unlike the name suggests, this sets the listener's gain to 0, essentially muting all sounds.
SoundPtr createSound(Ogre::String filename, Ogre::String resource_group_name="")
void resumeAllSounds()
Unlike the name suggests, this sets the listener's gain to the value of the CVar audio_master_volume.
void SetListener(Ogre::Vector3 position, Ogre::Vector3 direction, Ogre::Vector3 up, Ogre::Vector3 velocity)
Sets position and speed of the listener.
static const float PITCHDOWN_CUTOFF_FACTOR
SoundScriptTemplatePtr templ
SoundScriptInstance(int actor_id, SoundScriptTemplatePtr templ, SoundManager *sm, Ogre::String instancename, int soundLinkType=SL_DEFAULT, int soundLinkItemId=-1)
void setPosition(Ogre::Vector3 pos)
static const float PITCHDOWN_FADE_FACTOR
float sounds_pitchgain[MAX_SOUNDS_PER_SCRIPT]
void setVelocity(Ogre::Vector3 velo)
float pitchgain_cutoff(float sourcepitch, float targetpitch)
SoundPtr sounds[MAX_SOUNDS_PER_SCRIPT]
static const SoundPtr SOUNDPTR_NULL
void trigToggle(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
std::map< int, std::map< int, std::map< int, std::map< int, bool > > > > state_map
std::array< int, SS_MAX_MOD > free_pitches
SoundScriptTemplatePtr createTemplate(Ogre::String name, Ogre::String groupname, Ogre::String filename)
Ogre::StringVector script_patterns
const Ogre::StringVector & getScriptPatterns(void) const
void skipToNextCloseBrace(Ogre::DataStreamPtr &chunk)
std::array< int, SS_MAX_MOD > free_gains
void parseScript(Ogre::DataStreamPtr &stream, const Ogre::String &groupName)
void modulate(int actor_id, int mod, float value, int linkType=SL_DEFAULT, int linkItemID=-1)
void skipToNextOpenBrace(Ogre::DataStreamPtr &chunk)
bool getTrigState(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
std::map< Ogre::String, SoundScriptTemplatePtr > templates
std::array< SoundScriptInstancePtr, SS_MAX_TRIG *MAX_INSTANCES_PER_GROUP > trigs
void removeInstance(const SoundScriptInstancePtr &ssi)
void trigOnce(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
void trigKill(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
std::array< int, SS_MAX_TRIG > free_trigs
void trigStart(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
std::vector< SoundScriptInstancePtr > instances
SoundScriptInstancePtr createInstance(Ogre::String templatename, int actor_id, int soundLinkType=SL_DEFAULT, int soundLinkItemId=-1)
std::array< SoundScriptInstancePtr, SS_MAX_MOD *MAX_INSTANCES_PER_GROUP > pitches
void trigStop(int actor_id, int trig, int linkType=SL_DEFAULT, int linkItemID=-1)
void SetListener(Ogre::Vector3 position, Ogre::Vector3 direction, Ogre::Vector3 up, Ogre::Vector3 velocity)
std::array< SoundScriptInstancePtr, SS_MAX_MOD *MAX_INSTANCES_PER_GROUP > gains
Ogre::Real getLoadingOrder(void) const
bool setParameter(Ogre::StringVector vec)
int parseModulation(Ogre::String str)
float sound_pitches[MAX_SOUNDS_PER_SCRIPT]
SoundScriptTemplate(Ogre::String name, Ogre::String groupname, Ogre::String filename, bool baseTemplate)
Ogre::String sound_names[MAX_SOUNDS_PER_SCRIPT]
@ SS_TRIG_GPWS_PULLUP
@ SS_TRIG_AFTERBURNER4
@ SS_TRIG_AEROENGINE7
@ SS_TRIG_GPWS_APDISCONNECT
@ SS_TRIG_AEROENGINE2
@ SS_TRIG_AFTERBURNER2
@ SS_TRIG_AEROENGINE3
@ SS_TRIG_AFTERBURNER5
@ SS_TRIG_AFTERBURNER7
@ SS_TRIG_TURBOBACKFIRE
@ SS_TRIG_LINKED_COMMAND
@ SS_TRIG_AEROENGINE1
@ SS_TRIG_TURN_SIGNAL
@ SS_TRIG_AEROENGINE5
@ SS_TRIG_TURBOWASTEGATE
@ SS_TRIG_TURN_SIGNAL_WARN_TICK
@ SS_TRIG_AEROENGINE6
@ SS_TRIG_TURN_SIGNAL_TICK
@ SS_TRIG_AFTERBURNER6
@ SS_TRIG_GPWS_MINIMUMS
@ SS_TRIG_AIR_PURGE
@ SS_TRIG_AEROENGINE8
@ SS_TRIG_AFTERBURNER1
@ SS_TRIG_AEROENGINE4
@ SS_TRIG_GEARSLIDE
@ SS_TRIG_AFTERBURNER3
@ SS_TRIG_REVERSE_GEAR
@ SS_TRIG_AFTERBURNER8
@ MAX_INSTANCES_PER_GROUP
@ MAX_SOUNDS_PER_SCRIPT
@ SS_MOD_LINKED_COMMANDRATE
@ SS_MOD_MUSIC_VOLUME
@ EDITOR_MODE
Hacky, but whatever... added by Ulteq, 2016.
CVar * sim_state
CameraManager * GetCameraManager()
CVar * audio_enable_creak
void EraseIf(std::vector< T, A > &c, Predicate pred)
Definition Utils.h:75
std::string SanitizeUtf8String(std::string const &str_in)
Definition Utils.cpp:120