fmod 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+