fmod 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +5 -0
  4. data/.yardopts +2 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +5 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +96 -0
  9. data/Rakefile +1 -0
  10. data/bin/console +28 -0
  11. data/bin/setup +8 -0
  12. data/ext/fmod.dll +0 -0
  13. data/ext/fmod64.dll +0 -0
  14. data/ext/libfmod.dylib +0 -0
  15. data/ext/llbfmod.zip +0 -0
  16. data/extras/FMOD Studio Programmers API for Windows.chm +0 -0
  17. data/fmod.gemspec +58 -0
  18. data/lib/fmod.rb +564 -0
  19. data/lib/fmod/channel.rb +151 -0
  20. data/lib/fmod/channel_control.rb +821 -0
  21. data/lib/fmod/channel_group.rb +61 -0
  22. data/lib/fmod/core.rb +35 -0
  23. data/lib/fmod/core/bool_description.rb +18 -0
  24. data/lib/fmod/core/channel_mask.rb +24 -0
  25. data/lib/fmod/core/data_description.rb +14 -0
  26. data/lib/fmod/core/driver.rb +59 -0
  27. data/lib/fmod/core/dsp_description.rb +7 -0
  28. data/lib/fmod/core/dsp_index.rb +9 -0
  29. data/lib/fmod/core/dsp_type.rb +43 -0
  30. data/lib/fmod/core/extensions.rb +28 -0
  31. data/lib/fmod/core/file_system.rb +86 -0
  32. data/lib/fmod/core/filter_type.rb +19 -0
  33. data/lib/fmod/core/float_description.rb +16 -0
  34. data/lib/fmod/core/guid.rb +50 -0
  35. data/lib/fmod/core/init_flags.rb +19 -0
  36. data/lib/fmod/core/integer_description.rb +26 -0
  37. data/lib/fmod/core/mode.rb +36 -0
  38. data/lib/fmod/core/output_type.rb +30 -0
  39. data/lib/fmod/core/parameter_info.rb +41 -0
  40. data/lib/fmod/core/parameter_type.rb +10 -0
  41. data/lib/fmod/core/result.rb +88 -0
  42. data/lib/fmod/core/reverb.rb +217 -0
  43. data/lib/fmod/core/sound_ex_info.rb +7 -0
  44. data/lib/fmod/core/sound_format.rb +30 -0
  45. data/lib/fmod/core/sound_group_behavior.rb +9 -0
  46. data/lib/fmod/core/sound_type.rb +80 -0
  47. data/lib/fmod/core/speaker_index.rb +18 -0
  48. data/lib/fmod/core/speaker_mode.rb +16 -0
  49. data/lib/fmod/core/spectrum_data.rb +12 -0
  50. data/lib/fmod/core/structure.rb +23 -0
  51. data/lib/fmod/core/structures.rb +41 -0
  52. data/lib/fmod/core/tag.rb +51 -0
  53. data/lib/fmod/core/tag_data_type.rb +14 -0
  54. data/lib/fmod/core/time_unit.rb +40 -0
  55. data/lib/fmod/core/vector.rb +42 -0
  56. data/lib/fmod/core/window_type.rb +12 -0
  57. data/lib/fmod/dsp.rb +510 -0
  58. data/lib/fmod/dsp_connection.rb +113 -0
  59. data/lib/fmod/effects.rb +38 -0
  60. data/lib/fmod/effects/channel_mix.rb +101 -0
  61. data/lib/fmod/effects/chorus.rb +30 -0
  62. data/lib/fmod/effects/compressor.rb +52 -0
  63. data/lib/fmod/effects/convolution_reverb.rb +31 -0
  64. data/lib/fmod/effects/delay.rb +44 -0
  65. data/lib/fmod/effects/distortion.rb +16 -0
  66. data/lib/fmod/effects/dsps.rb +10 -0
  67. data/lib/fmod/effects/echo.rb +37 -0
  68. data/lib/fmod/effects/envelope_follower.rb +31 -0
  69. data/lib/fmod/effects/fader.rb +16 -0
  70. data/lib/fmod/effects/fft.rb +38 -0
  71. data/lib/fmod/effects/flange.rb +37 -0
  72. data/lib/fmod/effects/high_pass.rb +24 -0
  73. data/lib/fmod/effects/high_pass_simple.rb +25 -0
  74. data/lib/fmod/effects/it_echo.rb +56 -0
  75. data/lib/fmod/effects/it_lowpass.rb +36 -0
  76. data/lib/fmod/effects/ladspa_plugin.rb +14 -0
  77. data/lib/fmod/effects/limiter.rb +32 -0
  78. data/lib/fmod/effects/loudness_meter.rb +19 -0
  79. data/lib/fmod/effects/low_pass.rb +25 -0
  80. data/lib/fmod/effects/low_pass_simple.rb +26 -0
  81. data/lib/fmod/effects/mixer.rb +11 -0
  82. data/lib/fmod/effects/multiband_eq.rb +153 -0
  83. data/lib/fmod/effects/normalize.rb +47 -0
  84. data/lib/fmod/effects/object_pan.rb +62 -0
  85. data/lib/fmod/effects/oscillator.rb +52 -0
  86. data/lib/fmod/effects/pan.rb +166 -0
  87. data/lib/fmod/effects/param_eq.rb +36 -0
  88. data/lib/fmod/effects/pitch_shift.rb +47 -0
  89. data/lib/fmod/effects/return.rb +18 -0
  90. data/lib/fmod/effects/send.rb +21 -0
  91. data/lib/fmod/effects/sfx_reverb.rb +87 -0
  92. data/lib/fmod/effects/three_eq.rb +41 -0
  93. data/lib/fmod/effects/transceiver.rb +57 -0
  94. data/lib/fmod/effects/tremolo.rb +67 -0
  95. data/lib/fmod/effects/vst_plugin.rb +12 -0
  96. data/lib/fmod/effects/winamp_plugin.rb +12 -0
  97. data/lib/fmod/error.rb +108 -0
  98. data/lib/fmod/geometry.rb +380 -0
  99. data/lib/fmod/handle.rb +129 -0
  100. data/lib/fmod/reverb3D.rb +98 -0
  101. data/lib/fmod/sound.rb +810 -0
  102. data/lib/fmod/sound_group.rb +54 -0
  103. data/lib/fmod/system.rb +1242 -0
  104. data/lib/fmod/version.rb +3 -0
  105. metadata +220 -0
@@ -0,0 +1,54 @@
1
+
2
+ module FMOD
3
+ class SoundGroup < Handle
4
+
5
+ include Fiddle
6
+ include Enumerable
7
+
8
+ integer_reader(:max_audible, :SoundGroup_GetMaxAudible)
9
+ integer_writer(:max_audible=, :SoundGroup_SetMaxAudible)
10
+
11
+ integer_reader(:behavior, :SoundGroup_GetMaxAudibleBehavior)
12
+ integer_writer(:behavior=, :SoundGroup_SetMaxAudibleBehavior)
13
+
14
+ float_reader(:volume, :SoundGroup_GetVolume)
15
+ float_writer(:volume=, :SoundGroup_SetVolume)
16
+
17
+ float_reader(:fade_speed, :SoundGroup_GetMuteFadeSpeed)
18
+ float_writer(:fade_speed=, :SoundGroup_SetMuteFadeSpeed)
19
+
20
+ integer_reader(:count, :SoundGroup_GetNumSounds)
21
+ integer_reader(:playing_count, :SoundGroup_GetNumPlaying)
22
+
23
+ alias_method :size, :count
24
+
25
+ def name
26
+ buffer = "\0" * 512
27
+ FMOD.invoke(:SoundGroup_GetName, self, buffer, 512)
28
+ buffer.delete("\0")
29
+ end
30
+
31
+ def each
32
+ return to_enum(:each) unless block_given?
33
+ (0...count).each { |i| yield self[i] }
34
+ self
35
+ end
36
+
37
+ def [](index)
38
+ FMOD.valid_range?(0, 0, count - 1)
39
+ FMOD.invoke(:SoundGroup_GetSound, self, index, sound = int_ptr)
40
+ Sound.new(sound)
41
+ end
42
+
43
+ alias_method :sound, :[]
44
+
45
+ def parent
46
+ FMOD.invoke(:SoundGroup_GetSystemObject, self, system = int_ptr)
47
+ System.new(system)
48
+ end
49
+
50
+ def stop
51
+ FMOD.invoke(:SoundGroup_Stop, self)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,1242 @@
1
+
2
+
3
+ module FMOD
4
+ class System < Handle
5
+
6
+ CpuUsage = Struct.new(:dsp, :stream, :geometry, :update, :total)
7
+
8
+ RamUsage = Struct.new(:current, :max, :total)
9
+
10
+ FileUsage = Struct.new(:sample, :stream, :other)
11
+
12
+ Speaker = Struct.new(:index, :x, :y, :active)
13
+
14
+ Plugin = Struct.new(:handle, :type, :name, :version)
15
+
16
+ SoftwareFormat = Struct.new(:sample_rate, :speaker_mode, :raw_channels)
17
+
18
+ DspBuffer = Struct.new(:size, :count)
19
+
20
+ ##
21
+ # The internal buffer size for streams opened after this call. Larger values
22
+ # will consume more memory, whereas smaller values may cause buffer
23
+ # under-run/starvation/stuttering caused by large delays in disk access (ie
24
+ # net-stream), or CPU usage in slow machines, or by trying to play too many
25
+ # streams at once.
26
+ # @attr size [Integer] The size of stream file buffer. Default is 16384.
27
+ # @attr type [Integer] Type of unit for stream file buffer size.
28
+ # @see TimeUnit
29
+ StreamBuffer = Struct.new(:size, :type)
30
+
31
+ def initialize(handle)
32
+ super
33
+ @rolloff_callbacks = []
34
+ sig = [TYPE_VOIDP, TYPE_FLOAT]
35
+ abi = FMOD::ABI
36
+ cb = Closure::BlockCaller.new(TYPE_FLOAT, sig, abi) do |channel, distance|
37
+ unless @rolloff_callbacks.empty?
38
+ chan = Channel.new(channel)
39
+ @rolloff_callbacks.each { |proc| proc.call(chan, distance) }
40
+ end
41
+ distance
42
+ end
43
+ FMOD.invoke(:System_Set3DRolloffCallback, self, cb)
44
+ end
45
+
46
+ def on_rolloff(proc = nil, &block)
47
+ cb = proc || block
48
+ raise LocalJumpError, "No block given." if cb.nil?
49
+ @rolloff_callbacks << cb
50
+ end
51
+
52
+ # @group Speaker Positioning
53
+
54
+ ##
55
+ # Generates a "default" matrix based on the specified source and target
56
+ # speaker mode.
57
+ #
58
+ # @param source [Integer] The speaker mode being converted from.
59
+ # @param target [Integer] The speaker mode being converted to.
60
+ #
61
+ # @note _source_ and _target_ must not exceed {FMOD::MAX_CHANNEL_WIDTH}.
62
+ # @see FMOD::MAX_CHANNEL_WIDTH
63
+ # @see SpeakerMode
64
+ #
65
+ # @return [<Array<Array<Float>>] the mix matrix.
66
+ def default_matrix(source, target)
67
+ max = FMOD::MAX_CHANNEL_WIDTH
68
+ raise RangeError, "source channels cannot exceed #{max}" if source > max
69
+ raise RangeError, "target channels cannot exceed #{max}" if target > max
70
+ return [] if source < 1 || target < 1
71
+ buffer = "\0" * (SIZEOF_FLOAT * source * target)
72
+ FMOD.invoke(:System_GetDefaultMixMatrix, self, source, target, buffer, 0)
73
+ buffer.unpack('f*').each_slice(source).to_a
74
+ end
75
+
76
+ ##
77
+ # Helper function to return the speakers as array.
78
+ # @return [Array<Speaker>] the array of speakers.
79
+ def speakers
80
+ each_speaker.to_a
81
+ end
82
+
83
+ ##
84
+ # @return [Speaker] the current speaker position for the selected speaker.
85
+ # @see SpeakerIndex
86
+ def speaker(index)
87
+ args = ["\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT, "\0" * SIZEOF_INT ]
88
+ FMOD.invoke(:System_GetSpeakerPosition, self, index, *args)
89
+ args = [index] + args.join.unpack('ffl')
90
+ args[3] = args[3] != 0
91
+ Speaker.new(*args)
92
+ end
93
+
94
+ ##
95
+ # This function allows the user to specify the position of their actual
96
+ # physical speaker to account for non standard setups.
97
+ #
98
+ # It also allows the user to disable speakers from 3D consideration in a
99
+ # game.
100
+ #
101
+ # The function is for describing the "real world" speaker placement to
102
+ # provide a more natural panning solution for 3D sound. Graphical
103
+ # configuration screens in an application could draw icons for speaker
104
+ # placement that the user could position at their will.
105
+ #
106
+ # @overload set_speaker(speaker)
107
+ # @param speaker [Speaker] The speaker to set.
108
+ # @overload set_speaker(index, x, y, active = true)
109
+ # @param index [Integer] The index of the speaker to set.
110
+ # @see SpeakerIndex
111
+ # @param x [Float] The 2D X position relative to the listener.
112
+ # @param y [Float] The 2D Y position relative to the listener.
113
+ # @param active [Boolean] The active state of a speaker.
114
+ # @return [void]
115
+ def set_speaker(*args)
116
+ unless [1, 3, 4].include?(args.size)
117
+ message = "wrong number of arguments: #{args.size} for 1, 3, or 4"
118
+ raise ArgumentError, message
119
+ end
120
+ index, x, y, active = args[0].is_a?(Speaker) ? args[0].values : args
121
+ active = true if args.size == 3
122
+ FMOD.invoke(:System_SetSpeakerPosition, self, index, x, y, active.to_i)
123
+ end
124
+
125
+ ##
126
+ # @overload each_speaker
127
+ # When called with a block, yields each speaker in turn before returning
128
+ # self.
129
+ # @yield [speaker] Yields a speaker to the block.
130
+ # @yieldparam speaker [Speaker] The current enumerated speaker.
131
+ # @return [self]
132
+ # @overload each_speaker
133
+ # When called without a block, returns an enumerator for the speakers.
134
+ # @return [Enumerator]
135
+ def each_speaker
136
+ return to_enum(:each_speaker) unless block_given?
137
+ SpeakerIndex.constants(false).each do |const|
138
+ index = SpeakerIndex.const_get(const)
139
+ yield speaker(index) rescue next
140
+ end
141
+ self
142
+ end
143
+
144
+ # @!endgroup
145
+
146
+ # @!group Object Creation
147
+
148
+ ##
149
+ # @note <b>This must be called to create an {System} object before you can
150
+ # do anything else.</b>
151
+ #
152
+ # {System} creation function. Use this function to create one, or
153
+ # multiple instances of system objects.
154
+ # @param options [Hash] Options hash.
155
+ # @option options [Integer] :max_channels (32) The maximum number of
156
+ # channels to be used in FMOD. They are also called "virtual channels" as
157
+ # you can play as many of these as you want, even if you only have a small
158
+ # number of software voices.
159
+ # @option options [Integer] :flags (InitFlags::NORMAL) See {InitFlags}. This
160
+ # can be a selection of flags bitwise OR'ed together to change the
161
+ # behavior of FMOD at initialization time.
162
+ # @option options [Pointer|String] :driver_data (FMOD::NULL) Driver
163
+ # specific data that can be passed to the output plugin. For example the
164
+ # filename for the wav writer plugin.
165
+ # @return [System] the newly created {System} object.
166
+ def self.create(**options)
167
+ max = [options[:max_channels] || 32, 4093].min
168
+ flags = options[:flags] || InitFlags::NORMAL
169
+ driver = options[:driver_data] || FMOD::NULL
170
+ FMOD.invoke(:System_Create, address = "\0" * SIZEOF_INTPTR_T)
171
+ system = new(address)
172
+ FMOD.invoke(:System_Init, system, max, flags, driver)
173
+ system
174
+ end
175
+
176
+ ##
177
+ # Loads a sound into memory, or opens it for streaming.
178
+ #
179
+ # @param source [String, Pointer] Name of the file or URL to open encoded in
180
+ # a UTF-8 string, or a pointer to a pre-loaded sound memory block if
181
+ # {Mode::OPEN_MEMORY} / {Mode::OPEN_MEMORY_POINT} is used.
182
+ # @param options [Hash] Options hash.
183
+ # @option options [Integer] :mode (Mode::DEFAULT) Behavior modifier for
184
+ # opening the sound. See {Mode} for explanation of flags.
185
+ # @option options [SoundExInfo] :extra (FMOD::NULL) Extra data which lets
186
+ # the user provide extended information while playing the sound.
187
+ # @return [Sound] the created sound.
188
+ def create_sound(source, **options)
189
+ mode = options[:mode] || Mode::DEFAULT
190
+ extra = options[:extra] || FMOD::NULL
191
+ sound = int_ptr
192
+ FMOD.invoke(:System_CreateSound, self, source, mode, extra, sound)
193
+ Sound.new(sound)
194
+ end
195
+
196
+ ##
197
+ # Opens a sound for streaming. This function is a helper function that is
198
+ # the same as {#create_sound} but has the {Mode::CREATE_STREAM} flag added
199
+ # internally.
200
+ #
201
+ # @param source [String, Pointer] Name of the file or URL to open encoded in
202
+ # a UTF-8 string, or a pointer to a pre-loaded sound memory block if
203
+ # {Mode::OPEN_MEMORY} / {Mode::OPEN_MEMORY_POINT} is used.
204
+ # @param options [Hash] Options hash.
205
+ # @option options [Integer] :mode (Mode::DEFAULT) Behavior modifier for
206
+ # opening the sound. See {Mode} for explanation of flags.
207
+ # @option options [SoundExInfo] :extra (FMOD::NULL) Extra data which lets
208
+ # the user provide extended information while playing the sound.
209
+ # @return [Sound] the created sound.
210
+ def create_stream(source, **options)
211
+ mode = options[:mode] || Mode::DEFAULT
212
+ extra = options[:extra] || FMOD::NULL
213
+ sound = int_ptr
214
+ FMOD.invoke(:System_CreateSound, self, source, mode, extra, sound)
215
+ Sound.new(sound)
216
+ end
217
+
218
+ ##
219
+ # Creates an FMOD defined built in DSP unit object to be inserted into a DSP
220
+ # network, for the purposes of sound filtering or sound generation.
221
+ #
222
+ # This function is used to create special effects that come built into FMOD.
223
+ #
224
+ # @param type [Integer, Class] A pre-defined DSP effect or sound generator
225
+ # described by in {DspType}, or a Class found within the {Effects} module.
226
+ #
227
+ # @return [Dsp] the created DSP.
228
+ def create_dsp(type)
229
+ unless FMOD.type?(type, Integer, false)
230
+ unless FMOD.type?(type, Class) && type < Dsp
231
+ raise TypeError, "#{type} must either be or inherit from #{Dsp}."
232
+ end
233
+ end
234
+ if type.is_a?(Integer)
235
+ klass = Dsp.type_map(type)
236
+ else type.is_a?(Class)
237
+ klass = type
238
+ type = Dsp.type_map(type)
239
+ end
240
+ dsp = int_ptr
241
+ FMOD.invoke(:System_CreateDSPByType, self, type, dsp)
242
+ klass.new(dsp)
243
+ end
244
+
245
+ ##
246
+ # Creates a sound group, which can store handles to multiple {Sound}
247
+ # objects.
248
+ # @param name [String] Name of sound group.
249
+ # @return [SoundGroup] the created {SoundGroup}.
250
+ def create_sound_group(name)
251
+ utf8 = name.encode('UTF-8')
252
+ group = int_ptr
253
+ FMOD.invoke(:System_CreateSoundGroup, self, utf8, group)
254
+ SoundGroup.new(group)
255
+ end
256
+
257
+ ##
258
+ # Geometry creation function. This function will create a base geometry
259
+ # object which can then have polygons added to it.
260
+ #
261
+ # Polygons can be added to a geometry object using {Geometry.add_polygon}.
262
+ #
263
+ # A geometry object stores its list of polygons in a structure optimized for
264
+ # quick line intersection testing and efficient insertion and updating. The
265
+ # structure works best with regularly shaped polygons with minimal overlap.
266
+ # Many overlapping polygons, or clusters of long thin polygons may not be
267
+ # handled efficiently. Axis aligned polygons are handled most efficiently.
268
+ #
269
+ # The same type of structure is used to optimize line intersection testing
270
+ # with multiple geometry objects.
271
+ #
272
+ # It is important to set the value of max world-size to an appropriate value
273
+ # using {#world_size}. Objects or polygons outside the range of max
274
+ # world-size will not be handled efficiently. Conversely, if max world-size
275
+ # is excessively large, the structure may lose precision and efficiency may
276
+ # drop.
277
+ #
278
+ # @param max_polygons [Integer] Maximum number of polygons within this
279
+ # object.
280
+ # @param max_vertices [Integer] Maximum number of vertices within this
281
+ # object.
282
+ def create_geometry(max_polygons, max_vertices)
283
+ geometry = int_ptr
284
+ FMOD.invoke(:System_CreateGeometry, self, max_polygons, max_vertices, geometry)
285
+ Geometry.new(geometry)
286
+ end
287
+
288
+ ##
289
+ # Creates a "virtual reverb" object. This object reacts to 3D location and
290
+ # morphs the reverb environment based on how close it is to the reverb
291
+ # object's center.
292
+ #
293
+ # Multiple reverb objects can be created to achieve a multi-reverb
294
+ # environment. 1 Physical reverb object is used for all 3D reverb objects
295
+ # (slot 0 by default).
296
+ #
297
+ # The 3D reverb object is a sphere having 3D attributes (position, minimum
298
+ # distance, maximum distance) and reverb properties. The properties and 3D
299
+ # attributes of all reverb objects collectively determine, along with the
300
+ # listener's position, the settings of and input gains into a single 3D
301
+ # reverb DSP. When the listener is within the sphere of effect of one or
302
+ # more 3D reverbs, the listener's 3D reverb properties are a weighted
303
+ # combination of such 3D reverbs. When the listener is outside all of the
304
+ # reverbs, no reverb is applied.
305
+ #
306
+ # Creating multiple reverb objects does not impact performance. These are
307
+ # "virtual reverbs". There will still be only 1 physical reverb DSP running
308
+ # that just morphs between the different virtual reverbs.
309
+ #
310
+ # @return [Reverb3D] the created {Reverb3D} object.
311
+ def create_reverb
312
+ reverb = int_ptr
313
+ FMOD.invoke(:System_CreateReverb3D, self, reverb)
314
+ Reverb3D.new(reverb)
315
+ end
316
+
317
+ ##
318
+ # Creates a {ChannelGroup} object. These objects can be used to assign
319
+ # channels to for group channel settings, such as volume.
320
+ #
321
+ # Channel groups are also used for sub-mixing. Any channels that are
322
+ # assigned to a channel group get sub-mixed into that channel group's DSP.
323
+ #
324
+ # @param name [String, nil] Optional label to give to the channel group for
325
+ # identification purposes.
326
+ # @return [ChannelGroup] the created {ChannelGroup} object.
327
+ def create_channel_group(name = nil)
328
+ FMOD.invoke(:System_CreateChannelGroup, self, name, group = int_ptr)
329
+ ChannelGroup.new(group)
330
+ end
331
+
332
+ ##
333
+ # Creates a {Geometry} object that was previously serialized with
334
+ # {Geometry.save}.
335
+ # @param source [String] Either a filename where object is saved, or a
336
+ # binary block of serialized data.
337
+ # @param filename [Boolean] +true+ if source is a filename to be loaded,
338
+ # otherwise +false+ and source will be handled as binary data.
339
+ # @return [Geometry]
340
+ # @see Geometry.save
341
+ def load_geometry(source, filename = true)
342
+ source = IO.open(source, 'rb') { |io| io.read } if filename
343
+ size = source.bytesize
344
+ FMOD.invoke(:System_LoadGeometry, self, source, size, geometry = int_ptr)
345
+ Geometry.new(geometry)
346
+ end
347
+
348
+ # @!endgroup
349
+
350
+ # @!group System Resources
351
+
352
+ ##
353
+ # Retrieves in percent of CPU time - the amount of CPU usage that FMOD is
354
+ # taking for streaming/mixing and {#update} combined.
355
+ #
356
+ # @return [CpuUsage] the current CPU resource usage at the time of the call.
357
+ def cpu_usage
358
+ args = ["\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT,
359
+ "\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT]
360
+ FMOD.invoke(:System_GetCPUUsage, self, *args)
361
+ CpuUsage.new(*args.map { |arg| arg.unpack1('f') })
362
+ end
363
+
364
+ ##
365
+ # Retrieves the amount of dedicated sound ram available if the platform
366
+ # supports it.
367
+ #
368
+ # Most platforms use main RAM to store audio data, so this function usually
369
+ # isn't necessary.
370
+ #
371
+ # @return [RamUsage] the current RAM resource usage at the time of the call.
372
+ def ram_usage
373
+ args = ["\0" * SIZEOF_INT, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT]
374
+ FMOD.invoke(:System_GetSoundRAM, self, *args)
375
+ RamUsage.new(*args.map { |arg| arg.unpack1('l') })
376
+ end
377
+
378
+ ##
379
+ # Retrieves information about file reads by FMOD.
380
+ #
381
+ # The values returned are running totals that never reset.
382
+ #
383
+ # @return [FileUsage] the current total of file read resources used by FMOD
384
+ # at the time of the call.
385
+ def file_usage
386
+ args = ["\0" * SIZEOF_LONG_LONG, "\0" * SIZEOF_LONG_LONG,
387
+ "\0" * SIZEOF_LONG_LONG]
388
+ FMOD.invoke(:System_GetFileUsage, self, *args)
389
+ FileUsage.new(*args.map { |arg| arg.unpack1('q') })
390
+ end
391
+
392
+ # @!endgroup
393
+
394
+ # @!group Recording
395
+
396
+ ##
397
+ # Stops the recording engine from recording to the specified recording
398
+ # sound.
399
+ #
400
+ # This does +NOT+ raise an error if a the specified driver ID is incorrect
401
+ # or it is not recording.
402
+ #
403
+ # @param driver_id [Integer] Enumerated driver ID.
404
+ #
405
+ # @return [void]
406
+ def stop_recording(driver_id)
407
+ FMOD.invoke(:System_RecordStop, self, driver_id)
408
+ end
409
+
410
+ ##
411
+ # Starts the recording engine recording to the specified recording sound.
412
+ #
413
+ # @note The specified sound must be created with {Mode::CREATE_SAMPLE} flag.
414
+ #
415
+ # @param driver_id [Integer] Enumerated driver ID.
416
+ # @param sound [Sound] User created sound for the user to record to.
417
+ # @param loop [Boolean] Flag to tell the recording engine whether to
418
+ # continue recording to the provided sound from the start again, after it
419
+ # has reached the end. If this is set to true the data will be continually
420
+ # be overwritten once every loop.
421
+ #
422
+ # @return [void]
423
+ def record_start(driver_id, sound, loop = false)
424
+ FMOD.type?(sound, Sound)
425
+ FMOD.invoke(:System_RecordStart, self, driver_id, sound, loop.to_i)
426
+ end
427
+
428
+ ##
429
+ # Retrieves the state of the FMOD recording API, ie if it is currently
430
+ # recording or not.
431
+ #
432
+ # @param driver_id [Integer] Enumerated driver ID.
433
+ #
434
+ # @return [Boolean] the current recording state of the specified driver.
435
+ def recording?(driver_id)
436
+ bool = "\0" * SIZEOF_INT
437
+ FMOD.invoke(:System_IsRecording, self, driver_id, bool)
438
+ bool.unpack1('l') != 0
439
+ end
440
+
441
+ ##
442
+ # Retrieves the current recording position of the record buffer in PCM
443
+ # samples.
444
+ #
445
+ # @param driver_id [Integer] Enumerated driver ID.
446
+ #
447
+ # @return [Integer] the current recording position in PCM samples.
448
+ def record_position(driver_id)
449
+ position = "\0" * SIZEOF_INT
450
+ FMOD.invoke(:System_GetRecordPosition, self, driver_id, position)
451
+ position.unpack1('L')
452
+ end
453
+
454
+ ##
455
+ # Retrieves the number of recording devices available for this output mode.
456
+ #
457
+ # Use this to enumerate all recording devices possible so that the user can
458
+ # select one.
459
+ #
460
+ # @param connected [Boolean]
461
+ # * *true:* Retrieve the number of recording drivers currently plugged in.
462
+ # * *false:* Receives the number of recording drivers available for this
463
+ # output mode.
464
+ #
465
+ # @return [Integer] the number of record drivers.
466
+ def record_driver_count(connected = true)
467
+ total, present = "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
468
+ FMOD.invoke(:System_GetRecordNumDrivers, self, total, present)
469
+ (connected ? present : total).unpack1('l')
470
+ end
471
+
472
+ ##
473
+ # Retrieves identification information about a sound device specified by its
474
+ # index, and specific to the output mode set with {#output}.
475
+ #
476
+ # @param id [Integer] Index of the sound driver device. The total number of
477
+ # devices can be found with {#record_driver_count}.
478
+ #
479
+ # @return [Driver] the specified driver information.
480
+ def record_driver(id)
481
+ args = [id, "\0" * 512, 512, Guid.new] + (0...4).map { "\0" * SIZEOF_INT }
482
+ FMOD.invoke(:System_GetRecordDriverInfo, self, *args)
483
+ Driver.send(:new, args)
484
+ end
485
+
486
+ ##
487
+ # @!attribute [r] record_drivers
488
+ # @return [Array<Driver>] the array of available record drivers.
489
+ def record_drivers(connected = true)
490
+ (0...record_driver_count(connected)).map { |i| record_driver(i) }
491
+ end
492
+
493
+ # @!endgroup
494
+
495
+ # @!group Sound Card Drivers
496
+
497
+ ##
498
+ # @!attribute output
499
+ # The output mode for the platform. This is for selecting different OS
500
+ # specific APIs which might have different features.
501
+ #
502
+ # Changing this is only necessary if you want to specifically switch away
503
+ # from the default output mode for the operating system. The most optimal
504
+ # mode is selected by default for the operating system.
505
+ #
506
+ # @see OutputMode
507
+ # @return [Integer] the output mode for the platform.
508
+ integer_reader(:output, :System_GetOutput)
509
+ integer_writer(:output=, :System_SetOutput)
510
+
511
+ ##
512
+ # @!attribute [r] driver_count
513
+ # @return [Integer] the number of sound-card devices on the machine,
514
+ # specific to the output mode set with {#output}.
515
+ integer_reader(:driver_count, :System_GetNumDrivers)
516
+
517
+ ##
518
+ # @!attribute current_driver
519
+ # @return [Integer] the currently selected driver number. 0 represents the
520
+ # primary or default driver.
521
+ integer_reader(:current_driver, :System_GetDriver)
522
+ integer_writer(:current_driver=, :System_SetDriver)
523
+
524
+ ##
525
+ # Retrieves identification information about a sound device specified by its
526
+ # index, and specific to the output mode set with {#output}.
527
+ #
528
+ # @param id [Integer] Index of the sound driver device. The total number of
529
+ # devices can be found with {#driver_count}.
530
+ #
531
+ # @return [Driver] the driver information.
532
+ def driver_info(id)
533
+ args = [id, "\0" * 512, 512, Guid.new] + (0...3).map { "\0" * SIZEOF_INT }
534
+ FMOD.invoke(:System_GetDriverInfo, self, *args)
535
+ Driver.send(:new, args)
536
+ end
537
+
538
+ ##
539
+ # @!attribute output_handle
540
+ # Retrieves a pointer to the system level output device module. This means a
541
+ # pointer to a DirectX "LPDIRECTSOUND", or a WINMM handle, or with something
542
+ # like with {OutputType::NO_SOUND} output, the handle will be {FMOD::NULL}.
543
+ #
544
+ # @return [Pointer] the handle to the output mode's native hardware API
545
+ # object.
546
+ def output_handle
547
+ FMOD.invoke(:System_GetOutputHandle, self, handle = int_ptr)
548
+ Pointer.new(handle.unpack1('J'))
549
+ end
550
+
551
+ ##
552
+ # @!attribute [r] drivers
553
+ # @return [Array<Driver>] the array of available drivers.
554
+ def drivers
555
+ (0...driver_count).map { |id| driver_info(id) }
556
+ end
557
+
558
+ # @!endgroup
559
+
560
+ # @!group 3D Sound
561
+
562
+ ##
563
+ # @!attribute doppler_scale
564
+ # The general scaling factor for how much the pitch varies due to doppler
565
+ # shifting in 3D sound.
566
+ #
567
+ # Doppler is the pitch bending effect when a sound comes towards the
568
+ # listener or moves away from it, much like the effect you hear when a train
569
+ # goes past you with its horn sounding. With "doppler scale" you can
570
+ # exaggerate or diminish the effect. FMOD's effective speed of sound at a
571
+ # doppler factor of 1.0 is 340 m/s.
572
+ #
573
+ # @return [Float] the scaling factor.
574
+
575
+ def doppler_scale
576
+ scale = "\0" * SIZEOF_FLOAT
577
+ FMOD.invoke(:System_Get3DSettings, self, scale, nil, nil)
578
+ scale.unpack1('f')
579
+ end
580
+
581
+ def doppler_scale=(scale)
582
+ FMOD.invoke(:System_Set3DSettings, self, scale,
583
+ distance_factor, rolloff_scale)
584
+ end
585
+
586
+ ##
587
+ # @!attribute distance_factor
588
+ # The FMOD 3D engine relative distance factor, compared to 1.0 meters.
589
+ #
590
+ # Another way to put it is that it equates to "how many units per meter does
591
+ # your engine have". For example, if you are using feet then "scale" would
592
+ # equal 3.28.
593
+ #
594
+ # @return [Float] the relative distance factor.
595
+
596
+ def distance_factor
597
+ factor = "\0" * SIZEOF_FLOAT
598
+ FMOD.invoke(:System_Get3DSettings, self, nil, factor, nil)
599
+ factor.unpack1('f')
600
+ end
601
+
602
+ def distance_factor=(factor)
603
+ FMOD.invoke(:System_Set3DSettings, self, doppler_scale,
604
+ factor, rolloff_scale)
605
+ end
606
+
607
+ ##
608
+ # @!attribute rolloff_scale
609
+ # The global attenuation rolloff factor for {Mode::INVERSE_ROLLOFF_3D} based
610
+ # sounds only (which is the default).
611
+ #
612
+ # Volume for a sound set to {Mode::INVERSE_ROLLOFF_3D} will scale at minimum
613
+ # distance / distance. This gives an inverse attenuation of volume as the
614
+ # source gets further away (or closer). Setting this value makes the sound
615
+ # drop off faster or slower. The higher the value, the faster volume will
616
+ # attenuate, and conversely the lower the value, the slower it will
617
+ # attenuate. For example a rolloff factor of 1 will simulate the real world,
618
+ # where as a value of 2 will make sounds attenuate 2 times quicker.
619
+ #
620
+ # @return [Float] the global rolloff factor.
621
+
622
+ def rolloff_scale
623
+ scale = "\0" * SIZEOF_FLOAT
624
+ FMOD.invoke(:System_Get3DSettings, self, nil, nil, scale)
625
+ scale.unpack1('f')
626
+ end
627
+
628
+ def rolloff_scale=(scale)
629
+ FMOD.invoke(:System_Set3DSettings, self, doppler_scale,
630
+ distance_factor, scale)
631
+ end
632
+
633
+ ##
634
+ # Calculates geometry occlusion between a listener and a sound source.
635
+ #
636
+ # @param listener [Vector] The listener position.
637
+ # @param source [Vector] The source position.
638
+ #
639
+ # @return [Array(Float, Float)] the occlusion values as an array, the first
640
+ # element being the direct occlusion value, and the second element being
641
+ # the reverb occlusion value.
642
+ def geometry_occlusion(listener, source)
643
+ FMOD.type?(listener, Vector)
644
+ FMOD.type?(source, Vector)
645
+ args = ["\0" * SIZEOF_FLOAT, "\0" * SIZEOF_FLOAT]
646
+ FMOD.invoke(:System_GetGeometryOcclusion, self, listener, source, *args)
647
+ args.join.unpack('ff')
648
+ end
649
+
650
+ ##
651
+ # @!attribute listeners
652
+ # The number of 3D "listeners" in the 3D sound scene. This is useful mainly
653
+ # for split-screen game purposes.
654
+ #
655
+ # If the number of listeners is set to more than 1, then panning and doppler
656
+ # are turned off. *All* sound effects will be mono. FMOD uses a "closest
657
+ # sound to the listener" method to determine what should be heard in this
658
+ # case.
659
+ # * *Minimum:* 1
660
+ # * *Maximum:* {FMOD::MAX_LISTENERS}
661
+ # * *Default:* 1
662
+ # @return [Integer]
663
+ integer_reader(:listeners, :System_Get3DNumListeners)
664
+ integer_writer(:listeners=, :System_Set3DNumListeners, 1, FMOD::MAX_LISTENERS)
665
+
666
+ ##
667
+ # @!attribute world_size
668
+ # The maximum world size for the geometry engine for performance / precision
669
+ # reasons
670
+ #
671
+ # This setting should be done first before creating any geometry.
672
+ # It can be done any time afterwards but may be slow in this case.
673
+ #
674
+ # Objects or polygons outside the range of this value will not be handled
675
+ # efficiently. Conversely, if this value is excessively large, the structure
676
+ # may loose precision and efficiency may drop.
677
+ #
678
+ # @return [Float] the maximum world size for the geometry engine.
679
+ float_reader(:world_size, :System_GetGeometrySettings)
680
+ float_writer(:world_size=, :System_SetGeometrySettings)
681
+
682
+ # @!endgroup
683
+
684
+ # @!group Plugin Support
685
+
686
+ ##
687
+ # Loads an FMOD plugin. This could be a DSP, file format or output plugin.
688
+ #
689
+ # @param filename [String] Filename of the plugin to be loaded.
690
+ # @param priority [Integer] Codec plugins only, priority of the codec
691
+ # compared to other codecs, where 0 is the most important and higher
692
+ # numbers are less important.
693
+ #
694
+ # @return [Integer] the handle to the plugin.
695
+ def load_plugin(filename, priority = 128)
696
+ # noinspection RubyResolve
697
+ path = filename.encode(Encoding::UTF_8)
698
+ handle = "\0" * SIZEOF_INT
699
+ FMOD.invoke(:System_LoadPlugin, self, path, handle, priority)
700
+ handle.unpack1('L')
701
+ end
702
+
703
+ ##
704
+ # Unloads a plugin from memory.
705
+ #
706
+ # @param handle [Integer] Handle to a pre-existing plugin.
707
+ #
708
+ # @return [void]
709
+ def unload_plugin(handle)
710
+ FMOD.invoke(:System_UnloadPlugin, self, handle)
711
+ end
712
+
713
+ ##
714
+ # Retrieves the number of available plugins loaded into FMOD at the current
715
+ # time.
716
+ #
717
+ # @param type [Symbol] Specifies the type of plugin(s) to enumerate.
718
+ # * <b>:output</b> The plugin type is an output module. FMOD mixed audio
719
+ # will play through one of these devices
720
+ # * <b>:codec</b> The plugin type is a file format codec. FMOD will use
721
+ # these codecs to load file formats for playback.
722
+ # * <b>:dsp</b> The plugin type is a DSP unit. FMOD will use these plugins
723
+ # as part of its DSP network to apply effects to output or generate.rb
724
+ # sound in realtime.
725
+ # @return [Integer] the plugin count.
726
+ def plugin_count(type = :all)
727
+ plugin_type = %i[output codec dsp].index(type)
728
+ count = "\0" * SIZEOF_INT
729
+ unless plugin_type.nil?
730
+ FMOD.invoke(:System_GetNumPlugins, self, plugin_type, count)
731
+ return count.unpack1('l')
732
+ end
733
+ total = 0
734
+ (0..2).each do |i|
735
+ FMOD.invoke(:System_GetNumPlugins, self, i, count)
736
+ total += count.unpack1('l')
737
+ end
738
+ total
739
+ end
740
+
741
+ ##
742
+ # Specify a base search path for plugins so they can be placed somewhere
743
+ # else than the directory of the main executable.
744
+ #
745
+ # @param directory [String] A string containing a correctly formatted path
746
+ # to load plugins from.
747
+ #
748
+ # @return [void]
749
+ def plugin_path(directory)
750
+ # noinspection RubyResolve
751
+ path = directory.encode(Encoding::UTF_8)
752
+ FMOD.invoke(:System_SetPluginPath, self, path)
753
+ end
754
+
755
+ ##
756
+ # Retrieves the handle of a plugin based on its type and relative index.
757
+ #
758
+ # @param type [Symbol] The type of plugin type.
759
+ # * <b>:output</b> The plugin type is an output module. FMOD mixed audio
760
+ # will play through one of these devices
761
+ # * <b>:codec</b> The plugin type is a file format codec. FMOD will use
762
+ # these codecs to load file formats for playback.
763
+ # * <b>:dsp</b> The plugin type is a DSP unit. FMOD will use these plugins
764
+ # as part of its DSP network to apply effects to output or generate.rb
765
+ # sound in realtime.
766
+ # @param index [Integer] The relative index for the type of plugin.
767
+ #
768
+ # @return [Integer] the handle to the plugin.
769
+ def plugin(type, index)
770
+ handle = "\0" * SIZEOF_INT
771
+ plugin_type = %i[output codec dsp].index(type)
772
+ raise ArgumentError, "Invalid plugin type: #{type}." if plugin_type.nil?
773
+ FMOD.invoke(:System_GetPluginHandle, self, plugin_type, index, handle)
774
+ handle.unpack1('L')
775
+ end
776
+
777
+ ##
778
+ # Returns nested plugin definition for the given index.
779
+ #
780
+ # For plugins consisting of a single definition, only index 0 is valid and
781
+ # the returned handle is the same as the handle passed in.
782
+ #
783
+ # @param handle [Integer] A handle to an existing plugin returned from
784
+ # {#load_plugin}.
785
+ # @param index [Integer] Index into the list of plugin definitions.
786
+ #
787
+ # @return [Integer] the handle to the nested plugin.
788
+ def nested_plugin(handle, index)
789
+ nested = "\0" * SIZEOF_INT
790
+ FMOD.invoke(:System_GetNestedPlugin, self, handle, index, nested)
791
+ nested.unpack1('L')
792
+ end
793
+
794
+ ##
795
+ # Returns the number of plugins nested in the one plugin file.
796
+ #
797
+ # Plugins normally have a single definition in them, in which case the count
798
+ # is always 1.
799
+ #
800
+ # For plugins that have a list of definitions, this function returns the
801
+ # number of plugins that have been defined. {#nested_plugin} can be used to
802
+ # find each handle.
803
+ #
804
+ # @param handle [Integer] A handle to an existing plugin returned from
805
+ # {#load_plugin}.
806
+ #
807
+ # @return [Integer] the number of nested plugins.
808
+ def nested_plugin_count(handle)
809
+ count = "\0" * SIZEOF_INT
810
+ FMOD.invoke(:System_GetNumNestedPlugins, self, handle, count)
811
+ count.unpack1('l')
812
+ end
813
+
814
+ ##
815
+ # Retrieves information to display for the selected plugin.
816
+ #
817
+ # @param handle [Integer] The handle to the plugin.
818
+ #
819
+ # @return [Plugin] the plugin information.
820
+ def plugin_info(handle)
821
+ name, type, vs = "\0" * 512, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
822
+ FMOD.invoke(:System_GetPluginInfo, self, handle, type, name, 512, vs)
823
+ type = %i[output codec dsp][type.unpack1('l')]
824
+ # noinspection RubyResolve
825
+ name = name.delete("\0").force_encoding(Encoding::UTF_8)
826
+ Plugin.new(handle, type, name, FMOD.uint2version(vs))
827
+ end
828
+
829
+ ##
830
+ # @!attribute plugin_output
831
+ # @return [Integer] the currently selected output as an ID in the list of
832
+ # output plugins.
833
+ integer_reader(:plugin_output, :System_GetOutputByPlugin)
834
+ integer_writer(:plugin_output=, :System_SetOutputByPlugin)
835
+
836
+ ##
837
+ # @param handle [Integer] Handle to a pre-existing DSP plugin.
838
+ # @return [DspDescription] the description structure for a pre-existing DSP
839
+ # plugin.
840
+ def plugin_dsp_info(handle)
841
+ FMOD.invoke(:System_GetDSPInfoByPlugin, self, handle, address = int_ptr)
842
+ DspDescription.new(address)
843
+ end
844
+
845
+ ##
846
+ # Enumerates the loaded plugins, optionally specifying the type of plugins
847
+ # to loop through.
848
+ #
849
+ # @overload each_plugin(plugin_type = :all)
850
+ # When a block is passed, yields each plugin to the block in turn before
851
+ # returning self.
852
+ # @yield [plugin] Yields a plugin to the block.
853
+ # @yieldparam plugin [Plugin] The currently enumerated plugin.
854
+ # @return [self]
855
+ # @overload each_plugin(plugin_type = :all)
856
+ # When no block is given, returns an enumerator for the plugins.
857
+ # @return [Enumerator]
858
+ # @param plugin_type [Symbol] Specifies the type of plugin(s) to enumerate.
859
+ # * <b>:output</b> The plugin type is an output module. FMOD mixed audio
860
+ # will play through one of these devices
861
+ # * <b>:codec</b> The plugin type is a file format codec. FMOD will use
862
+ # these codecs to load file formats for playback.
863
+ # * <b>:dsp</b> The plugin type is a DSP unit. FMOD will use these plugins
864
+ # as part of its DSP network to apply effects to output or generate.rb
865
+ # sound in realtime.
866
+ def each_plugin(plugin_type = :all)
867
+ return to_enum(:each_plugin) unless block_given?
868
+ types = plugin_type == :all ? %i[output codec dsp] : [plugin_type]
869
+ types.each do |type|
870
+ (0...plugin_count(type)).each do |index|
871
+ handle = plugin(type, index)
872
+ yield plugin_info(handle)
873
+ end
874
+ end
875
+ self
876
+ end
877
+
878
+ # @!endgroup
879
+
880
+
881
+
882
+
883
+
884
+
885
+
886
+
887
+
888
+
889
+
890
+
891
+
892
+
893
+
894
+
895
+
896
+
897
+
898
+
899
+
900
+
901
+
902
+
903
+
904
+
905
+
906
+
907
+
908
+
909
+
910
+
911
+
912
+
913
+
914
+
915
+
916
+ def network_proxy
917
+ buffer = "\0" * 512
918
+ FMOD.invoke(:System_GetNetworkProxy, self, buffer, 512)
919
+ # noinspection RubyResolve
920
+ buffer.delete("\0").force_encoding(Encoding::UTF_8)
921
+ end
922
+
923
+ def network_proxy=(url)
924
+ # noinspection RubyResolve
925
+ FMOD.invoke(:System_SetNetworkProxy, self, url.encode(Encoding::UTF_8))
926
+ end
927
+
928
+ integer_reader(:network_timeout, :System_GetNetworkTimeout)
929
+ integer_writer(:network_timeout=, :System_SetNetworkTimeout)
930
+
931
+ integer_reader(:software_channels, :System_GetSoftwareChannels)
932
+ integer_writer(:software_channels=, :System_SetSoftwareChannels, 0, 64)
933
+
934
+
935
+
936
+ def master_channel_group
937
+ FMOD.invoke(:System_GetMasterChannelGroup, self, group = int_ptr)
938
+ ChannelGroup.new(group)
939
+ end
940
+
941
+ def master_sound_group
942
+ FMOD.invoke(:System_GetMasterSoundGroup, self, group = int_ptr)
943
+ SoundGroup.new(group)
944
+ end
945
+
946
+
947
+ def update
948
+ FMOD.invoke(:System_Update, self)
949
+ end
950
+
951
+ ##
952
+ # Closes the {System} object without freeing the object's memory, so the
953
+ # system handle will still be valid.
954
+ #
955
+ # Closing the output renders objects created with this system object
956
+ # invalid. Make sure any sounds, channel groups, geometry and DSP objects
957
+ # are released before closing the system object.
958
+ #
959
+ # @return [void]
960
+ def close
961
+ FMOD.invoke(:System_Close, self)
962
+ end
963
+
964
+ ##
965
+ # @!attribute [r] version
966
+ # @return [String] the current version of FMOD being used.
967
+ def version
968
+ FMOD.invoke(:System_GetVersion, self, version = "\0" * SIZEOF_INT)
969
+ FMOD.uint2version(version)
970
+ end
971
+
972
+ ##
973
+ # Plays a sound object on a particular channel and {ChannelGroup}.
974
+ #
975
+ # When a sound is played, it will use the sound's default frequency and
976
+ # priority.
977
+ #
978
+ # A sound defined as {Mode::THREE_D} will by default play at the position of
979
+ # the listener.
980
+ #
981
+ # Channels are reference counted. If a channel is stolen by the FMOD
982
+ # priority system, then the handle to the stolen voice becomes invalid, and
983
+ # Channel based commands will not affect the new sound playing in its place.
984
+ # If all channels are currently full playing a sound, FMOD will steal a
985
+ # channel with the lowest priority sound. If more channels are playing than
986
+ # are currently available on the sound-card/sound device or software mixer,
987
+ # then FMOD will "virtualize" the channel. This type of channel is not
988
+ # heard, but it is updated as if it was playing. When its priority becomes
989
+ # high enough or another sound stops that was using a real hardware/software
990
+ # channel, it will start playing from where it should be. This technique
991
+ # saves CPU time (thousands of sounds can be played at once without actually
992
+ # being mixed or taking up resources), and also removes the need for the
993
+ # user to manage voices themselves. An example of virtual channel usage is a
994
+ # dungeon with 100 torches burning, all with a looping crackling sound, but
995
+ # with a sound-card that only supports 32 hardware voices. If the 3D
996
+ # positions and priorities for each torch are set correctly, FMOD will play
997
+ # all 100 sounds without any 'out of channels' errors, and swap the real
998
+ # voices in and out according to which torches are closest in 3D space.
999
+ # Priority for virtual channels can be changed in the sound's defaults, or
1000
+ # at runtime with {Channel.priority}.
1001
+ #
1002
+ # @param sound [Sound] The sound to play.
1003
+ # @param group [ChannelGroup] The {ChannelGroup} become a member of. This is
1004
+ # more efficient than using {Channel.group}, as it does it during the
1005
+ # channel setup, rather than connecting to the master channel group, then
1006
+ # later disconnecting and connecting to the new {ChannelGroup} when
1007
+ # specified. Specify +nil+ to ignore (use master {ChannelGroup}).
1008
+ # @param paused [Boolean] flag to specify whether to start the channel
1009
+ # paused or not. Starting a channel paused allows the user to alter its
1010
+ # attributes without it being audible, and un-pausing with
1011
+ # ChannelControl.resume actually starts the sound.
1012
+ #
1013
+ # @return [Channel] the newly playing channel.
1014
+ def play_sound(sound, group = nil, paused = false)
1015
+ FMOD.type?(sound, Sound)
1016
+ channel = int_ptr
1017
+ FMOD.invoke(:System_PlaySound, self, sound, group, paused.to_i, channel)
1018
+ Channel.new(channel)
1019
+ end
1020
+
1021
+ def play_dsp(dsp, group = nil, paused = false)
1022
+ FMOD.type?(dsp, Dsp)
1023
+ channel = int_ptr
1024
+ FMOD.invoke(:System_PlayDSP, self, dsp, group, paused.to_i, channel)
1025
+ Channel.new(channel)
1026
+ end
1027
+
1028
+ def [](index)
1029
+ reverb = int_ptr
1030
+ FMOD.invoke(:System_GetReverbProperties, self, index, reverb)
1031
+ Reverb.new(reverb.unpack1('J'))
1032
+ end
1033
+
1034
+ def []=(index, reverb)
1035
+ FMOD.type?(reverb, Reverb)
1036
+ FMOD.invoke(:System_SetReverbProperties, self, index, reverb)
1037
+ end
1038
+
1039
+ def mixer_suspend
1040
+ FMOD.invoke(:System_MixerSuspend, self)
1041
+ if block_given?
1042
+ yield
1043
+ FMOD.invoke(:System_MixerResume, self)
1044
+ end
1045
+ end
1046
+
1047
+ def mixer_resume
1048
+ FMOD.invoke(:System_MixerResume, self)
1049
+ end
1050
+
1051
+ ##
1052
+ # Route the signal from a channel group into a separate audio port on the
1053
+ # output driver.
1054
+ #
1055
+ # Note that an FMOD port is a hardware specific reference, to hardware
1056
+ # devices that exist on only certain platforms (like a console headset, or
1057
+ # dedicated hardware music channel for example). It is not supported on all
1058
+ # platforms.
1059
+ #
1060
+ # @param group [ChannelGroup] Channel group to route away to the new port.
1061
+ # @param port_type [Integer] Output driver specific audio port type. See
1062
+ # extra platform specific header (if it exists) for port numbers
1063
+ # @param port_index [Integer] Output driver specific index of the audio
1064
+ # port. Use {FMOD::PORT_INDEX_NONE} if this is not required.
1065
+ # @param pass_thru [Boolean] If +true+ the signal will continue to be passed
1066
+ # through to the main mix, if +false+ the signal will be entirely to the
1067
+ # designated port.
1068
+ #
1069
+ # @return [void]
1070
+ def attach_to_port(group, port_type, port_index, pass_thru)
1071
+ FMOD.type?(group, ChannelGroup)
1072
+ FMOD.invoke(:System_AttachChannelGroupToPort, self, port_type,
1073
+ port_index, group, pass_thru.to_i)
1074
+ end
1075
+
1076
+ ##
1077
+ # Disconnect a channel group from a port and route audio back to the default
1078
+ # port of the output driver.
1079
+ #
1080
+ # @param group [ChannelGroup] Channel group to route away back to the
1081
+ # default audio port.
1082
+ #
1083
+ # @return [void]
1084
+ def detach_from_port(group)
1085
+ FMOD.type?(group, ChannelGroup)
1086
+ FMOD.invoke(:System_DetachChannelGroupFromPort, self, group)
1087
+ end
1088
+
1089
+ ##
1090
+ # Mutual exclusion function to lock the FMOD DSP engine (which runs
1091
+ # asynchronously in another thread), so that it will not execute. If the
1092
+ # FMOD DSP engine is already executing, this function will block until it
1093
+ # has completed.
1094
+ #
1095
+ # The function may be used to synchronize DSP network operations carried out
1096
+ # by the user.
1097
+ #
1098
+ # An example of using this function may be for when the user wants to
1099
+ # construct a DSP sub-network, without the DSP engine executing in the
1100
+ # background while the sub-network is still under construction.
1101
+ #
1102
+ # Once the user no longer needs the DSP engine locked, it must be unlocked
1103
+ # with {#unlock_dsp}.
1104
+ #
1105
+ # Note that the DSP engine should not be locked for a significant amount of
1106
+ # time, otherwise inconsistency in the audio output may result. (audio
1107
+ # skipping/stuttering).
1108
+ #
1109
+ # @overload lock_dsp
1110
+ # Locks the DSP engine, must unlock with {#unlock_dsp}.
1111
+ # @overload lock_dsp
1112
+ # @yield Locks the DSP engine, and unlocks it when the block exits.
1113
+ # @return [void]
1114
+ def lock_dsp
1115
+ FMOD.invoke(:System_LockDSP, self)
1116
+ if block_given?
1117
+ yield
1118
+ FMOD.invoke(:System_UnlockDSP, self)
1119
+ end
1120
+ end
1121
+
1122
+ ##
1123
+ # Mutual exclusion function to unlock the FMOD DSP engine (which runs
1124
+ # asynchronously in another thread) and let it continue executing.
1125
+ #
1126
+ # @note The DSP engine must be locked with {#lock_dsp} before this function
1127
+ # is called.
1128
+ # @return [void]
1129
+ def unlock_dsp
1130
+ FMOD.invoke(:System_UnlockDSP, self)
1131
+ end
1132
+
1133
+ ##
1134
+ # Helper method to create and enumerate each type of internal DSP unit.
1135
+ # @overload each_dsp
1136
+ # When called with a block, yields each DSP type in turn before returning
1137
+ # self.
1138
+ # @yield [dsp] Yields a DSP unit to the block.
1139
+ # @yieldparam dsp [Dsp] The current enumerated DSP unit.
1140
+ # @return [self]
1141
+ # @overload each_dsp
1142
+ # When called without a block, returns an enumerator for the DSP units.
1143
+ # @return [Enumerator]
1144
+ def each_dsp
1145
+ return to_enum(:each_dsp) unless block_given?
1146
+ FMOD::DspType.constants(false).each do |const|
1147
+ type = DspType.const_get(const)
1148
+ yield create_dsp(type) rescue next
1149
+ end
1150
+ self
1151
+ end
1152
+
1153
+ ##
1154
+ # Retrieves the number of currently playing channels.
1155
+ # @param total [Boolean] +true+ to return the number of playing channels
1156
+ # (both real and virtual), +false+ to return the number of playing
1157
+ # non-virtual channels only.
1158
+ # @return [Integer] the number of playing channels.
1159
+ def playing_channels(total = true)
1160
+ count, real = "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
1161
+ FMOD.invoke(:System_GetChannelsPlaying, self, count, real)
1162
+ (total ? count : real).unpack1('l')
1163
+ end
1164
+
1165
+ ##
1166
+ # Retrieves a handle to a channel by ID.
1167
+ #
1168
+ # @param id [Integer] Index in the FMOD channel pool. Specify a channel
1169
+ # number from 0 to the maximum number of channels specified in
1170
+ # {System.create} minus 1.
1171
+ #
1172
+ # @return [Channel] the requested channel.
1173
+ def channel(id)
1174
+ FMOD.invoke(:System_GetChannel, self, id, handle = int_ptr)
1175
+ Channel.new(handle)
1176
+ end
1177
+
1178
+ ##
1179
+ # @return [Integer] the a speaker mode's channel count.
1180
+ # @param speaker_mode [Integer] the speaker mode to query.
1181
+ # @see SpeakerMode
1182
+ def speaker_mode_channels(speaker_mode)
1183
+ count = "\0" * SIZEOF_INT
1184
+ FMOD.invoke(:System_GetSpeakerModeChannels, self, speaker_mode, count)
1185
+ count.unpack1('l')
1186
+ end
1187
+
1188
+ ##
1189
+ # @!attribute software_format
1190
+ # The output format for the software mixer.
1191
+ #
1192
+ # If loading Studio banks, this must be set with speaker mode
1193
+ # corresponding to the project's output format if there is a possibility of
1194
+ # the output audio device not matching the project's format. Any differences
1195
+ # between the project format and the system's speaker mode will cause the
1196
+ # mix to sound wrong.
1197
+ #
1198
+ # If not loading Studio banks, do not set this unless you explicitly want
1199
+ # to change a setting from the default. FMOD will default to the speaker
1200
+ # mode and sample rate that the OS / output prefers.
1201
+ #
1202
+ # @return [SoftwareFormat] the output format for the software mixer.
1203
+
1204
+ def software_format
1205
+ args = ["\0" * SIZEOF_INT, "\0" * SIZEOF_INT, "\0" * SIZEOF_INT]
1206
+ FMOD.invoke(:System_GetSoftwareFormat, self, *args)
1207
+ args.map! { |arg| arg.unpack1('l') }
1208
+ SoftwareFormat.new(*args)
1209
+ end
1210
+
1211
+ def software_format=(format)
1212
+ FMOD.type?(format, SoftwareFormat)
1213
+ FMOD.invoke(:System_GetSoftwareFormat, self, *format.values)
1214
+ end
1215
+
1216
+ def stream_buffer
1217
+ size, type = "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
1218
+ FMOD.invoke(:System_GetStreamBufferSize, self, size, type)
1219
+ StreamBuffer.new(size.unpack1('L'), type.unpack1('l'))
1220
+ end
1221
+
1222
+ def stream_buffer=(buffer)
1223
+ FMOD.type?(buffer, StreamBuffer)
1224
+ raise RangeError, "size must be greater than 0" unless buffer.size > 0
1225
+ FMOD.invoke(:System_SetStreamBufferSize, self, *buffer.values)
1226
+ end
1227
+
1228
+ def dsp_buffer
1229
+ size, count = "\0" * SIZEOF_INT, "\0" * SIZEOF_INT
1230
+ FMOD.invoke(:System_GetDSPBufferSize, self, size, count)
1231
+ DspBuffer.new(size.unpack1('L'), count.unpack1('l'))
1232
+ end
1233
+
1234
+ def dsp_buffer=(buffer)
1235
+ FMOD.type?(buffer, DspBuffer)
1236
+ raise RangeError, "size must be greater than 0" unless buffer.size > 0
1237
+ FMOD.invoke(:System_SetDSPBufferSize, self, *buffer.values)
1238
+ end
1239
+ end
1240
+ end
1241
+
1242
+