LUADOC - Farming Simulator 22

SoundManager

Description
This class handles all sounds
Parent
AbstractManager
Functions

addIndoorStateChangedListener

Description
Definition
addIndoorStateChangedListener()
Code
1325function SoundManager:addIndoorStateChangedListener(target)
1326 table.addElement(self.indoorStateChangedListeners, target)
1327end

cloneSample

Description
Returns a clone of the sample at the given link node
Definition
cloneSample(table sample, integer linkNode)
Arguments
tablesamplesample object
integerlinkNodeid of new link node
Return Values
tablesamplesample object
Code
152function SoundManager:cloneSample(sample, linkNode, modifierTargetObject)
153 local newSample = table.copy(sample)
154 newSample.modifiers = table.copy(sample.modifiers)
155
156 if not sample.is2D then
157 newSample.soundNode = createAudioSource(newSample.sampleName, newSample.filename, newSample.outerRadius, newSample.innerRadius, newSample.current.volume, newSample.loops)
158 newSample.soundSample = getAudioSourceSample(newSample.soundNode)
159 setAudioSourceAutoPlay(newSample.soundNode, false)
160 link(linkNode, newSample.soundNode)
161 newSample.linkNode = linkNode
162
163 if newSample.linkNodeOffset == nil then
164 setTranslation(newSample.soundNode, 0, 0, 0)
165 else
166 setTranslation(newSample.soundNode, newSample.linkNodeOffset[1], newSample.linkNodeOffset[2], newSample.linkNodeOffset[3])
167 end
168 end
169
170 setSampleGroup(newSample.soundSample, sample.audioGroup)
171 newSample.audioGroup = sample.audioGroup
172
173 if sample.supportsReverb then
174 addSampleEffect(newSample.soundSample, SoundManager.DEFAULT_REVERB_EFFECT)
175 else
176 removeSampleEffect(sample.soundSample, SoundManager.DEFAULT_REVERB_EFFECT)
177 end
178
179 if modifierTargetObject ~= nil then
180 newSample.modifierTargetObject = modifierTargetObject
181 end
182
183 newSample.sourceRandomizations = {}
184 for i=1, #sample.sourceRandomizations do
185 local randomSample = sample.sourceRandomizations[i]
186
187 local newRandomSample = self:getRandomSample(sample, randomSample.filename)
188
189 table.insert(newSample.sourceRandomizations, newRandomSample)
190 end
191
192 self.samples[newSample] = newSample
193 table.insert(self.orderedSamples, newSample)
194
195 return newSample
196end

cloneSample2D

Description
Returns a clone of the sample at the given link node
Definition
cloneSample2D(table sample, integer linkNode)
Arguments
tablesamplesample object
integerlinkNodeid of new link node
Return Values
tablesamplesample object
Code
203function SoundManager:cloneSample2D(sample, linkNode, modifierTargetObject)
204 local newSample = table.copy(sample)
205 newSample.modifiers = table.copy(sample.modifiers)
206
207 newSample.audioGroup = sample.audioGroup
208 newSample.linkNode = nil
209 newSample.soundNode = nil
210 newSample.is2D = true
211
212 newSample.soundSample = createSample(newSample.sampleName)
213 newSample.orgSoundSample = newSample.soundSample
214
215 loadSample(newSample.soundSample, newSample.filename, false)
216 newSample.duration = getSampleDuration(newSample.soundSample)
217 setSampleGroup(newSample.soundSample, sample.audioGroup)
218 newSample.audioGroup = sample.audioGroup
219
220 if modifierTargetObject ~= nil then
221 newSample.modifierTargetObject = modifierTargetObject
222 end
223
224 newSample.sourceRandomizations = {}
225 for i=1, #sample.sourceRandomizations do
226 local randomSample = sample.sourceRandomizations[i]
227
228 local newRandomSample = {}
229 newRandomSample.filename = randomSample.filename
230 newRandomSample.isEmpty = randomSample.isEmpty
231 newRandomSample.is2D = true
232 if not randomSample.isEmpty then
233 newRandomSample.soundSample = createSample(newSample.sampleName)
234 loadSample(newRandomSample.soundSample, newRandomSample.filename, false)
235 end
236
237 table.insert(newSample.sourceRandomizations, newRandomSample)
238 end
239
240 self.samples[newSample] = newSample
241 table.insert(self.orderedSamples, newSample)
242
243 return newSample
244end

consoleCommandToggleDebug

Description
Definition
consoleCommandToggleDebug()
Code
1376function SoundManager:consoleCommandToggleDebug()
1377 SoundManager.GLOBAL_DEBUG_ENABLED = not SoundManager.GLOBAL_DEBUG_ENABLED
1378 if SoundManager.GLOBAL_DEBUG_ENABLED then
1379 -- add all 3d samples to debugSamples table
1380 for _, sample in pairs(self.orderedSamples) do
1381 if sample.linkNode ~= nil then
1382 self.debugSamples[sample] = true
1383 end
1384 end
1385 else
1386 table.clear(self.debugSamples)
1387 -- remove all debugSamples except the ones flagged in xml
1388 for _, sample in pairs(self.debugSamplesFlagged) do
1389 self.debugSamples[sample] = true
1390 end
1391 end
1392 return string.format("SoundManager.GLOBAL_DEBUG_ENABLED=%s", SoundManager.GLOBAL_DEBUG_ENABLED)
1393end

createAudio2d

Description
Definition
createAudio2d()
Code
516function SoundManager:createAudio2d(sample, filename)
517 if sample.soundSample ~= nil then
518 delete(sample.soundSample)
519 end
520
521 sample.soundSample = createSample(sample.sampleName)
522 sample.orgSoundSample = sample.soundSample
523 loadSample(sample.soundSample, filename, false)
524
525 self:onCreateAudio2d(sample)
526end

createAudioSource

Description
Definition
createAudioSource()
Code
470function SoundManager:createAudioSource(sample, filename)
471 if sample.soundNode ~= nil then
472 delete(sample.soundNode)
473 end
474
475 sample.soundNode = createAudioSource(sample.sampleName, filename, sample.outerRadius, sample.innerRadius, sample.current.volume, sample.loops)
476 sample.soundSample = getAudioSourceSample(sample.soundNode)
477
478 self:onCreateAudioSource(sample)
479end

delete

Description
Definition
delete()
Code
66function SoundManager:delete()
67 if self.soundTemplateXMLFile ~= nil then
68 delete(self.soundTemplateXMLFile)
69 self.soundTemplateXMLFile = nil
70 end
71end

deleteSample

Description
Deletes the sample
Definition
deleteSample(table sample)
Arguments
tablesamplesample object
Code
1048function SoundManager:deleteSample(sample)
1049 if sample ~= nil and sample.filename ~= nil then
1050 if self:getIsSamplePlaying(sample) then
1051 self:stopSample(sample)
1052 end
1053
1054 self.samples[sample] = nil
1055 table.removeElement(self.activeSamples, sample)
1056 table.removeElement(self.orderedSamples, sample)
1057 self.debugSamples[sample] = nil
1058 table.removeElement(self.debugSamplesFlagged, sample)
1059
1060 if sample.soundNode ~= nil then
1061 delete(sample.soundNode)
1062 end
1063 if sample.is2D and sample.orgSoundSample ~= nil then
1064 delete(sample.orgSoundSample)
1065 end
1066
1067 for i=1, #sample.sourceRandomizations do
1068 local randomSample = sample.sourceRandomizations[i]
1069
1070 if not randomSample.isEmpty then
1071 if randomSample.soundNode ~= nil and randomSample.soundNode ~= sample.soundNode then
1072 delete(randomSample.soundNode)
1073 end
1074 if randomSample.is2D then
1075 delete(randomSample.soundSample)
1076 end
1077 end
1078 end
1079
1080 sample.sourceRandomizations = {}
1081 sample.soundSample = nil
1082 sample.soundNode = nil
1083 end
1084end

deleteSamples

Description
Deletes table of samples
Definition
deleteSamples(table samples)
Arguments
tablesamplestable with sample objects
Code
1089function SoundManager:deleteSamples(samples, delay, afterSample)
1090 if samples ~= nil then
1091 for _, sample in pairs(samples) do
1092 self:deleteSample(sample, delay, afterSample)
1093 end
1094 end
1095end

draw

Description
Definition
draw()
Code
851function SoundManager:draw()
852 -- draw sample debug
853 for linkNode, linkNodeSamples in pairs(self.debugSamplesLinkNodes) do
854 -- draw link node
855 local x,y,z = getWorldTranslation(linkNode)
856 local debugNode = createTransformGroup("sampleDebugNode")
857 setTranslation(debugNode, x,y,z)
858
859 local linkNodeText = string.format("LinkNode '%s' (visible=%s)", getName(linkNode), getEffectiveVisibility(linkNode))
860 DebugUtil.drawDebugNode(linkNode, linkNodeText, false)
861
862 -- draw invidiual samples
863 for i=1, #linkNodeSamples do
864 local sample = linkNodeSamples[i]
865 local name = sample.sampleName or i
866 local rotOffset = i/100 -- slightly offset rotation per sample so circles with same radius distinguishable
867
868 local text = string.format("AudioSample '%s' IR=%d OR=%d isPlaying=%s tmpl=%s", name, sample.innerRadius, sample.outerRadius, self:getIsSamplePlaying(sample), sample.templateName)
869 local color = DebugUtil.tableToColor(sample)
870 setRotation(debugNode, 0, rotOffset, 0)
871 Utils.renderTextAtWorldPosition(x,y,z, text, getCorrectTextSize(0.016), i * getCorrectTextSize(0.016), color)
872 DebugUtil.drawDebugCircleAtNode(debugNode, sample.innerRadius, 20, color, true)
873 DebugUtil.drawDebugCircleAtNode(debugNode, sample.outerRadius, 20, color, true)
874
875 -- redraw circle rotated 90deg
876 setRotation(debugNode, 0, math.rad(90)+rotOffset, 0)
877 DebugUtil.drawDebugCircleAtNode(debugNode, sample.innerRadius, 20, color, true)
878 DebugUtil.drawDebugCircleAtNode(debugNode, sample.outerRadius, 20, color, true)
879 end
880 delete(debugNode)
881 end
882end

getCurrentFadeFactor

Description
Definition
getCurrentFadeFactor()
Code
1300function SoundManager:getCurrentFadeFactor(sample)
1301 local fadeFactor = 1
1302
1303 if sample.fadeIn ~= 0 then
1304 fadeFactor = sample.fade / sample.fadeIn
1305 end
1306
1307 return fadeFactor
1308end

getCurrentRandomizationValue

Description
Definition
getCurrentRandomizationValue()
Code
1288function SoundManager:getCurrentRandomizationValue(sample, attribute)
1289 if sample.randomizations ~= nil then
1290 if sample.randomizations[attribute] ~= nil then
1291 return sample.randomizations[attribute]
1292 end
1293 end
1294
1295 return 0
1296end

getCurrentSampleLowpassGain

Description
Definition
getCurrentSampleLowpassGain()
Code
1282function SoundManager:getCurrentSampleLowpassGain(sample)
1283 return (sample.current.lowpassGain + self:getCurrentRandomizationValue(sample, "lowpassGain")) * sample.lowpassGainScale + sample.offsets.lowpassGain
1284end

getCurrentSamplePitch

Description
Definition
getCurrentSamplePitch()
Code
1276function SoundManager:getCurrentSamplePitch(sample)
1277 return (sample.current.pitch + self:getCurrentRandomizationValue(sample, "pitch")) * sample.pitchScale + sample.offsets.pitch
1278end

getCurrentSampleVolume

Description
Definition
getCurrentSampleVolume()
Code
1270function SoundManager:getCurrentSampleVolume(sample)
1271 return math.max((sample.current.volume + self:getCurrentRandomizationValue(sample, "volume")) * self:getCurrentFadeFactor(sample) * sample.volumeScale + sample.offsets.volume, 0)
1272end

getIsIndoor

Description
Checks if indoor mode is active
Definition
getIsIndoor()
Return Values
booleanisIndoortrue if indoor mode is active else false
Code
1338function SoundManager:getIsIndoor()
1339 return self.isIndoor
1340end

getIsInsideBuilding

Description
Checks if inside building mode is active
Definition
getIsInsideBuilding()
Return Values
booleanisIndoortrue if inside building mode is active else false
Code
1354function SoundManager:getIsInsideBuilding()
1355 return self.isInsideBuilding
1356end

getIsSamplePlaying

Description
Checks if a sample is playing
Definition
getIsSamplePlaying(table sample, float offset)
Arguments
tablesamplesample object
floatoffsetan offset
Return Values
booleanisPlayingtrue if sample is playing else false
Code
1216function SoundManager:getIsSamplePlaying(sample, offset)
1217 if sample ~= nil and sample.soundSample ~= nil then
1218 return isSamplePlaying(sample.soundSample)
1219 end
1220
1221 return false
1222end

getModifierFactor

Description
Gets modifier factor
Definition
getModifierFactor(table sample, string modifierName)
Arguments
tablesamplesample object
stringmodifierNamethe modifier name
Return Values
floatfactorthe modifier factor
Code
1363function SoundManager:getModifierFactor(sample, modifierName)
1364 if sample.modifiers ~= nil then
1365 local modifier = sample.modifiers[modifierName]
1366 if modifier ~= nil and modifier.currentValue ~= nil then
1367 return modifier.currentValue
1368 end
1369 end
1370
1371 return 1.0
1372end

getRandomSample

Description
Definition
getRandomSample()
Code
779function SoundManager:getRandomSample(sample, filename)
780 local randomSample = {}
781 randomSample.filename = filename
782 if filename ~= "-" then
783 if not sample.is2D then
784 local audioSource = createAudioSource(sample.sampleName, filename, sample.outerRadius, sample.innerRadius, 1, sample.loops)
785 if audioSource ~= 0 then
786 randomSample.soundNode = audioSource
787 local sampleId = getAudioSourceSample(randomSample.soundNode)
788 if sample.supportsReverb then
789 addSampleEffect(sampleId, SoundManager.DEFAULT_REVERB_EFFECT)
790 else
791 removeSampleEffect(sampleId, SoundManager.DEFAULT_REVERB_EFFECT)
792 end
793 end
794 else
795 local sample2D = createSample(sample.sampleName)
796 if sample2D ~= 0 and loadSample(sample2D, filename, false) then
797 randomSample.soundSample = sample2D
798 randomSample.is2D = true
799 end
800 end
801 else
802 randomSample.isEmpty = true
803 end
804
805 return randomSample
806end

getSampleModifierValue

Description
Gets sample modifiers
Definition
getSampleModifierValue(table sample, table inputs, string debugString)
Arguments
tablesamplesample object
tableinputsa table of values
stringdebugStringa string
Code
1026function SoundManager:getSampleModifierValue(sample, attribute, typeIndex)
1027 local modifier = sample.modifiers[attribute]
1028 if modifier ~= nil then
1029 local curve = modifier[typeIndex]
1030 if curve ~= nil then
1031 local typeData = self.modifierTypeIndexToDesc[typeIndex]
1032 local t = typeData.func(sample.modifierTargetObject)
1033 if typeData.maxFunc ~= nil and typeData.minFunc ~= nil then
1034 local min = typeData.minFunc(sample.modifierTargetObject)
1035 t = MathUtil.clamp((t - min) / (typeData.maxFunc(sample.modifierTargetObject) - min), 0, 1)
1036 end
1037
1038 return curve:get(t), t, true
1039 end
1040 end
1041
1042 return 0, 0, false
1043end

initDataStructures

Description
Initialize data structures
Definition
initDataStructures()
Code
38function SoundManager:initDataStructures()
39 self.samples = {}
40 self.orderedSamples = {}
41 self.activeSamples = {}
42 self.activeSamplesSet = {}
43 self.debugSamplesFlagged = {} -- array, containing all samples which are flagged for debugging in xml
44 self.debugSamples = {} -- sample indexed table, contains all samples which will be drawn
45 self.debugSamplesLinkNodes = {} -- link-node indexed table, contaning array of samples
46 self.currentSampleIndex = 1
47 self.oldRandomizationIndex = 1
48 self.isIndoor = false
49 self.isInsideBuilding = false
50
51 self.soundTemplates = {}
52 self.soundTemplateXMLFile = nil
53 self:loadSoundTemplates(SoundManager.DEFAULT_SOUND_TEMPLATES)
54
55 self.modifierTypeNameToIndex = {}
56 self.modifierTypeIndexToDesc = {}
57 SoundModifierType = self.modifierTypeNameToIndex
58
59 setReverbEffect(0, Reverb.GS_OPEN_FIELD, Reverb.GS_OPEN_FIELD, 1.0)
60
61 self.indoorStateChangedListeners = {}
62end

loadModifiersFromXML

Description
Loads a sample modifiers from xml
Definition
loadModifiersFromXML(table sample, integer xmlFile, string key)
Arguments
tablesamplesample object
integerxmlFilexml-file handle
stringkeyxml element key
Code
656function SoundManager:loadModifiersFromXML(sample, xmlFile, key)
657 sample.modifiers = Utils.getNoNil(sample.modifiers, {})
658 for _, attribute in pairs(SoundManager.SAMPLE_MODIFIER_ATTRIBUTES) do
659 local modifier = Utils.getNoNil(sample.modifiers[attribute], {})
660 modifier.hasModification = Utils.getNoNil(modifier.hasModification, false)
661
662 local i = 0
663 while true do
664 local modKey = string.format("%s.%s.modifier(%d)", key, attribute, i)
665 if not hasXMLProperty(xmlFile, modKey) then
666 break
667 end
668
669 local type = getXMLString(xmlFile, modKey.."#type")
670 local typeIndex = SoundModifierType[type]
671 if typeIndex ~= nil then
672 if modifier[typeIndex] == nil then
673 modifier[typeIndex] = AnimCurve.new(linearInterpolator1)
674 end
675
676 local value = getXMLFloat(xmlFile, modKey.."#value")
677 local modifiedValue = getXMLFloat(xmlFile, modKey.."#modifiedValue")
678 modifier[typeIndex]:addKeyframe({modifiedValue, time=value}, xmlFile, modKey)
679
680 modifier.hasModification = true
681 --TODO: remove debug comment and enable warning for everyone for next release
682--#debug else
683--#debug Logging.xmlWarning(xmlFile, "Unknown modifier type '%s' in '%s'\nAvailable types: %s", type, key, table.concatKeys(SoundModifierType, ", "))
684 end
685 i = i + 1
686 end
687
688 modifier.currentValue = nil
689 sample.modifiers[attribute] = modifier
690 end
691end

loadRandomizationsFromXML

Description
Loads a sample modifiers from xml
Definition
loadRandomizationsFromXML(table sample, integer xmlFile, string key)
Arguments
tablesamplesample object
integerxmlFilexml-file handle
stringkeyxml element key
Code
698function SoundManager:loadRandomizationsFromXML(sample, xmlFile, key, baseDir)
699 sample.randomizationsIn = sample.randomizationsIn or {}
700 sample.randomizationsOut = sample.randomizationsOut or {}
701
702 local i = 0
703 while true do
704 local baseKey = string.format("%s.randomization(%d)", key, i)
705 if not hasXMLProperty(xmlFile, baseKey) then
706 break
707 end
708
709 local randomization = {}
710
711 randomization.minVolume = getXMLFloat(xmlFile, baseKey.."#minVolume")
712 randomization.maxVolume = getXMLFloat(xmlFile, baseKey.."#maxVolume")
713
714 randomization.minPitch = getXMLFloat(xmlFile, baseKey.."#minPitch")
715 randomization.maxPitch = getXMLFloat(xmlFile, baseKey.."#maxPitch")
716
717 randomization.minLowpassGain = getXMLFloat(xmlFile, baseKey.."#minLowpassGain")
718 randomization.maxLowpassGain = getXMLFloat(xmlFile, baseKey.."#maxLowpassGain")
719
720 randomization.isInside = Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#isInside"), true)
721 randomization.isOutside = Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#isOutside"), true)
722
723 if randomization.isInside then
724 if randomization.minVolume ~= nil then
725 if sample.indoorAttributes.volume + randomization.minVolume <= 0 then
726 Logging.xmlWarning(xmlFile, "Invalid sample '%s' randomization found in %s. randomization#minVolume can result in negative volume (indoor)", sample.templateName or sample.sampleName, baseKey)
727 end
728 end
729
730 table.insert(sample.randomizationsIn, randomization)
731 end
732
733 if randomization.isOutside then
734 if randomization.minVolume ~= nil then
735 if sample.outdoorAttributes.volume + randomization.minVolume <= 0 then
736 Logging.xmlWarning(xmlFile, "Invalid sample '%s' randomization found in %s. randomization#minVolume can result in negative volume (outdoor)", sample.templateName or sample.sampleName, baseKey)
737 end
738 end
739
740 table.insert(sample.randomizationsOut, randomization)
741 end
742
743 i = i + 1
744 end
745
746 sample.sourceRandomizations = sample.sourceRandomizations or {}
747 i = 0
748 while true do
749 local baseKey = string.format("%s.sourceRandomization(%d)", key, i)
750 if not hasXMLProperty(xmlFile, baseKey) then
751 break
752 end
753
754 local filename = getXMLString(xmlFile, baseKey.."#file")
755 if filename ~= nil then
756 if filename ~= "-" then
757 filename = Utils.getFilename(filename, baseDir)
758 end
759
760 local randomSample = self:getRandomSample(sample, filename)
761 table.insert(sample.sourceRandomizations, randomSample)
762 end
763
764 i = i + 1
765 end
766
767 if #sample.sourceRandomizations > 0 and not sample.addedBaseFileToRandomizations then
768 local filename = Utils.getFilename(sample.filename, baseDir)
769
770 local randomSample = self:getRandomSample(sample, filename)
771 table.insert(sample.sourceRandomizations, randomSample)
772
773 sample.addedBaseFileToRandomizations = true
774 end
775end

loadSample2DFromXML

Description
Loads a 2D sample from XML. This creates a sample using no spatial information to be used for global, client-only contexts (e.g. UI).
Definition
loadSample2DFromXML(int xmlFile, string baseKey, string sampleName, string baseDir, int loops, string audioGroup)
Arguments
intxmlFileSample definition XML file handle
stringbaseKeyParent element key of sample
stringsampleNameSample element name
stringbaseDirSample file path base directory
intloopsLoop count of sample, defaults to 1. A value of 0 will loop indefinitely.
stringaudioGroupSample audio group
Code
323function SoundManager:loadSample2DFromXML(xmlFile, baseKey, sampleName, baseDir, loops, audioGroup)
324 local sample = nil
325
326 local isValid, usedExternal, definitionXmlFile, sampleKey = self:validateSampleDefinition(xmlFile, baseKey, sampleName, baseDir, audioGroup, true)
327
328 if isValid then
329 sample = {}
330 sample.is2D = true
331 sample.sampleName = sampleName
332
333 local template = getXMLString(definitionXmlFile, sampleKey .. "#template")
334 if template ~= nil then
335 sample = self:loadSampleAttributesFromTemplate(sample, template, baseDir, loops, definitionXmlFile, sampleKey)
336 end
337
338 if not self:loadSampleAttributesFromXML(sample, definitionXmlFile, sampleKey, baseDir, loops) then
339 return nil
340 end
341
342 sample.filename = Utils.getFilename(sample.filename, baseDir)
343 sample.linkNode = nil
344 sample.current = sample.outdoorAttributes
345 sample.audioGroup = audioGroup
346 sample.supportsReverb = Utils.getNoNil(getXMLBool(xmlFile, sampleKey.."#supportsReverb"), true)
347
348 sample.soundSample = createSample(sample.sampleName)
349 sample.orgSoundSample = sample.soundSample
350 loadSample(sample.soundSample, sample.filename, false) -- false -> 2D sound
351 sample.duration = getSampleDuration(sample.soundSample)
352
353 setSampleGroup(sample.soundSample, sample.audioGroup)
354 setSampleVolume(sample.soundSample, sample.current.volume)
355 setSamplePitch(sample.soundSample, sample.current.pitch)
356 setSampleFrequencyFilter(sample.soundSample, 1.0, sample.current.lowpassGain, 0.0, sample.current.lowpassCutoffFrequency, 0.0, sample.current.lowpassResonance)
357 if sample.supportsReverb then
358 addSampleEffect(sample.soundSample, SoundManager.DEFAULT_REVERB_EFFECT)
359 else
360 removeSampleEffect(sample.soundSample, SoundManager.DEFAULT_REVERB_EFFECT)
361 end
362
363 sample.offsets = {volume=0, pitch=0, lowpassGain=0}
364
365 self.samples[sample] = sample
366 table.insert(self.orderedSamples, sample)
367 end
368
369 if usedExternal then
370 delete(definitionXmlFile)
371 end
372
373 return sample
374end

loadSampleAttributesFromTemplate

Description
Definition
loadSampleAttributesFromTemplate()
Code
541function SoundManager:loadSampleAttributesFromTemplate(sample, templateName, baseDir, defaultLoops, xmlFile, sampleKey)
542 local xmlKey = self.soundTemplates[templateName]
543 if xmlKey ~= nil then
544 if self.soundTemplateXMLFile ~= nil then
545 local templateSample = {}
546 templateSample.is2D = sample.is2D
547 templateSample.sampleName = sample.sampleName
548 templateSample.templateName = templateName
549 if not self:loadSampleAttributesFromXML(templateSample, self.soundTemplateXMLFile, xmlKey, baseDir, defaultLoops, false) then
550 return sample
551 end
552 return templateSample
553 end
554 else
555 local xmlFileObject = g_xmlManager:getFileByHandle(xmlFile)
556 if xmlFileObject ~= nil then
557 Logging.xmlError(xmlFileObject, "Sound template '%s' was not found in %s", templateName, sampleKey)
558 else
559 Logging.error("Sound template '%s' was not found in %s", templateName, sampleKey)
560 end
561 end
562
563 return sample
564end

loadSampleAttributesFromXML

Description
Loads a sample attributes from xml
Definition
loadSampleAttributesFromXML(table sample, integer xmlFile, string key)
Arguments
tablesamplesample object
integerxmlFilexml-file handle
stringkeyxml element key
Code
571function SoundManager:loadSampleAttributesFromXML(sample, xmlFile, key, baseDir, defaultLoops, requiresFile)
572 local parent = getXMLString(xmlFile, key.."#parent")
573
574
575 if parent ~= nil then
576 local templateKey = self.soundTemplates[parent]
577 if templateKey ~= nil then
578 self:loadSampleAttributesFromXML(sample, self.soundTemplateXMLFile, templateKey, baseDir, defaultLoops, false)
579 end
580 end
581
582 sample.filename = Utils.getNoNil(getXMLString(xmlFile, key.."#file"), sample.filename)
583 if sample.filename == nil and (requiresFile == nil or requiresFile) then
584 print("Warning: Filename not defined in '"..tostring(key).. "'. Ignoring it!")
585 return false
586 end
587
588 sample.linkNodeOffset = Utils.getNoNil(getXMLString(xmlFile, key.."#linkNodeOffset"), "0 0 0"):getVectorN(3)
589
590 sample.innerRadius = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key.."#innerRadius"), sample.innerRadius), 5.0)
591 sample.outerRadius = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key.."#outerRadius"), sample.outerRadius), 80.0)
592
593 sample.volumeScale = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key.."#volumeScale"), sample.volumeScale), 1.0)
594 sample.pitchScale = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key.."#pitchScale"), sample.pitchScale), 1.0)
595 sample.lowpassGainScale = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key.."#lowpassGainScale"), sample.lowpassGainScale), 1.0)
596
597 sample.loopSynthesisRPMRatio = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key.."#loopSynthesisRPMRatio"), sample.loopSynthesisRPMRatio), 1.0)
598
599 sample.indoorAttributes = Utils.getNoNil(sample.indoorAttributes, {})
600 sample.indoorAttributes.volume = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".volume#indoor"), sample.indoorAttributes.volume), 0.8)
601 sample.indoorAttributes.pitch = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".pitch#indoor"), sample.indoorAttributes.pitch), 1.0)
602 sample.indoorAttributes.lowpassGain = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".lowpassGain#indoor"), sample.indoorAttributes.lowpassGain), 0.8)
603 sample.indoorAttributes.lowpassCutoffFrequency = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".lowpassCutoffFrequency#indoor"), sample.indoorAttributes.lowpassCutoffFrequency), 0.0) -- by default this is defined by the group (default 5000hz, resonance 2)
604 sample.indoorAttributes.lowpassResonance = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".lowpassResonance#indoor"), sample.indoorAttributes.lowpassResonance), 0.0)
605
606 sample.outdoorAttributes = Utils.getNoNil(sample.outdoorAttributes, {})
607 sample.outdoorAttributes.volume = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".volume#outdoor"), sample.outdoorAttributes.volume), 1.0)
608 sample.outdoorAttributes.pitch = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".pitch#outdoor"), sample.outdoorAttributes.pitch), 1.0)
609 sample.outdoorAttributes.lowpassGain = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".lowpassGain#outdoor"), sample.outdoorAttributes.lowpassGain), 1.0)
610 sample.outdoorAttributes.lowpassCutoffFrequency = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".lowpassCutoffFrequency#outdoor"), sample.outdoorAttributes.lowpassCutoffFrequency), 0.0)
611 sample.outdoorAttributes.lowpassResonance = Utils.getNoNil(Utils.getNoNil(getXMLFloat(xmlFile, key..".lowpassResonance#outdoor"), sample.outdoorAttributes.lowpassResonance), 0.0)
612
613 sample.loops = Utils.getNoNil(Utils.getNoNil(getXMLInt(xmlFile, key.."#loops"), sample.loops), Utils.getNoNil(defaultLoops, 1))
614
615 sample.supportsReverb = Utils.getNoNil(Utils.getNoNil(getXMLBool(xmlFile, key.."#supportsReverb"), sample.supportsReverb), true)
616
617 sample.isLocalSound = Utils.getNoNil(Utils.getNoNil(getXMLBool(xmlFile, key.."#isLocalSound"), sample.isLocalSound), false)
618
619 sample.debug = Utils.getNoNil(Utils.getNoNil(getXMLBool(xmlFile, key.."#debug"), sample.debug), false)
620 if sample.debug or SoundManager.GLOBAL_DEBUG_ENABLED then
621 if sample.debug then
622 -- save flagged samples in separate table to keep them when disabling global mode
623 table.insert(self.debugSamplesFlagged, sample)
624 end
625 self.debugSamples[sample] = true
626 sample.debug = nil
627 end
628
629 local fadeIn = getXMLFloat(xmlFile, key.."#fadeIn")
630 if fadeIn ~= nil then
631 fadeIn = fadeIn * 1000
632 end
633 sample.fadeIn = Utils.getNoNil(Utils.getNoNil(fadeIn, sample.fadeIn), 0)
634
635 local fadeOut = getXMLFloat(xmlFile, key.."#fadeOut")
636 if fadeOut ~= nil then
637 fadeOut = fadeOut * 1000
638 end
639 sample.fadeOut = Utils.getNoNil(Utils.getNoNil(fadeOut, sample.fadeOut), 0)
640
641 sample.fade = 0
642
643 sample.isIndoor = false
644
645 self:loadModifiersFromXML(sample, xmlFile, key)
646 self:loadRandomizationsFromXML(sample, xmlFile, key, baseDir)
647
648 return true
649end

loadSampleFromXML

Description
Loads a sample from xml
Definition
loadSampleFromXML(integer xmlFile, string key, string sampleName, string baseDir, table components, integer loops, integer audioGroup, table i3dMappings, table modifierTargetObject)
Arguments
integerxmlFilexml-file handle
stringkeyxml element key
stringsampleNamesample name
stringbaseDirbase directory
tablecomponentscomponents
integerloopsnumber of loops
integeraudioGroupaudio group index
tablei3dMappingsi3d mapping table
tablemodifierTargetObjectmodifier target object
Code
387function SoundManager:loadSampleFromXML(xmlFile, baseKey, sampleName, baseDir, components, loops, audioGroup, i3dMappings, modifierTargetObject)
388 local sample = nil
389
390 if type(xmlFile) == "table" then
391 xmlFile = xmlFile.handle
392 end
393
394 local externalSoundsFile, volumeFactor
395 if modifierTargetObject ~= nil then
396 externalSoundsFile = modifierTargetObject.externalSoundsFile
397 volumeFactor = modifierTargetObject.soundVolumeFactor
398 end
399
400 local isValid, _, definitionXmlFile, sampleKey, linkNode = self:validateSampleDefinition(
401 xmlFile, baseKey, sampleName, baseDir, audioGroup, false, components, i3dMappings, externalSoundsFile)
402
403 if isValid then
404 sample = {}
405 sample.is2D = false
406 sample.sampleName = sampleName
407
408 local template = getXMLString(definitionXmlFile, sampleKey .. "#template")
409 if template ~= nil then
410 sample = self:loadSampleAttributesFromTemplate(sample, template, baseDir, loops, definitionXmlFile, sampleKey)
411 end
412
413 if not self:loadSampleAttributesFromXML(sample, definitionXmlFile, sampleKey, baseDir, loops) then
414 return nil
415 end
416
417 sample.filename = Utils.getFilename(sample.filename, baseDir)
418 sample.isGlsFile = sample.filename:find(".gls") ~= nil
419 sample.linkNode = linkNode
420 sample.modifierTargetObject = modifierTargetObject
421 sample.current = sample.outdoorAttributes
422 sample.audioGroup = audioGroup
423
424 if volumeFactor ~= nil then
425 sample.volumeScale = sample.volumeScale * volumeFactor
426 end
427
428 self:createAudioSource(sample, sample.filename)
429
430 sample.offsets = {volume=0, pitch=0, lowpassGain=0}
431
432 self.samples[sample] = sample
433 table.insert(self.orderedSamples, sample)
434 end
435
436 return sample
437end

loadSamplesFromXML

Description
Loads multiple samples for same xml key into a table and returns it
Definition
loadSamplesFromXML(integer xmlFile, string key, string sampleName, string baseDir, table components, integer loops, integer audioGroup, table i3dMappings, table modifierTargetObject, table samples)
Arguments
integerxmlFilexml-file handle
stringkeyxml element key
stringsampleNamesample name
stringbaseDirbase directory
tablecomponentscomponents
integerloopsnumber of loops
integeraudioGroupaudio group index
tablei3dMappingsi3d mapping table
tablemodifierTargetObjectmodifier target object
tablesamplestable to add the loaded samples
Return Values
tablesamplestable with samples
Code
452function SoundManager:loadSamplesFromXML(xmlFile, baseKey, sampleName, baseDir, components, loops, audioGroup, i3dMappings, modifierTargetObject, samples)
453 samples = samples or {}
454 local i = 0
455 while true do
456 local sample = g_soundManager:loadSampleFromXML(xmlFile, baseKey, string.format("%s(%d)", sampleName, i), baseDir, components, loops, audioGroup, i3dMappings, modifierTargetObject)
457 if sample == nil then
458 break
459 end
460
461 table.insert(samples, sample)
462 i = i + 1
463 end
464
465 return samples
466end

loadSoundTemplates

Description
Loads sound templates from xml file
Definition
loadSoundTemplates()
Return Values
booleantrueif loading was successful else false
Code
103function SoundManager:loadSoundTemplates(xmlFilename)
104 local xmlFile = loadXMLFile("soundTemplates", xmlFilename)
105 if xmlFile ~= 0 then
106 local i = 0
107 while true do
108 local key = string.format("soundTemplates.template(%d)", i)
109 if not hasXMLProperty(xmlFile, key) then
110 break
111 end
112
113 local name = getXMLString(xmlFile, key.."#name")
114 if name ~= nil then
115 if self.soundTemplates[name] == nil then
116 self.soundTemplates[name] = key
117 else
118 print(string.format("Warning: Sound template '%s' already exists!", name))
119 end
120 end
121
122 i = i + 1
123 end
124
125 self.soundTemplateXMLFile = xmlFile
126 return true
127 end
128
129 return false
130end

new

Description
Creating manager
Definition
new()
Return Values
tableinstanceinstance of object
Code
28function SoundManager.new(customMt)
29 local self = AbstractManager.new(customMt or SoundManager_mt)
30
31 addConsoleCommand("gsSoundManagerDebug", "Toggle SoundManager global debug mode", "consoleCommandToggleDebug", self)
32
33 return self
34end

onCreateAudio2d

Description
Definition
onCreateAudio2d()
Code
530function SoundManager:onCreateAudio2d(sample)
531 sample.duration = getSampleDuration(sample.soundSample)
532
533 setSampleGroup(sample.soundSample, sample.audioGroup)
534 setSampleVolume(sample.soundSample, sample.current.volume)
535 setSamplePitch(sample.soundSample, sample.current.pitch)
536 setSampleFrequencyFilter(sample.soundSample, 1.0, sample.current.lowpassGain, 0.0, sample.current.lowpassCutoffFrequency, 0.0, sample.current.lowpassResonance)
537end

onCreateAudioSource

Description
Definition
onCreateAudioSource()
Code
483function SoundManager:onCreateAudioSource(sample, ignoreReverb)
484 sample.soundSample = getAudioSourceSample(sample.soundNode)
485 sample.duration = getSampleDuration(sample.soundSample)
486 sample.outerRange = getAudioSourceRange(sample.soundNode)
487 sample.innerRange = getAudioSourceInnerRange(sample.soundNode)
488 sample.isDirty = true
489
490 setSampleGroup(sample.soundSample, sample.audioGroup)
491 setSampleVolume(sample.soundSample, sample.current.volume)
492 setSamplePitch(sample.soundSample, sample.current.pitch)
493 setSampleFrequencyFilter(sample.soundSample, 1.0, sample.current.lowpassGain, 0.0, sample.current.lowpassCutoffFrequency, 0.0, sample.current.lowpassResonance)
494
495 if not ignoreReverb then
496 if sample.supportsReverb then
497 addSampleEffect(sample.soundSample, SoundManager.DEFAULT_REVERB_EFFECT)
498 else
499 removeSampleEffect(sample.soundSample, SoundManager.DEFAULT_REVERB_EFFECT)
500 end
501 end
502
503 setAudioSourceAutoPlay(sample.soundNode, false)
504
505 link(sample.linkNode, sample.soundNode)
506
507 if sample.linkNodeOffset == nil then
508 setTranslation(sample.soundNode, 0, 0, 0)
509 else
510 setTranslation(sample.soundNode, sample.linkNodeOffset[1], sample.linkNodeOffset[2], sample.linkNodeOffset[3])
511 end
512end

playSample

Description
Plays the sample
Definition
playSample(table sample)
Arguments
tablesamplesample object
Code
1100function SoundManager:playSample(sample, delay, afterSample)
1101 if sample ~= nil then
1102 if not sample.isLocalSound or sample.modifierTargetObject == nil or (sample.isLocalSound and sample.modifierTargetObject.isActiveForLocalSound) then
1103 self:updateSampleRandomizations(sample)
1104 self:updateSampleModifiers(sample)
1105 self:updateSampleAttributes(sample, true)
1106
1107 if not sample.isEmptySample then
1108 delay = delay or 0
1109
1110 local afterSampleId = 0
1111 if afterSample ~= nil then
1112 afterSampleId = afterSample.soundSample
1113 end
1114 playSample(sample.soundSample, sample.loops, self:getModifierFactor(sample, "volume") * self:getCurrentSampleVolume(sample), 0, delay, afterSampleId)
1115
1116 table.addElement(self.activeSamples, sample)
1117
1118--#debug if SoundManager.GLOBAL_DEBUG_ENABLED then
1119--#debug if sample.linkNode ~= nil and not getEffectiveVisibility(sample.linkNode) then
1120--#debug if sample.modifierTargetObject == nil or sample.modifierTargetObject.propertyState == nil or sample.modifierTargetObject.propertyState ~= Vehicle.PROPERTY_STATE_SHOP_CONFIG then
1121--#debug Logging.devWarning("Sound link node '%s' is not visible or not linked to the root node. The sound '%s' will not play", I3DUtil.getNodePath(sample.linkNode), sample.sampleName)
1122--#debug end
1123--#debug end
1124--#debug end
1125 end
1126 end
1127 end
1128end

playSamples

Description
Plays table of samples
Definition
playSamples(table samples, integer delay, table afterSample)
Arguments
tablesamplestable with sample objects
integerdelaydelay in ms
tableafterSamplesample will be played as soon as afterSample has stoppped
Code
1135function SoundManager:playSamples(samples, delay, afterSample)
1136 for _, sample in pairs(samples) do
1137 self:playSample(sample, delay, afterSample)
1138 end
1139end

registerModifierType

Description
Loads sound templates from xml file
Definition
registerModifierType()
Return Values
booleantrueif loading was successful else false
Code
76function SoundManager:registerModifierType(typeName, func, minFunc, maxFunc)
77 typeName = typeName:upper()
78
79 if SoundModifierType[typeName] == nil then
80 if type(func) ~= "function" then
81 Logging.error("SoundManager.registerModifierType: parameter 'func' is of type '%s'. Possibly the registerModifierType is called before the definition of the function?", type(func))
82 printCallstack()
83 return
84 end
85
86 local desc = {}
87 desc.name = typeName
88 desc.index = #self.modifierTypeIndexToDesc + 1
89 desc.func = func
90 desc.minFunc = minFunc
91 desc.maxFunc = maxFunc
92
93 SoundModifierType[typeName] = desc.index
94 table.insert(self.modifierTypeIndexToDesc, desc)
95 end
96
97 return SoundModifierType[typeName]
98end

registerModifierXMLPaths

Description
Definition
registerModifierXMLPaths()
Code
1397function SoundManager.registerModifierXMLPaths(schema, path)
1398 schema:register(XMLValueType.STRING, path .. ".modifier(?)#type", "Modifier type")
1399 schema:register(XMLValueType.FLOAT, path .. ".modifier(?)#value", "Source value of modifier type")
1400 schema:register(XMLValueType.FLOAT, path .. ".modifier(?)#modifiedValue", "Change that is applied on sample value")
1401end

registerSampleXMLPaths

Description
Definition
registerSampleXMLPaths()
Code
1405function SoundManager.registerSampleXMLPaths(schema, basePath, name)
1406 schema:setSubSchemaIdentifier("sounds")
1407
1408 if name == nil then
1409 Logging.error("Failed to register sound sample xml paths! No sound name given.")
1410 printCallstack()
1411 end
1412
1413 schema:setXMLSharedRegistration("SoundManager_sound", basePath)
1414
1415 local soundPath = basePath .. "." .. name
1416 schema:register(XMLValueType.NODE_INDEX, soundPath .. "#linkNode", "Link node for 3d sound")
1417 schema:register(XMLValueType.VECTOR_TRANS, soundPath .. "#linkNodeOffset", "Sound source will be offset by this value to the link node")
1418 schema:register(XMLValueType.STRING, soundPath .. "#template", "Sound template name")
1419 schema:register(XMLValueType.STRING, soundPath .. "#parent", "Parent sample for heredity")
1420 schema:register(XMLValueType.STRING, soundPath .. "#file", "Path to sound sample")
1421 schema:register(XMLValueType.FLOAT, soundPath .. "#outerRadius", "Outer radius", 5)
1422 schema:register(XMLValueType.FLOAT, soundPath .. "#innerRadius", "Inner radius", 80)
1423 schema:register(XMLValueType.INT, soundPath .. "#loops", "Number of loops (0 = infinite)", 1)
1424 schema:register(XMLValueType.BOOL, soundPath .. "#supportsReverb", "Flag to disable reverb", true)
1425 schema:register(XMLValueType.BOOL, soundPath .. "#isLocalSound", "While set for vehicle sounds it will only play for the player currently using the vehicle", false)
1426 schema:register(XMLValueType.BOOL, soundPath .. "#debug", "Flag to enable debug rendering", false)
1427 schema:register(XMLValueType.FLOAT, soundPath .. "#fadeIn", "Fade in time in seconds", 0)
1428 schema:register(XMLValueType.FLOAT, soundPath .. "#fadeOut", "Fade out time in seconds", 0)
1429
1430 schema:register(XMLValueType.FLOAT, soundPath .. ".volume#indoor", "Indoor volume", 0.8)
1431 schema:register(XMLValueType.FLOAT, soundPath .. ".pitch#indoor", "Indoor pitch", 1.0)
1432 schema:register(XMLValueType.FLOAT, soundPath .. ".lowpassGain#indoor", "Indoor lowpass gain", 0.8)
1433
1434 schema:register(XMLValueType.FLOAT, soundPath .. ".lowpassCutoffFrequency#indoor", "Indoor lowpass cutoff frequency", 5000.0)
1435 schema:register(XMLValueType.FLOAT, soundPath .. ".lowpassResonance#indoor", "Indoor lowpass resonance", 2.0)
1436 schema:register(XMLValueType.FLOAT, soundPath .. ".lowpassCutoffFrequency#outdoor", "Outdoor lowpass cutoff frequency", 5000.0)
1437 schema:register(XMLValueType.FLOAT, soundPath .. ".lowpassResonance#outdoor", "Outdoor lowpass resonance", 2.0)
1438
1439 schema:register(XMLValueType.FLOAT, soundPath .. ".volume#outdoor", "Outdoor volume", 1.0)
1440 schema:register(XMLValueType.FLOAT, soundPath .. ".pitch#outdoor", "Outdoor pitch", 1.0)
1441 schema:register(XMLValueType.FLOAT, soundPath .. ".lowpassGain#outdoor", "Outdoor lowpass gain", 1.0)
1442
1443 schema:register(XMLValueType.FLOAT, soundPath .. "#volumeScale", "Additional scale that is applied on the volume attributes", 1)
1444 schema:register(XMLValueType.FLOAT, soundPath .. "#pitchScale", "Additional pitch that is applied on the volume attributes", 1)
1445 schema:register(XMLValueType.FLOAT, soundPath .. "#lowpassGainScale", "Additional lowpass gain that is applied on the volume attributes", 1)
1446
1447 schema:register(XMLValueType.FLOAT, soundPath .. "#loopSynthesisRPMRatio", "Ratio between rpm in the gls file and actual rpm of the motor (e.g. 0.9: max. rpm in the gls file will be reached at 90% of motor rpm)", 1)
1448
1449 SoundManager.registerModifierXMLPaths(schema, soundPath .. ".volume")
1450 SoundManager.registerModifierXMLPaths(schema, soundPath .. ".pitch")
1451 SoundManager.registerModifierXMLPaths(schema, soundPath .. ".lowpassGain")
1452 SoundManager.registerModifierXMLPaths(schema, soundPath .. ".loopSynthesisRpm")
1453 SoundManager.registerModifierXMLPaths(schema, soundPath .. ".loopSynthesisLoad")
1454
1455 schema:register(XMLValueType.FLOAT, soundPath .. ".randomization(?)#minVolume", "Min volume")
1456 schema:register(XMLValueType.FLOAT, soundPath .. ".randomization(?)#maxVolume", "Max volume")
1457 schema:register(XMLValueType.FLOAT, soundPath .. ".randomization(?)#minPitch", "Max pitch")
1458 schema:register(XMLValueType.FLOAT, soundPath .. ".randomization(?)#maxPitch", "Max pitch")
1459 schema:register(XMLValueType.FLOAT, soundPath .. ".randomization(?)#minLowpassGain", "Max lowpass gain")
1460 schema:register(XMLValueType.FLOAT, soundPath .. ".randomization(?)#maxLowpassGain", "Max lowpass gain")
1461 schema:register(XMLValueType.BOOL, soundPath .. ".randomization(?)#isInside", "Randomization is applied inside", true)
1462 schema:register(XMLValueType.BOOL, soundPath .. ".randomization(?)#isOutside", "Randomization is applied outside", true)
1463
1464 schema:register(XMLValueType.STRING, soundPath .. ".sourceRandomization(?)#file", "Path to sound sample")
1465
1466 schema:setXMLSharedRegistration()
1467 schema:setSubSchemaIdentifier()
1468end

reloadSoundTemplates

Description
Reloads sound templates xml file
Definition
reloadSoundTemplates()
Code
134function SoundManager:reloadSoundTemplates()
135 for k, _ in pairs(self.soundTemplates) do
136 self.soundTemplates[k] = nil
137 end
138
139 if entityExists(self.soundTemplateXMLFile) then
140 delete(self.soundTemplateXMLFile)
141 self.soundTemplateXMLFile = nil
142 end
143
144 self:loadSoundTemplates(SoundManager.DEFAULT_SOUND_TEMPLATES)
145end

removeIndoorStateChangedListener

Description
Definition
removeIndoorStateChangedListener()
Code
1331function SoundManager:removeIndoorStateChangedListener(target)
1332 table.removeElement(self.indoorStateChangedListeners, target)
1333end

setCurrentSampleAttributes

Description
Definition
setCurrentSampleAttributes()
Code
1258function SoundManager:setCurrentSampleAttributes(sample, isIndoor)
1259 if isIndoor then
1260 sample.current = sample.indoorAttributes
1261 sample.randomizations = sample.randomizationsIn
1262 else
1263 sample.current = sample.outdoorAttributes
1264 sample.randomizations = sample.randomizationsOut
1265 end
1266end

setIsIndoor

Description
Sets the indoor state
Definition
setIsIndoor(boolean true)
Arguments
booleantrueif sound should be played as indoor, else false
Code
1313function SoundManager:setIsIndoor(isIndoor)
1314 if self.isIndoor ~= isIndoor then
1315 self.isIndoor = isIndoor
1316
1317 for _, target in ipairs(self.indoorStateChangedListeners) do
1318 target:onIndoorStateChanged(isIndoor)
1319 end
1320 end
1321end

setIsInsideBuilding

Description
Sets the inside building state
Definition
setIsInsideBuilding(boolean true)
Arguments
booleantrueif sound should be played as inside building, else false
Code
1345function SoundManager:setIsInsideBuilding(isInsideBuilding)
1346 if self.isInsideBuilding ~= isInsideBuilding then
1347 self.isInsideBuilding = isInsideBuilding
1348 end
1349end

setSampleLoopSynthesisParameters

Description
Sets sample loop synthesis parameters
Definition
setSampleLoopSynthesisParameters(table sample, float rpm, float loadFactor)
Arguments
tablesamplesample object
floatrpmrpm
floatloadFactorloadFactor
Code
1229function SoundManager:setSampleLoopSynthesisParameters(sample, rpm, loadFactor)
1230 if sample ~= nil then
1231 if rpm ~= nil then
1232 if sample.loopSynthesisRPMRatio ~= 1 then
1233 rpm = math.max(math.min(rpm / sample.loopSynthesisRPMRatio, 1), 0)
1234 end
1235
1236 setSampleLoopSynthesisRPM(sample.soundSample, rpm, true)
1237 end
1238
1239 if loadFactor ~= nil then
1240 setSampleLoopSynthesisLoadFactor(sample.soundSample, loadFactor)
1241 end
1242 end
1243end

setSampleLowpassGainOffset

Description
Sets a lowpass gain offset for given sample
Definition
setSampleLowpassGainOffset(table samples, float offset)
Arguments
tablesamplestable with sample objects
floatoffsetoffset
Code
1185function SoundManager:setSampleLowpassGainOffset(sample, offset)
1186 if sample ~= nil then
1187 sample.offsets.lowpassGain = offset
1188 end
1189end

setSamplePitch

Description
Sets the sample pitch
Definition
setSamplePitch(table sample, float pitch)
Arguments
tablesamplesample object
floatpitchpitch
Code
1205function SoundManager:setSamplePitch(sample, pitch)
1206 if sample ~= nil then
1207 setSamplePitch(sample.soundSample, pitch)
1208 end
1209end

setSamplePitchOffset

Description
Sets a pitch offset for given sample
Definition
setSamplePitchOffset(table samples, float offset)
Arguments
tablesamplestable with sample objects
floatoffsetoffset
Code
1175function SoundManager:setSamplePitchOffset(sample, offset)
1176 if sample ~= nil then
1177 sample.offsets.pitch = offset
1178 end
1179end

setSamplesLoopSynthesisParameters

Description
Sets samples loop synthesis parameters
Definition
setSamplesLoopSynthesisParameters(table sample, float rpm, float loadFactor)
Arguments
tablesamplesample object
floatrpmrpm
floatloadFactorloadFactor
Code
1250function SoundManager:setSamplesLoopSynthesisParameters(samples, rpm, loadFactor)
1251 for _, sample in pairs(samples) do
1252 self:setSampleLoopSynthesisParameters(sample, rpm, loadFactor)
1253 end
1254end

setSampleVolume

Description
Sets the sample volume
Definition
setSampleVolume(table sample, float volume)
Arguments
tablesamplesample object
floatvolumevolume
Code
1195function SoundManager:setSampleVolume(sample, volume)
1196 if sample ~= nil then
1197 setSampleVolume(sample.soundSample, volume)
1198 end
1199end

setSampleVolumeOffset

Description
Sets a volume offset for given sample
Definition
setSampleVolumeOffset(table samples, float offset)
Arguments
tablesamplestable with sample objects
floatoffsetoffset
Code
1165function SoundManager:setSampleVolumeOffset(sample, offset)
1166 if sample ~= nil then
1167 sample.offsets.volume = offset
1168 end
1169end

stopSample

Description
Stops the sample
Definition
stopSample(table sample, float delay, float fadeOut)
Arguments
tablesamplesample object
floatdelayset delay in ms for stopping the sound, set automatically for loop systehsis samples, 0 otherwise
floatfadeOutset the fade out duration in ms, if not given default defined in the sample is used
Code
1146function SoundManager:stopSample(sample, delay, fadeOut)
1147 if sample ~= nil and sample.soundSample ~= nil then
1148 stopSample(sample.soundSample, delay or getSampleLoopSynthesisStopDuration(sample.soundSample), fadeOut or sample.fadeOut)
1149 end
1150end

stopSamples

Description
Stops table of samples
Definition
stopSamples(table samples)
Arguments
tablesamplestable with sample objects
Code
1155function SoundManager:stopSamples(samples)
1156 for _, sample in pairs(samples) do
1157 self:stopSample(sample)
1158 end
1159end

update

Description
Definition
update()
Code
810function SoundManager:update(dt)
811 for i=0, SoundManager.MAX_SAMPLES_PER_FRAME do
812 local index = self.currentSampleIndex
813
814 if index > #self.activeSamples then
815 self.currentSampleIndex = 1
816 break
817 end
818
819 local sample = self.activeSamples[index]
820 if self:getIsSamplePlaying(sample) then
821 self:updateSampleFade(sample, dt)
822 self:updateSampleModifiers(sample)
823 self:updateSampleAttributes(sample)
824 else
825 table.removeElement(self.activeSamples, sample)
826 sample.fade = 0
827 end
828
829 self.currentSampleIndex = self.currentSampleIndex + 1
830 end
831
832 -- collect samples to debug
833 table.clear(self.debugSamplesLinkNodes)
834 for sample in pairs(self.debugSamples) do
835 if sample.linkNode ~= nil and entityExists(sample.linkNode) then
836 local distanceToCam = calcDistanceFrom(getCamera(), sample.linkNode)
837 -- sample is flagged for debugging or global debug is enabled + player is within 15m or 150% of the outer radius of the source
838 if distanceToCam < 15 or (distanceToCam < sample.outerRadius * 1.5) then
839 -- group samples by linkNodes
840 if self.debugSamplesLinkNodes[sample.linkNode] == nil then
841 self.debugSamplesLinkNodes[sample.linkNode] = {}
842 end
843 table.insert(self.debugSamplesLinkNodes[sample.linkNode], sample)
844 end
845 end
846 end
847end

updateSampleAttributes

Description
Updates sample attributes
Definition
updateSampleAttributes(table sample, boolean force)
Arguments
tablesamplesample object
booleanforcetrue if indoor / outdoor change should be forced
Code
925function SoundManager:updateSampleAttributes(sample, force)
926 if sample ~= nil then
927 if sample.isIndoor ~= self.isIndoor or force then
928 self:setCurrentSampleAttributes(sample, self.isIndoor)
929 sample.isIndoor = self.isIndoor
930 end
931
932 local volumeFactor = self:getModifierFactor(sample, "volume")
933 local pitchFactor = self:getModifierFactor(sample, "pitch")
934 local lowpassGainFactor = self:getModifierFactor(sample, "lowpassGain")
935
936 setSampleVolume(sample.soundSample, volumeFactor * self:getCurrentSampleVolume(sample))
937 setSamplePitch(sample.soundSample, pitchFactor * self:getCurrentSamplePitch(sample))
938 setSampleFrequencyFilter(sample.soundSample, 1.0, lowpassGainFactor * self:getCurrentSampleLowpassGain(sample), 0.0, sample.current.lowpassCutoffFrequency, 0.0, sample.current.lowpassResonance)
939
940 if sample.modifiers["loopSynthesisRpm"].hasModification then
941 local loopSynthesisRpmFactor = self:getModifierFactor(sample, "loopSynthesisRpm")
942 setSampleLoopSynthesisRPM(sample.soundSample, MathUtil.clamp(loopSynthesisRpmFactor, 0, 1), true)
943 end
944
945 if sample.modifiers["loopSynthesisLoad"].hasModification then
946 local loopSynthesisLoadFactor = self:getModifierFactor(sample, "loopSynthesisLoad")
947 setSampleLoopSynthesisLoadFactor(sample.soundSample, MathUtil.clamp(loopSynthesisLoadFactor, 0, 1))
948 end
949 end
950end

updateSampleFade

Description
Updates sample fade
Definition
updateSampleFade(table sample, float dt)
Arguments
tablesamplesample object
floatdttime since last call in ms
Code
888function SoundManager:updateSampleFade(sample, dt)
889 if sample ~= nil then
890 if sample.fadeIn ~= 0 then
891 sample.fade = math.min(sample.fade + dt, sample.fadeIn)
892 end
893 end
894end

updateSampleModifiers

Description
Updates sample modifiers
Definition
updateSampleModifiers(table sample)
Arguments
tablesamplesample object
Code
899function SoundManager:updateSampleModifiers(sample)
900 if sample == nil or sample.modifiers == nil then
901 return
902 end
903
904 for attributeIndex, attribute in pairs(SoundManager.SAMPLE_MODIFIER_ATTRIBUTES) do
905 local modifier = sample.modifiers[attribute]
906
907 if modifier ~= nil then
908 local value = 1.0
909 for name, typeIndex in pairs(SoundModifierType) do
910 local changeValue, _, available = self:getSampleModifierValue(sample, attribute, typeIndex)
911 if available then
912 value = value * changeValue
913 end
914 end
915
916 modifier.currentValue = value
917 end
918 end
919end

updateSampleRandomizations

Description
Updates sample modifiers
Definition
updateSampleRandomizations(table sample, table inputs)
Arguments
tablesamplesample object
tableinputsa table of values
Code
956function SoundManager:updateSampleRandomizations(sample)
957 if sample ~= nil then
958 for _, name in ipairs(SoundManager.SAMPLE_RANDOMIZATIONS) do
959 if (name == "randomizationsIn") == sample.isIndoor then
960 local numRandomizations = #sample[name]
961 if numRandomizations > 0 then
962 local randomizationIndexToUse = math.max(math.floor(math.random(numRandomizations)), 1)
963 local randomizationToUse = sample[name][randomizationIndexToUse]
964
965 if randomizationToUse.minVolume ~= nil and randomizationToUse.maxVolume then
966 sample[name].volume = math.random() * (randomizationToUse.maxVolume - randomizationToUse.minVolume) + randomizationToUse.minVolume
967 end
968
969 if randomizationToUse.minPitch ~= nil and randomizationToUse.maxPitch then
970 sample[name].pitch = math.random() * (randomizationToUse.maxPitch - randomizationToUse.minPitch) + randomizationToUse.minPitch
971 end
972
973 if randomizationToUse.minLowpassGain ~= nil and randomizationToUse.maxLowpassGain then
974 sample[name].lowpassGain = math.random() * (randomizationToUse.maxLowpassGain - randomizationToUse.minLowpassGain) + randomizationToUse.minLowpassGain
975 end
976 end
977 end
978 end
979
980 local numRandomizations = #sample.sourceRandomizations
981 if numRandomizations > 0 then
982 local randomizationIndexToUse = 1
983 for i = 1, 3 do
984 randomizationIndexToUse = math.max(math.floor(math.random(numRandomizations)), 1)
985 if self.oldRandomizationIndex ~= randomizationIndexToUse then
986 break
987 end
988 end
989 self.oldRandomizationIndex = randomizationIndexToUse
990 local randomSample = sample.sourceRandomizations[randomizationIndexToUse]
991
992 if not sample.is2D then
993 if sample.soundSample ~= nil then
994 stopSample(sample.soundSample, 0.0, sample.fadeOut)
995 end
996
997 if not randomSample.isEmpty then
998 sample.soundNode = randomSample.soundNode
999 self:onCreateAudioSource(sample, true)
1000 sample.isEmptySample = false
1001 else
1002 sample.isEmptySample = true
1003 end
1004 else
1005 if sample.soundSample ~= nil then
1006 stopSample(sample.soundSample, 0.0, sample.fadeOut)
1007 end
1008
1009 if not randomSample.isEmpty then
1010 sample.soundSample = randomSample.soundSample
1011 self:onCreateAudio2d(sample)
1012 sample.isEmptySample = false
1013 else
1014 sample.isEmptySample = true
1015 end
1016 end
1017 end
1018 end
1019end

validateSampleDefinition

Description
Validate a sample definition and parameters.
Definition
validateSampleDefinition(int xmlFile, string baseKey, string sampleName, string baseDir, string audioGroup, bool is2D, table components, table i3dMappings)
Arguments
intxmlFileSample definition XML file handle
stringbaseKeyParent element key of sample
stringsampleNameSample element name
stringbaseDirSample file path base directory
stringaudioGroupSample audio group
boolis2DIf true, the sample is interpreted as a non-spatial sound sample
tablecomponentsPath components targeting a node to which a spatial audio source is linked
tablei3dMappingsMappings of I3D indices to path components for node link resolution
Return Values
boolTrueif the definition and parameters are valid
boolTrueif an external XML file is loaded in place of the given parameter, caller must delete the handle afterwards!
intXMLfile handle, either the one passed in as an argument or an alternate external sound file definition which must be released by the caller
stringSounddefinition parent element key which may have changed according to an alternate external sound file definition
intLinknode target for spatial sound samples
Code
261function SoundManager:validateSampleDefinition(xmlFile, baseKey, sampleName, baseDir, audioGroup, is2D, components, i3dMappings, externalSoundsFile)
262 local isValid = false
263 local usedExternal = false
264 local actualXMLFile = xmlFile
265 local sampleKey = ""
266 local linkNode = nil
267
268 if sampleName ~= nil then
269 if not AudioGroup.getIsValidAudioGroup(audioGroup) then
270 print("Warning: Invalid audioGroup index '" .. tostring(audioGroup) .. "'.")
271 end
272
273 sampleKey = baseKey .. "." .. sampleName
274
275 if externalSoundsFile ~= nil then
276 if not hasXMLProperty(actualXMLFile, sampleKey) then
277 sampleKey = Vehicle.xmlSchemaSounds:replaceRootName(sampleKey)
278 actualXMLFile = externalSoundsFile.handle
279 usedExternal = true
280 end
281 end
282
283 local xmlFileObject = g_xmlManager:getFileByHandle(xmlFile)
284 if xmlFileObject ~= nil then
285 XMLUtil.checkDeprecatedXMLElements(xmlFileObject, baseKey .. "#externalSoundFile", "vehicle.base.sounds#filename") --FS19 to FS22
286 end
287
288 if actualXMLFile ~= nil then
289 if hasXMLProperty(actualXMLFile, sampleKey) then
290 isValid = true
291
292 if not is2D then -- check if linkNode exists
293 linkNode = I3DUtil.indexToObject(components, getXMLString(actualXMLFile, sampleKey .. "#linkNode"), i3dMappings)
294 if linkNode == nil then
295 if type(components) == "number" then
296 linkNode = components
297 elseif type(components) == "table" then
298 linkNode = components[1].node
299 else
300 print("Warning: Could not find linkNode (" .. tostring(getXMLString(actualXMLFile, sampleKey .. "#linkNode")) .. ") for sample '" .. tostring(sampleName) .. "'. Ignoring it!")
301 isValid = false
302 end
303 end
304 end
305 end
306 else
307 Logging.warning("Unable to load sample '%s' from internal or given external sound file '%s'!", sampleName, externalSoundsFile)
308 end
309 end
310
311 return isValid, usedExternal, actualXMLFile, sampleKey, linkNode
312end